Test architecture and change recipes
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Read from cmake/UnitTests.cmake, cmake/TestHelpers.cmake, the launchers and the workflows; nothing was executed, and whether the current CI jobs are green was not checked. The GoogleTest PRE_TEST behaviour is read from the module, not observed.
A source file under tests/ does not automatically mean one standalone executable or one CI run. CNA's unit corpus is conditionally assembled into shared GoogleTest object groups; focused developer binaries reuse those objects while CTest discovers the aggregate. Renderer example programs, platform conformance, C ABI executables, configure-script gates, launcher-driven live suites and CI workflows are distinct evidence layers, and each proves something different. This page is the map of those layers for a maintainer who registers, selects or interprets tests: how CnaTests is built, what CTest names, labels and working directories mean, how GPU evidence stays honest, and what each workflow does and does not cover.
Evidence: read from cmake/UnitTests.cmake, cmake/TestHelpers.cmake, cmake/TestDisplayPolicy.cmake, cmake/Tests/ModuleProbes.cmake, cmake/Harnesses.cmake, the launchers under tools/platform and the workflows at snapshot 009d40f5. No configure, build or test was run for this page; a registered test is not a passing test. The working commands are on the testing handbook, and the full change-to-test matrix on What to test after changing X; this page keeps the object groups, discovery rules, labels, private display wrapper, CI architecture and representative recipes.
How CnaTests is assembled
cmake/UnitTests.cmake (when CNA_BUILD_TESTS=ON; needs vendor/googletest)
→ glob module-owned tests, renderer-owned tests and the shared top-level tests/*.cpp
→ remove standalone-main() files and sources unavailable in this configuration
→ group by physical owner: modules/<name>/tests → <name>; every renderer family → renderers;
top-level tests/ → integration
→ cna_<group>_test_objects [OBJECT libraries, EXCLUDE_FROM_ALL]
├── CnaTests [every group; the default target; the GoogleTest/CTest corpus]
└── Cna<Group>Tests [one group; EXCLUDE_FROM_ALL developer binary; links gtest_main]
→ gtest_discover_tests(CnaTests, DISCOVERY_MODE PRE_TEST, WORKING_DIRECTORY = repository root)
→ named CTest routes over CnaTests for platform, audio, input, glTF, X11 and Wayland behaviour
→ script-mode and policy gates (cmake/Tests, Harnesses, ModuleProbes) registered later
Object libraries are intentional: a static test archive can discard self-registering GoogleTest translation units when no symbol is directly referenced, so each group is an OBJECT library whose objects are added to CnaTests and to the group's focused executable. A focused executable links the owning module's dependency closure rather than the whole CNA umbrella wherever it can, so CnaMathTests and CnaCoreTests are genuinely narrower builds; the cross-module integration group, the renderers policy group and graphics-ext link the umbrella because they exercise the aggregate and need the selected renderer registry. A focused executable is not an extra CTest registration: it exists for fast local iteration, while the full CnaTests discovery remains the default corpus and is not run twice in CI. On the shared-library layout CnaTests names CNA first, so libcna.so precedes the static archives (sharp-runtime among them) that also contributed to it, or the process would carry two copies of their code and static state.
Conditional source filters matter. Implementation-specific platform tests (SDL3, SDL2, X11, Wayland, Win32, Terminal) are included only when that implementation exists in the link graph, and the code the X11 and Wayland backends share (Xkb, Freedesktop, Posix, Linux) has its tests only for those two selections. SDL2-native fixtures need separate binaries (cna_platform_sdl2_tests, cna_audio_sdl2_tests) because SDL2 and SDL3 targets advertise incompatible interface requirements. The audio suites that read SDL3_mixer's own tracks leave the corpus for every audio value except SDL3 (the XACT category suite also runs under ALSA, which has a mixer of CNA's own). Without FFmpeg the real-decoder fixtures leave but the no-backend contract tests remain; without networking the network and GamerServices suites leave; the inspector suites need CNA_BUILD_INSPECTOR; the tests of the optional sharp-runtime XML serialization component leave on Windows targets, as do suites that spawn a POSIX helper process (they and their helper executables are also excluded on Emscripten, Android and iOS). The experimental C API tests define their own main() and ABI conditions and live in the C API module, not in CnaTests. Therefore "all tests passed" means only "all tests configured and runnable in this build".
Test sources reach the code under test through cna_test_build_config: sharp-runtime and GoogleTest, compile-only include roots for the public modules, the test-support roots (modules/graphics/tests, modules/platform/tests, modules/input/tests, tools/common, tests/support, the vendored glTF header and, for one implementation test, modules/audio/src), SDL3 when SDL is configured and the tree is not SDL2-only, and, per compiled-in renderer, a CNA_RENDERER_PRESENT_<IDENTITY> definition plus that family's include roots and link-time switches. The PRESENT macros exist because only the default renderer's macro is defined project-wide: a family's suites guard their bodies on defined(CNA_RENDERER_<X>) || defined(CNA_RENDERER_PRESENT_<X>), so they compile to real tests whenever the family is compiled in and to nothing otherwise. Suites that include a family's headers are also filtered out of every tree that does not contain it (FNA3D, WebGPU, and the five EasyGL identities, which no longer includes OPENGL4, a family of its own with no easy-gl checkout).
Several tests drive a separate executable: helper processes for network loopback, the GamerServices dispatcher hang, audio without hardware, the SDL_Quit ordering hazard, mixer destruction with active voices, terminal restoration and resize, the X11 exit path, the CNB and glTF converters and cna-content. Their paths are baked into the object group as compile definitions and the group depends on the helper target, so a helper is always built before the test that spawns it. Those helpers are defined in cmake/Harnesses.cmake and the tool includes at the root.
Object groups and focused targets
The file counts are the C++ sources by owner, before per-configuration filtering (they sum with the C API, probe, Apple and fixture sources outside CnaTests to the 904 of the site method). A group with no sources in a configuration is not created.
| Group | Focused target | Sources | Links |
|---|---|---|---|
| audio | CnaAudioTests | 38 | cna_audio, cna_input, cna_media |
| content | CnaContentTests | 170 | cna_content, cna_graphics_ext |
| content_pipeline | CnaContentPipelineTests | 47 | cna_content_pipeline, cna_content_compiler |
| core | CnaCoreTests | 9 | cna_core |
| design | CnaDesignTests | 2 | cna_design |
| diagnostics | CnaDiagnosticsTests | 1 | cna_diagnostics |
| devices, devices_ext | CnaDevicesTests, CnaDevicesExtTests | 27, 10 | cna_devices, cna_devices_ext |
| gamer_services, net | CnaGamerServicesTests, CnaNetTests | 14, 18 | CNA_GamerServices, CNA_Net (networking on) |
| graphics | CnaGraphicsTests | 161 | cna_graphics_core |
| graphics_ext | CnaGraphicsExtTests | 92 | the CNA umbrella |
| input | CnaInputModuleTests | 47 | cna_input (and the XML serialization component off Windows) |
| inspector | CnaInspectorTests | 1 | cna_inspector (only with CNA_BUILD_INSPECTOR) |
| integration | CnaIntegrationTests | 2 | the CNA umbrella |
| math | CnaMathTests | 27 | cna_math (and the XML serialization component off Windows) |
| media | CnaMediaTests | 30 | cna_media |
| phone | CnaPhoneTests | 2 | cna_phone, cna_runtime |
| platform | CnaPlatformModuleTests | 85 | cna_platform, cna_input |
| renderers | CnaRendererTests | 69 | the CNA umbrella |
| runtime | CnaRuntimeTests | 27 | cna_runtime |
| storage | CnaStorageTests | 1 | cna_storage |
A new module tests/ directory needs an entry for its group in CNA_TEST_FOCUSED_TARGET_<group>; the configure stops with "No focused test target name is defined for group" otherwise. CnaTests additionally links the build-time content libraries, the inspector, the network modules and cna_phone explicitly, because none of them is in the umbrella.
CTest names, labels and working directory
gtest_discover_tests registers individual CnaTests cases with DISCOVERY_MODE PRE_TEST, so enumeration happens when CTest reads the test files rather than at configure time: by that mode's design the cases are known only once CnaTests has been built (this was read from the GoogleTest module's behaviour and not observed here). The registration pins the repository root as the working directory because numerous fixture paths are relative to tests/assets. Under an X11 selection a TEST_FILTER removes the live suites (they need a private server, a window manager, uinput or a portal) from the discovered set, because dedicated entries run them once on the right server; under ALSA the discovered cases get the null playback and recording devices; under X11 and Wayland they get a nonexistent session bus so no test reaches the desktop portal of the machine it runs on; and where the test-display policy applies they carry the Wayland guard below.
Renderer-family examples and the named platform, audio, input and glTF routes register through cna_register_renderer_test (cmake/TestHelpers.cmake), which takes NAME, COMMAND and optional TIMEOUT, LABELS, ENVIRONMENT, WORKING_DIRECTORY and SKIP_REGULAR_EXPRESSION. It escapes list separators inside labels and environment values, and when no working directory is given it pins the top build directory: the historical layout in which content-loading examples resolve their assets next to the executable, kept explicit after registrations moved into their modules. The two policies differ (discovered cases run in the repository root, helper-registered entries in the build root unless they say otherwise; the glTF rungs and the Direct2D unit entry name the source root, CnaInputTests does not), and mixing them can create a test that passes by direct execution and fails only under CTest. An opt-in launcher variable, CNA_RENDERER_GPU_TEST_LAUNCHER, routes single-executable display tests through a wrapper script; only the WebGPU examples set it, so a headless run there skips rather than aborts.
The skip convention
CTest exit code 77 is the deliberate skip code. A one-shot at the end of cmake/UnitTests.cmake gives it to every test registered at root scope so far; each module-local registration file ends with cna_apply_skip_convention() for its own directory; and cmake/Tests/ModuleProbes.cmake, which registers later, reapplies it to the link-closure tests. A test skipped for an absent display, adapter or helper is not successful feature evidence, and it is worth reading a test's registered properties before assuming that a report with no failures means it rendered pixels. Note that the module link-closure gates read CMakeFiles/<target>.dir/link.txt, which only Makefile generators write, so in a Ninja tree they report skipped.
Output gates: Vulkan and OpenGL4
Some defects are reported after the last statement a test can execute (during device destruction, for instance), out of reach of an in-process assertion, so CNA gates on the process output as well. The Vulkan renderer's debug messenger prints [Vulkan Validation] <message> for every layer message at warning severity or above; any CTest whose output contains that prefix fails. The OpenGL4 renderer installs a KHR_debug callback (Debug builds, or CNA_OPENGL4_DEBUG_OUTPUT=1) that prints [OpenGL4 GL Error] <message> for every GL error, undefined-behaviour or high-severity message, and a stock program that fails to build is reported on the same line; the same shape of gate fails those tests. The gates are added to FAIL_REGULAR_EXPRESSION as list members in cna_register_renderer_test, and for the thousands of discovered cases (which are in no directory's TESTS property at configure time) through the one hook that reaches them, gtest_discover_tests(PROPERTIES …) in cmake/UnitTests.cmake, where the two patterns are joined into one alternation because a semicolon inside PROPERTIES would split the pair. Each gate is applied only where a device of that API can exist (the identity is the default or a member of the compiled set), and each has a per-test exemption list that is empty and must stay reasoned: an entry must name the exact message and why it is not CNA's. Registrations that replace FAIL_REGULAR_EXPRESSION after registering would drop the gate, so a directory that does so re-applies it last: the Vulkan examples call cna_apply_vulkan_validation_gate and the OpenGL4 examples cna_apply_opengl4_gl_error_gate.
Platform and input contracts
PlatformFactory::GetAvailable() (modules/platform/src/PlatformFactory.cpp) feeds a parameterised conformance suite: the selected implementation (SDL3, SDL2, Win32, X11 or Wayland) joins Headless in every build, and Terminal joins as well on POSIX targets, so several implementations can be held to one contract in one process. CnaPlatformTests runs the display-independent contract with a long list of suite filters, shuffled and repeated three times to catch global subsystem-refcount leakage. Window tests have a separate CnaPlatformWindowTests registration because SDL's dummy video driver must be initialised in an isolated process: a shared CnaTests process may already have selected another video driver, in which case they would skip and contribute nothing. That entry also holds the cross-module window tests (GraphicsDevicePlatformWindowTests, GameWindowPlatformTest), so CNA_PLATFORM_CTEST_BINARY, which can redirect every platform and X11 route to the focused CnaPlatformModuleTests binary for a narrow CI cell, proves a smaller contract: that binary does not contain the graphics and runtime suites. CnaPlatformXErrorHandlerTests needs a real X11 connection (its subject is what Xlib does with a failing request), so it forces the video driver and display.
The native backends split their suites by what each needs, because a bare Xvfb has no window manager (so maximise, minimise, EWMH fullscreen and focus do not happen there at all) and the translation tables need no server whatsoever:
| Entry (selection) | Needs | Skips (77) when |
|---|---|---|
CnaX11MappingTests (X11) | nothing: scancode, key, modifier, button, focus, auto-repeat and evdev tables and the SDL-containment scan | never by environment |
CnaX11EvdevTests (X11, where evdev is compiled) | writable /dev/uinput and readable event nodes; no display | each test skips where the machine grants neither |
CnaX11IntegrationTests, CnaX11WindowManagerTests, CnaX11InputMethodTests, CnaX11ExclusiveFullscreenTests (X11) | a private Xvfb per run on a display number the launcher searches for (tools/platform/x11_test_server.sh); a window-manager binary, a private ibus, or both | Xvfb, the window manager or ibus is absent |
CnaX11TouchscreenTests (X11) | a uinput touchscreen taken exclusively by a private rootless Xorg; opt-in through the entry's environment | uinput, Xorg or its dummy and evdev drivers are missing |
CnaWaylandMappingTests, CnaWaylandProtocolTests, CnaWaylandPortalTests (WAYLAND) | nothing external: pure mapping functions, an in-process test compositor reached through WAYLAND_SOCKET, and a private dbus daemon for the portal | libdbus or dbus-daemon is missing (portal) |
CnaWaylandWestonTests, CnaWaylandWestonGpuTests, CnaWaylandWestonScaledTests (WAYLAND) | a private headless Weston through tools/platform/wayland_test_server.sh (software renderer; GL renderer; output scale 2) | Weston is not installed or does not come up |
CnaWaylandMutterTests, CnaWaylandMutterCzechTests, CnaWaylandIbusTests (WAYLAND) | GNOME's compositor headless on a private session bus, with real input through its RemoteDesktop API; a Czech keymap; a real input method | gnome-shell, dbus-daemon, ibus or its engine is absent |
CnaWaylandLinkClosure (WAYLAND) | readelf, and the Wayland validation harness target | not registered when either is absent |
No Wayland test reaches the desktop it runs on: the test environment points WAYLAND_DISPLAY and the session bus at nothing before any test starts. The sibling pages trace each backend's suites: X11, Wayland, Win32, Terminal and the backend overview. The Win32 backend is also validated through a separate standalone project, tools/platform/standalone_tests/CMakeLists.txt, which builds the platform module and its whole suite without the rest of CNA (cross-built with mingw-w64 and run under Wine, or natively with MSVC); it compiles the same sources under the same selection rules and does not replace the normal registrations.
The input label points to one canonical CnaInputTests filter maintained in cmake/UnitTests.cmake (CNA_INPUT_TEST_FILTER, a list of suite-name tokens), run with --gtest_shuffle --gtest_repeat=5 under a 1,200 second timeout. The comment above the run step in input-ci.yml still calls the entry “shuffled x3” and places the filter in CMakeLists.txt; both statements are stale, and the registration in cmake/UnitTests.cmake (five repeats, filter defined there) is what runs (read at 009d40f5, not executed). InputManager and related state are process-wide, so an order-dependent leak can escape a one-pass run; and if you add an input suite whose name matches none of the tokens you must extend that single filter, otherwise ctest -L input silently omits the new suite. Audio has analogous entries: CnaAudioPlatformTests (the contract, conformance and null-device suites, shuffled and repeated three times, with dummy SDL audio and null devices), the script-mode CnaAudioPlatformSelection_* cases, and for ALSA trees CnaAudioAlsaTests and a recording test that plays through ALSA's file device and measures what was captured.
Configuration and policy tests that need no GPU
A large share of the registered tests are not GoogleTest at all: they run CMake in script mode, small fixtures or Python gates. They are cheap, run anywhere and pin the rules the rest of the build depends on. Names that contain a variable in the table are generated per case.
| Test | What it pins | Mechanism |
|---|---|---|
CnaAudioPlatformSelection_<case> (eight cases) | The audio default, each implemented value, both reserved values and an unknown value | Runs cmake/AudioPlatformSelection.cmake under cmake -P |
CnaRendererDefaultSelection_<case> (four cases) | Single renderer, default inside the set, default listed first, default outside the set rejected | Runs cmake/RendererDefaultSelection.cmake under cmake -P |
CnaRendererRetired_* | Every name that is not a current identity is refused by name on the selector route, one generated test per entry of the retired list, plus the set-member and option routes and the controls that keep the refusal honest (a live selector is accepted, an unknown name stays unknown) | Runs cmake/RendererIdentities.cmake under cmake -P |
RendererIdentityRegistry, RendererCombinationRegistry, RendererTargetDiscipline, RuntimeRendererDiscipline, RendererCurationApiDecisions | Identity tables agree; combination rules match their documentation; families never read the global renderer scalar; renderer decisions stay behind descriptors; two API decisions of the renderer curation | Python scripts under scripts/ |
CnaSdl2OnlyRendererGate, CnaWaylandPlatformSelection, CnaSdlOffFindsNoSdlPackage | The SDL2-only guard; the Wayland selection is offered or refused with the packages to install, and changed no default; an SDL-free configure finds no SDL package anywhere in the tree | Script mode, or a nested configure in a private work directory |
CnaSdlPrebuiltFingerprint | The persistent SDL install is rebuilt when its source, patch series or arguments change (the SDL prebuilt) | Pure CMake on a fixture with fourteen cases |
CnaTestDisplayPolicy, CnaTestDisplayPolicyAsSubproject, CnaTestDisplayIsolation | Which displays count as the live desktop and the opt-in; the policy when CNA is a subproject; every registration in a configured tree obeys the policy | Script mode; a nested configure (CMake 3.28 or newer); scripts/check_test_display_isolation.py over ctest -N |
ModuleProbe_*, ModuleLinkClosure_* | Fifteen tiny consumers, one per module alias, each with forbidden-library patterns; the HEADLESS tree proves no native graphics SDK is linked, the VULKAN tree that only the selected renderer's SDK is | Executables plus scripts/check_module_link_closure.py; skipped under Ninja |
Eighteen *ShaderPackageReproducibility tests | Checked-in shader packages stay derivable from their declared sources | The package generator with --check; exits 77 without shaderc |
SpirvPayloadValidation, CnaProfileDeadTestClassifier | Every shipped SPIR-V module validates against the Vulkan 1.1 environment; the classifier that names tests dying on a graphics-profile refusal | Python; the first needs spirv-val and a VULKAN identity |
CApi* gates (twelve) | ABI baseline, header compatibility, coverage inventory and scope, limitations, doc export counts, route test coverage, bool contract, release gate, declared exports | Python over tools/c-api; the last two need the C API library |
Fourteen XnaPipeline* and provenance gates, and four XNB gates (CnaXnbSpecificationConformance, CnaXnbModelCorpusSweep, CnaXnbDependencyBoundary, CnaXnbPlanStatusConsistency) | The Content Pipeline parity report and input matrix are current; the API inventory is frozen; genuine-runtime interop cases; the XNB specification checker; the runtime does not link build-time dependencies | Python over the oracle tooling; the interop entries skip (77) without their runtime |
StrictXnaApiSurfaceCheck_Compile_Run, StrictXnaApiSurfaceLeakCheck_MustFailToCompile, CNAEXT_* | Extension calls stop a strict-purity build; the negative twin must fail to compile; the CNAEXT naming, guard, matrix, accessor and Doxygen rules | Compile-only checks and scripts |
Display isolation and meaningful GPU evidence
tools/platform/run_gpu_tests_private.sh <build-dir> -R '^Vulkan_' starts a private Weston compositor and a rootful Xwayland with DRI3, then runs CTest with DISPLAY and WAYLAND_DISPLAY pointing there; it cleans both up when done and never paints onto the developer's desktop (tools/platform/run_gpu_tests_private.sh). It refuses a build tree whose CNA_TEST_DISPLAY cache value forces a display, because CTest's configured environment would override isolation. That cache value's policy is in cmake/TestDisplayPolicy.cmake: empty by default (tests inherit the caller's display), the live desktop is honoured only with CNA_TEST_ALLOW_LIVE_DISPLAY=ON, any other value (an Xvfb, say) is honoured, and every test carries the Wayland guard WAYLAND_DISPLAY=string_append:, which turns "unset" into "empty" (connection refused) so libwayland cannot fall back to the owner's compositor while an exported value, such as the private runner's, is left alone. Xvfb often lacks DRI3 and cannot substitute for real Vulkan presentation. A launcher's code 77 means "environment unavailable", not "renderer correct". Some Wine-based differential tests have a documented private-runtime hang and need separate treatment; after a ctest run the wrapper names tests that died on a graphics-profile refusal before their assertions.
Renderer examples are selected by more than the compiled-in identity. Vulkan's example and oracle block, for instance, requires CNA_BUILD_EXAMPLES=ON, CNA_BUILD_TESTS=ON, a non-Emscripten, non-Windows target and a CNA_GRAPHICS_RENDERER STREQUAL "VULKAN" guard, and its tests are named Vulkan_*. That guard is re-pointed to each compiled identity in turn by the renderers loop in modules/renderers/CMakeLists.txt, so a multi-renderer build that includes Vulkan also enters the block even when another identity is the default; other families guard differently (some compare against the default identity), so read each family's example CMake file rather than assuming. There are also diagnostic corpus executables that emit raw pixels for a separate two-build comparison gate (cna_diag_compare diffs two dumps within a tolerance); they intentionally are not CTest entries, because CNA_GRAPHICS_RENDERER is a compile-time choice and two renderers need two builds. To claim parity, record both comparison inputs and the gate outcome. WebGPU's display tests go through a preflight and retry launcher that turns a missing display or adapter into a skip and propagates real failures unchanged (modules/renderers/webgpu/webgpu_test_launcher.sh).
What CI covers — and what it does not
Twenty workflow files define 28 jobs. The rows below say what each gates at this snapshot and where its boundary is. These are workflow roles, not a promise that every named job runs on every push: most have path filters, several ignore documentation-only changes, and the general suite was added specifically because older specialised jobs ran only filtered subsets. A green filtered job must not be read as an unfiltered regression run, and whether the current jobs are green was not checked for this page.
| Workflow | What it runs | Boundary |
|---|---|---|
general-tests-ci.yml | Automatic (documentation-only changes excluded). Checks the build-performance policy, does a full default build (Ninja, Debug, OPENGLES3, tests and examples on) and runs an unfiltered ctest under a fixed Xvfb display passed as CNA_TEST_DISPLAY; a classification step allows exactly one known, tracked failure and fails the job on anything else. | One EasyGL identity on one host, not all renderer families. Its ctest step is continue-on-error, so the classification step is the gate. |
input-ci.yml | Four rows (OPENGLES3; OPENGLES3 with ASan and UBSan; SDL_RENDERER; VULKAN): build CnaTests only and run xvfb-run -a ctest -L input. | The input subset only, on Xvfb. |
multi-renderer-ci.yml | The identity, combination, discipline and descriptor scripts; a HEADLESS, SOFTWARE and STUB build; each renderer reachable by name and by the environment variable; the default-membership tests; CnaTests (with three filter exclusions); a single-HEADLESS control. | No GPU or window API; does not establish Vulkan or Direct3D parity. |
platform-ci.yml | Six jobs: a contract matrix (SDL3 with OPENGLES3, SDL2 with OPENGLES3, SDL3 with Vulkan, SDL3 with Software, Headless, Terminal); a MinGW cross-build of the Win32 platform harness executed under Wine; a native MSVC job (manual only); two SDL-free X11 lanes (one with GPU renderers and ALSA); an SDL-enable matrix that checks configure outcomes. Source gates run on the Headless cell and in the SDL-free lane. | The Wine lane covers the platform harness, not renderers. The Wine Direct3D probe can report an environment limitation and still pass. No workflow covers native Wayland or Android. |
devices-tests.yml | The devices-ubsan preset; the Microsoft::Devices and CNA::Devices suites named in its two --gtest_filter lists (they miss seven Microsoft::Devices suites and, by naming renamed dialog suites, four CNA::Devices ones); the strict XNA API surface check. | Path-filtered; the only workflow that uses a preset. |
gltf-renderer-stride-ci.yml, gltf-sanitizers-ci.yml | The glTF ladder and stride conformance on STUB, HEADLESS, OPENGLES3, VULKAN and SOFTWARE, plus an L7 corpus job that replays the EasyGL goldens through a pinned external viewer; ASan and UBSan over glTF import with Draco on and off, golden and provenance checks. | The Vulkan, Software and Direct3D 11 golden sets are recorded, not CI-run. |
emscripten-multi-renderer-ci.yml, htmldom-ci.yml | One WebAssembly bundle with WEBGL2, CANVAS, HTML_DOM and SVG_DOM (configure, build, and assertions that each family's archive and the JavaScript selection exports are present); the HTML_DOM suite in headless Chromium. Both pin an emsdk version. | The bundle lane is designed to establish configure, build and link, not browser behaviour; both workflows pin a sharp-runtime revision that lacks a component CNA's default build requests, so by a static reading they stop at configure at this snapshot (CNA-BUG-199). |
apple-ci.yml, metal-macos-ci.yml | A host-portable Apple CMake check; a macOS build with SDL_RENDERER and portable suites; an iOS device final link and a simulator launch of one frame; native Metal contract tests. | No physical-device or pixel-correctness claim. The Metal workflow's push path filter still names three CMake files from the layout that was later dissolved into module files (a backend-libraries manifest, an examples file and a Metal tests file, none of which exists at this snapshot), so it does not trigger on edits to the current test registration. |
d3d-windows-ci.yml, gdi-windows-ci.yml, content-pipeline-windows-ci.yml | Native MSVC builds for Direct3D 11, Direct3D 12 and Direct2D (and a recompile of the checked-in HLSL); the GDI renderer's ctest -L GDI; the content tool and content tests with a determinism hash check. | Manual dispatch only (workflow_dispatch) for the first two, with no Windows lane for Direct3D 9; the third runs on pushes to its own branch and manually. CnaTests is not built there. |
Five C API workflows (c-api-abi-baseline, -compat-matrix, -coverage-gate, -limitations, -release-gate) | The ABI baseline, header compatibility across several compilers, the coverage inventory, the limitations matrix and the release gate, each with regeneration and drift checks. | Header and inventory checks: none builds the C library, and the release gate itself reports the API is not ready. |
32bit-arithmetic-ci.yml | A standalone project built with -m32 that runs three overflow checks; triggered only by the files it guards. | Narrow by design. |
What no workflow runs: the whole XNA oracle corpus, the FNA differential harness, the WebGPU, SDL_gpu and OpenGL4 parity-fixture entries, and the Vulkan, Software and Direct3D 11 glTF golden sets. Renderer evidence remains per family and per configuration; see Renderer coverage is configuration-scoped.
The 32-bit arithmetic lane in detail
No workflow builds the CNA library or CnaTests for a 32-bit target. The one lane that runs code at a 32-bit width is 32bit-arithmetic-ci.yml. It checks something narrow: that size computations that multiply into std::size_t refuse to wrap when size_t is 32 bits wide. A 64-bit host cannot show this, because a uint32_t simulation there does not exercise the real code paths. The 64-bit unit tests of the audio-tag parser, for example, can only simulate a narrow size_t.
The lane builds a standalone CMake project, tools/media/arithmetic32bit, on ubuntu-latest with gcc-multilib and -m32. The project is not added to the main build, whose SDL3, SDL3_image, SDL3_mixer and FFmpeg dependencies are not provided for a 32-bit target in this toolchain. It compiles only the implementation files the checks need, into three executables registered as CTest tests:
AudioTagParser32BitOverflowCheck: crafted ID3v2.3 frame-size and FLAC vendor-length fields that would wrap a 32-bit sum, checked againstAudioTagParser.cpp(built withContentLoadException.cpp).FramebufferAllocation32BitOverflowCheck: the software framebuffer planner,framebuffer_allocation_32bit_check.cpp.TextureAllocation32BitOverflowCheck: the software texture planner.
Each harness first asserts sizeof(std::size_t) == 4, so a mis-built 64-bit binary fails instead of passing vacuously. The framebuffer harness then checks four things:
- A 3840×2160 4× MSAA layout with stencil plans to exactly 174,182,400 bytes.
- A 16384² 4× request fails as
ArithmeticOverflowbefore any multiplication wraps. - An 11000² request, whose product is representable, fails as
ByteBudgetExceeded. - Generated mip storage counts against the budget.
The planners behind these checks (SoftwareFramebufferAllocation.hpp) contain no #ifdef; they are width-portable by construction. The byte budget of 512 MiB per surface (SoftwareFramebufferMaxBytes) is chosen to keep one surface practical in a 32-bit process while still admitting that 4K GDI back buffer.
The workflow triggers on pushes and pull requests to next, develop and main that touch the guarded sources, and on manual dispatch. It is evidence for these three computations only. It says nothing about a 32-bit build of CNA as a whole. The tag-parser, framebuffer and texture checks come from CNA's remediation items REMED-MEDIA-001, GDI-067 and GDI-076. Checked by reading the workflow, the standalone project and the harnesses at 009d40f5; not executed.
Change-oriented minimum and broader checks
These are representative recipes; the complete matrix is on What to test after changing X. Commands identify actual preset, label and target names but were read from CMake and the workflows, not executed against every dependency configuration.
| Change | Fast local evidence | Broader pre-merge evidence |
|---|---|---|
| Core or math | cmake --preset unit; cmake --build --preset unit-core or unit-math; run the focused binary. | Build and run the aggregate CnaTests; a compiler and host matrix if public headers or arithmetic widths changed (the 32-bit workflow guards a few named files). |
GraphicsDevice state or resources | cmake --build --preset unit-graphics; run CnaGraphicsTests with a focused --gtest_filter. The unit preset uses STUB, so device-dependent cases run against a renderer that draws nothing. | Selected renderer oracles, at least one independent backend, resource and disposal tests and GPU validation where applicable. |
| Vulkan | No preset selects VULKAN: configure a Vulkan tree by hand and run a named Vulkan_* CTest case under the private GPU wrapper. | The relevant Vulkan suite with the validation gate, lifetime traces, a second-frame, resize and disposal case, the neutral graphics suite and a second-renderer comparison. |
| OpenGL4 | An OPENGL4 tree (native GL, no easy-gl sibling); the OpenGL4-labelled entries under the private wrapper, with the GL error gate active in Debug builds. | The parity fixtures and the EasyGL comparison entries, and a sanitizer run with the Mesa suppression file for driver-side leaks. |
| Vendored SDL, an SDL patch or the SDL arguments | ctest -R '^CnaSdlPrebuiltFingerprint$'; reconfigure and read the (re)building or is current line. | The SDL_gpu entries under the private wrapper, including SdlGpu_TextureDefragment, which forces the patched Vulkan defragmenter over a texture allocation three times and reads the survivors back. |
| Win32 | The standalone platform harness cross-built with mingw-w64 and run under Wine, and the Win32 event, window and service suites. | The manual native-MSVC workflow for DPI, clipboard and real Direct3D, plus the renderer and input tests the patch affects. |
| Input | The focused input binary for a fixture; ctest -L input for the shuffled, repeated suite. | Platform-specific mapping and live-device or window tests on the affected hosts. |
| CMake selector | The script-mode gates (ctest -R '^Cna(AudioPlatformSelection|RendererDefaultSelection|RendererRetired)_'), the scripts/check_renderer_*.py scripts, and configure runs of the expected valid and invalid cases. | A single-renderer and a multi-renderer build, the generated registry and descriptor gate, the final link, and a runtime selection smoke (cna_demo_renderer_selection). |
| C API or a binding | Configure with CNA_BUILD_C_API=ON (networking must be on) and run the CApi_* smoke and handle tests; the header and inventory gates need no build. | The five C API workflow gates and each maintained binding's own revision-pinned suite; this interop mapping is documented on C API and bindings architecture. |
Avoid running the entire GPU suite on a live desktop or interpreting skips as passes. For a hang, retain the CTest timeout and capture thread stacks; for a lifetime bug, reproduce both the explicit Dispose and the destructor or device-outliving paths under ASan where possible. The generated test index is a syntactic inventory of registrations; the owning CMake registration and a configured ctest -N are authoritative for a particular build.
Source tour for test ownership
cmake/UnitTests.cmake: follow the source filters, object groups, focused targets, GoogleTest discovery and the platform, audio, input and glTF routes.cmake/TestHelpers.cmake: renderer-test registration (working directory, environment, timeout, skip pattern), the Vulkan and OpenGL4 output gates and the skip convention.cmake/TestDisplayPolicy.cmakeandcmake/TestDisplayPolicyRules.cmake: which display tests use and the Wayland guard.cmake/Tests/ModuleProbes.cmakeandcmake/Harnesses.cmake: the module probes, the policy gates and the helper executables.CMakePresets.json: distinguish configure from build presets and use the exact binary tree they declare.tools/platform/run_gpu_tests_private.sh: inspect the environment isolation and skip behaviour before relying on screenshots..github/workflows/: identify the job that builds and runs a test on a relevant host.
Deep dives on this topic
Long-form pages that explain the exact semantics, invariants and evidence behind this subject.
- Android and Apple targets: structure, lifecycle, assets and evidence — How CNA's Android application and NDK code are structured, how Game handles mobile lifecycle events, how assets and saves are found on a device, and what the macOS and iOS workflows check.
- Avatars: the inert XNA API and the skinned extension — Why CNA's XNA avatar classes report Unavailable and draw nothing, and how the opt-in SkinnedModelEXT path samples clips, builds the bone palette, tints parts and loads its content.
- C API evidence, coverage inventory and release gate — How far the evidence for CNA's C ABI 0.29.0 reaches, what each release-gate criterion really checks, why the gate measures two unmet criteria at 009d40f5, and how the coverage inventory classifies modules.
- CANVAS, HTML_DOM and SVG_DOM: browser object models, refusals and evidence — How CNA's three non-WebGL browser renderers represent sprites, targets and blending, where each refuses work, why two cannot read the back buffer, and which browser tests actually run.
- CNAEXT catalogue: extension surfaces by namespace — A catalogue of CNA's non-XNA surface at snapshot 009d40f5, namespace by namespace, with member names, reasons and boundaries, how much the CNAEXT marker covers, and what the strict check proves.
- CNJ documents: envelope, sourceFile and sidecar rules — The rules a .cnj document must satisfy in CNA: envelope validation, which reader accepts which type, how sourceFile and sidecar paths resolve, which parser reads which field, and what CNJ does not bound.
- Compatibility levels and the evidence vector — The distinct levels of XNA compatibility CNA keeps apart, the evidence vocabulary used across the site, what each renderer class can prove, and how to state a verification claim.
- Compiled XNA effects: admission, reflection, passes and renderer runtimes — What happens to Direct3D 9 Effect Framework bytecode in CNA: admission order and preflight bounds, the reflected object graph, parameter upload, pass-state publication, cloning, the XNB EffectReader and per-renderer translation.
- Configuring CNA per target: routes, toolchains and what a green build proves — How the Linux, Windows (MSVC and MinGW-w64 with Wine), Android, Emscripten and Apple routes configure CNA, run its tests, and what a successful configure, build or test run on each one proves.
- Content input boundaries and hostile-input behaviour — Where CNA enforces size, count, nesting and path limits across XNB, CNB, CNJ and the build importer, how the loaders are fuzzed, which exceptions hostile files produce, and which gaps remain.
- ContentManager resolution, caching and failure rules — Which file ContentManager::Load<T> actually reads, what RootDirectory does and does not confine, what the cache keeps, and which exception each tier throws at this snapshot.
- Coordinate and composition conventions: handedness, row vectors, depth and clip space — CNA's right-handed basis, row-vector matrices applied in reading order, quaternion products that run the other way, the [0,1] depth range derived three times, clip-space W and the ToColumnMajor bridge.
- Curve evaluation: keys, tangents, loop types and the XNA reference — How CNA's Curve evaluates: sorted keys, the per-segment Hermite basis, Step continuity, the five loop types, smooth tangents and degenerate curves, each compared with the XNA 4.0 algorithm.
- Custom HLSL ShaderEffect on the Direct3D renderers — How DIRECTX11, DIRECTX12 and DIRECTX9 compile custom HLSL, resolve uniform names by reflection, feed SpriteBatch and 3D draws, bind textures and build Direct3D 12 pipeline states.
- Device reset, disposal, adapters and format queries — Order and failure boundaries of GraphicsDevice::Reset, the manager's hooks, device-loss status, resource registration and disposal, plus CNA's adapter, format and profile queries.
- DIRECT2D and GDI: two Windows 2D delivery stacks — How CNA's two Windows-only 2D renderers draw, refuse, present and recover: Direct2D over a private Direct3D 11 device versus GDI over a private CPU 2D core, and what their tests prove.
- Direct3D evidence: MinGW cross-builds, Wine translators and native Windows — How DIRECTX9, DIRECTX11 and DIRECTX12 are built and tested: MinGW cross-builds, DXVK and vkd3d-proton gates, the shared parity inventory, forced-headless runs and the manual Windows job.
- Direct3D presentation, swap interval, clears, viewport and scissor — How DIRECTX9, DIRECTX11 and DIRECTX12 treat presentation modes, PresentInterval, back-buffer and depth formats, full screen, flip-model rebinding, viewport, scissor, clears and depth bias.
- DIRECTX11 and DIRECTX12 internals: lifetimes, frames in flight, descriptors and target finalisation — Device creation, lifetime groups and recovery, Direct3D 12 frame slots and growable descriptor heaps, HeadlessEXT, readback, MRT and cube finalisation and render-target usage on the DXGI renderers.
- DIRECTX9: stock-effect bytecode, device lifecycle and oracle findings — How the XNA-fidelity renderer compiles Microsoft's stock effects, enforces GraphicsProfile from D3DCAPS9, recovers lost devices, handles targets and why its sprite projection is what it is.
- easy-gl and meta-gl: the two-library GL stack beneath the EasyGL family — How meta-gl and easy-gl split loading, typed calls, ownership and failure beneath CNA's five GL identities: revisions CNA needs, feature gating, per-thread state, context loss, tests and build inheritance.
- EasyGL state, clears, targets, queries and buffers: current semantics — What the EasyGL GL-profile renderer does at this snapshot for wireframe, occlusion counts, colour masks, clears, two-sided stencil, fog, base vertex, render targets, context-loss policy and viewports.
- EasyGL: five GL profiles, one implementation, and the other GL-named renderers — What OPENGLES2, OPENGLES3, OPENGL33, WEBGL1 and WEBGL2 share in EasyGL, where they differ, how far evidence carries between them, and how OPENGL4 and PORTABLEGL differ.
- Effect object model: techniques, passes, parameters and the draw packet — What a CNA Effect contains in its stock, compiled and ShaderEffect forms: collections, pass application, Clone and Dispose, stock parameter tables versus XNA, EffectParameter storage and GpuDrawParams.
- Evidence tiers of the native modern GPU renderers — What VULKAN, SDL_GPU, WEBGPU and METAL implement at this snapshot, what evidence backs each, why a capability bit is not evidence, and the defect shapes these renderers exposed.
- Exit, Exiting, Dispose and destruction — What each way of ending a CNA game runs: Exit versus Exiting, explicit Dispose versus destruction, the XNA 4.0 disposal order, repeated, re-entrant and throwing disposal, and component lifetimes at shutdown.
- Four shader routes: stock semantics, D3D9 stock sources, compiled effects and ShaderEffect — How CNA answers XNA's .fx: renderer-owned stock effects, DIRECTX9's recompiled Microsoft sources, compiled Effect Framework bytecode on qualified renderers, and the renderer-specific ShaderEffect contract.
- free-direct and free-api beneath the FREEDIRECT renderer — How CNA's 2D-only FREEDIRECT renderer sits on free-direct and free-api: the dependency chain, what each library implements, the fopen macro hazard and its containment, inherited limits and evidence rules.
- Game components and the service container — Exact contracts of GameComponent, DrawableGameComponent, GameComponentCollection and GameServiceContainer in CNA: ordering, content loading, disposal without unregistration, events, iterators and type-keyed services.
- GamerServices behaviour contract — What CNA's GamerServices does without Xbox LIVE: the four local identities, persisted achievements and leaderboards, the Guide dialogs the game draws, the inert members and the dispatcher hang.
- GameTime and the timestep: exact clock semantics — What CNA's game clock reports: GameTime protection, fixed-step catch-up with worked numbers, IsRunningSlowly hysteresis versus XNA 4.0, ResetElapsedTime, vsync versus timestep, and the browser clock.
- GameWindow, GraphicsDeviceManager and the supporting types — CNA's single GameWindow facade and its failure policy, GraphicsDeviceManager construction, CreateDevice, ApplyChanges and presentation preferences, and LaunchParameters, TitleContainer, TitleLocation and FrameworkDispatcher.
- glTF conformance: corpus, oracle ladder and evidence — The pinned glTF specification, the 148-asset generated corpus, the L0-L7 oracle ladder, renderer-owned pixel campaigns, the Khronos comparisons, the defect ledger and the milestone statuses.
- Graphics resource lifetime: tracking, copies, moves and disposal — Which graphics resources a CNA GraphicsDevice tracks, how copies and moves of each resource type behave, what disposal unbinds, and which resources report lost content.
- GraphicsDevice: the shared device contract — Exact device-level behaviour of CNA's GraphicsDevice: construction and windows, bound state objects, viewport and scissor, Clear and Present, bindings, draw calls, readback and extensions.
- Math value types in C++: object layout, equality, hashing and API shape — Which CNA math types carry a vtable, why Color is 24 bytes on 64-bit hosts, the internal vertex stream structs, output-reference aliasing, exact equality, hash and ToString differences, and the split argument exceptions.
- Module boundaries: cycles, umbrellas and the gates that enforce them — Why CNA keeps three static-archive cycles, what the CNA, CnaExt and BuildConfig compositions carry, what each boundary gate and the fifteen module probes check, when they run and what a violation looks like.
- Native C API contract: admission, buffers, retention and route families — What CNA's C ABI 0.29.0 version checks admit, the exact count-then-copy protocol, callback and registration lifetimes, resources that retain others, caller-created devices and all 61 headers by family.
- Network sessions and the SystemLink protocol — What each NetworkSessionType really does in CNA, the SystemLink wire and discovery protocols, the trust model, delivery guarantees, host migration and the evidence behind them.
- Oracles, tolerances and engagement gates — The oracles CNA uses (real XNA, FNA, derived tables, cross-renderer controls, own goldens), their authority and tolerances, and the gates that prove a Wine, Proton or browser run engaged.
- Planes, rays and bounding volumes: exact containment and intersection semantics — Half-space conventions, plane transforms, ray tolerances, box corner order, sphere and frustum containment rules in CNA, compared function by function with XNA 4.0, with workarounds for every mismatch.
- Presentation modes, swap interval, native handles and back-buffer readback across renderers — What each renderer family does with the presentation mode, swap interval, formats and full-screen request, how window handles are borrowed, and exactly what GetBackBufferData returns.
- Rectangle, Point and Color: integer geometry and packed colour semantics — Rectangle's half-open edges, touching and empty rules, Point's missing Vector2 bridge and rounding, Color's AABBGGRR word, the 141/140/139 named-colour counts, construction rounding and premultiplied alpha.
- Render targets: usage, cube faces, resolve and readback — RenderTarget2D and RenderTargetCube semantics at the pinned snapshot: construction, binding rules, RenderTargetUsage, cube faces, resolve and mip generation, readback, disposal and lost content.
- Render-target binding, clears, viewport and scissor across renderers — The shared SetRenderTargets transaction, the no-op rebind, what RenderTargetUsage selects, which clears throw, and how each renderer family applies clears, viewport and scissor.
- SDL_GPU shader intake, pipeline keys and draw order — Why CNA's SDL_GPU renderer uses precompiled SPIR-V in SDL_gpu's set convention, which GLSL ShaderEffect accepts, how pipelines are keyed, what state is dynamic, and how draw order and vsync are kept.
- SDL_GPU uploads, render-target lifetime and swapchain recovery — What SDL_gpu validation exposed in CNA's SDL_GPU renderer, when uploads may cycle, how render targets outlive their wrappers, what MRT writes, and how a failed swapchain acquisition keeps the frame.
- SDL_RENDERER: the 2D contract, its refusals and its evidence — Where SDL_RENDERER's 2D boundary sits: execution-time 3D refusal, its single capability, emulated and unhonoured XNA features, address modes, clears, readback coordinates and how its tests run.
- Sensors and vibration: delivery, math and lifetime — Which thread delivers a CNA sensor reading and in which units, the Android compass and motion mathematics, the landscape remap, VibrateController semantics, the open Dispose(bool) defect and the evidence limits.
- sharp-runtime parity boundaries and verification — sharp-runtime's parity target, its permanent deviations and their reasons, frozen naming, platform and 128-bit boundaries, and what its tests, negative consumers, audit index and sanitizer runs do and do not prove.
- Sound effects, streaming, capture and the audio evidence — Caller-visible semantics of SoundEffect, 3D audio, XNB sound decoding, DynamicSoundEffectInstance buffer accounting and Microphone capture in CNA, and what the audio tests do and do not prove.
- SpriteBatch sorting, flushing and renderer batching — How CNA's SpriteBatch flushes and sorts (XNA's unstable quicksort, reproduced), what each renderer kind does with the sprite stream, the viewport-local projection and the Direct3D 9 half-pixel offset.
- SpriteBatch: state, lifecycle and error semantics — Exact shared SpriteBatch contract in CNA: Begin defaults, when states reach the device and what End leaves behind, Immediate exclusivity, exceptions, Draw overloads, sub-pixel destinations and texture lifetime.
- SpriteFont and DrawString text layout — Exact text layout in CNA: SpriteFont tables and default character, per-glyph advance and the XNA first-glyph rule, MeasureString, UTF-8 decoding, whole-string flips, rotation and sub-pixel glyphs.
- State objects: identity, binding and what reaches the renderer — BlendState, DepthStencilState, RasterizerState and SamplerState in CNA: shared identity, XNA's freeze-on-bind rule, what each renderer hook receives, profile checks and per-family support.
- Surface formats: profile gates, renderer verdicts and format usage — How CNA decides whether a texture, cube, volume or render target may use a SurfaceFormat: per-resource profile tables, the renderer verdict, draw-time rules and usage masks.
- Test populations, counts and structural gates — Which test layer produced a CNA result, what each static count at 009d40f5 counts, why no CTest total exists, and how identity, containment, inventory and mutation gates work.
- Tests, examples, presets and tools: what each verdict proves — What a green result from CNA's test corpus, examples, golden images, CMake presets and developer tools actually establishes, sorted by authority class, and how to read a result before citing it.
- Texture data transfer: SetData, GetData, mip levels and streams — Exact SetData and GetData semantics for Texture2D, TextureCube and Texture3D: transfer windows, the CPU shadow, mip levels, compressed blocks, readback and FromStream.
- The cross-platform contract: axes, composition and evidence per route — How target OS, platform implementation, renderer set, audio implementation and the XNA surface compose in CNA, what IPlatform owns, and why each platform claim is an evidence vector.
- The Game class: contract, run modes and extension points — Exact semantics of CNA's Game base class: event types, property guards, override points, Run versus RunOneFrame versus Tick, the browser loop, reserved debug keys and the exception boundary.
- The input model: snapshots, keys, the mouse and logical coordinates — What Keyboard and Mouse GetState return and when, the Keys numbering and layout helpers, the mouse extensions and cursors, and how renderers map window pixels to logical coordinates.
- The renderer contract: IGraphicsRenderer defaults, factories and failure shapes — Which IGraphicsRenderer bodies a renderer family must write, what each inherited default does to a public call, how null factories fail, and the evidence ladder behind a feature.
- The web target: Emscripten build contract, browser loop, storage and renderer evidence — CNA's Emscripten build contract (exception ABI, Asyncify, threads), the Asyncify browser loop, content and save storage in the virtual file system, web networking, and the evidence per browser renderer.
- The XNB container and the ContentReader object graph — Byte-level and session-level rules for reading .xnb files in CNA: header, compression, the type-reader table, one-based object indices, shared resources, limits and custom-reader rules.
- Touch panel and gesture semantics — When CNA's touch state advances, which connected flag to trust, TouchCollection and TouchLocation contracts, gesture filtering, timestamps and the deliberate differences from FNA.
- Vector, Matrix and MathHelper numerics: interpolation, clamping and degenerate inputs — What CNA's vector, matrix and MathHelper functions return for out-of-range amounts, inverted clamps, NaN, zero vectors, singular matrices and bad camera input, compared with XNA 4.0, plus the precision and test evidence.
- Verification tiers: evidence forms, oracle authority and CI reporting — A decoding key for CNA verification claims: claim labels, what each evidence form establishes, which authority decides which question, what CI runs at 009d40f5 and how to report it.
- Vertex and index buffers: CPU shadows, SetDataOptions and layouts — How CNA vertex and index buffers store, upload and read data, what SetDataOptions does on each renderer family, and how vertex layouts reach a renderer by stride and by declaration.
- Vertex declarations, bindings and stream composition — From C++ vertex values to the renderer boundary: stream layouts, VertexDeclaration rules and profile limits, index widths, dynamic updates, VertexBufferBinding, semantic composition, the minimum-offset fold and draw validation order.
- Vulkan draw-time state, ordered clears, occlusion queries and descriptor pools — How CNA's deferred VULKAN renderer carries blend, stencil, viewport and scissor state, orders clears, counts occlusion queries and grows descriptor pools, with the defects behind each rule.
- Vulkan presentation, frame pacing and back-buffer readback — How CNA's VULKAN renderer picks its swapchain format and present mode, synchronises two frames in flight, and reads the back buffer without racing the presentation engine.
- WebGPU renderer semantics: surfaces, targets, mips and pipeline state — Exact behaviour of CNA's WEBGPU renderer: non-sRGB surface policy, target-relative SpriteBatch coordinates, cube render targets, blit-free mip generation, dynamic and baked state, ordered clears and BC textures.
- Windows from Linux: MinGW cross-builds, runtime staging and Wine evidence — The MinGW-w64 route from Linux to a runnable Windows test: toolchain, target-built SDL, DLL staging, CTest emulators, which Wine runtime owns each renderer, prefix hygiene and evidence tiers.
- XACT at run time: engine, banks, cues and their limits — The run-time contract of CNA's XACT classes: load failures, cue ownership and registration, AudioEngine::Update, variations, RPC curves, categories, instance limits, filters and wave banks.
- XNB type readers: wire contracts and validation — What each built-in XNB type reader reads, validates and refuses at this snapshot, from primitives and textures to SpriteFont, Model, effects and audio, with the evidence behind each.
Known issues in this area
Current defects, gaps and limitations at this snapshot that touch this subject.
- CNA-BUG-012: Two tests still encode the wrong occlusion-query expectation for SDL_GPU after the renderer was made truthful — SDL_GPU now reports OcclusionQuery false and refuses queries, but the shared capability test still expects true and SdlGpu_OcclusionQuery_Limitation expects a refusal message the public constructor never produces.
- CNA-BUG-035: devices-tests.yml's exact-name gtest filters skip seven Microsoft::Devices suites and never match the FileDialog and MessageBox suites — DEVICES_GTEST_FILTER misses 29 of 474 TEST definitions under modules/devices/tests, and CNA_DEVICES_GTEST_FILTER names FileDialogTests.* and MessageBoxTests.*, which match no suite, so the 12 FileDialog and MessageBox ca
- CNA-BUG-036: run-all-renderer-smoke-tests.sh ignores the build's exit status, and its '-- -k' build flag aborts every Ninja build, so smoke verdicts can come from stale binaries — The orchestrator runs cmake --build <dir> -j4 -- -k and discards the result; -k without a count is a make option that Ninja rejects, so in a Ninja tree nothing is rebuilt and ctest runs whatever executables the tree alre
- CNA-BUG-054: HEADLESS reports occlusion-query support and a precise pixel count while its query always answers 1 — HEADLESS inherits OcclusionQuery = true and the default isPixelCountPreciseEXT() = true, but HeadlessOcclusionQueryRenderer completes at once and returns PixelCount() == 1 whatever was drawn.
- CNA-BUG-062: MODULE_SCOPE in generate_coverage_inventory.py leaves design, diagnostics and inspector unclassified, so every C API inventory gate stops — The C API coverage generator refuses to run because three public modules are missing from MODULE_SCOPE, so the coverage, limitations, scope-model and release-gate checks fail at this snapshot and the committed coverage n
- CNA-BUG-093: HeadlessRenderer::GetLastFrameStatistics() reports the frame in progress, not the finished frame its documentation names — GetLastFrameStatistics() is documented as the statistics of the frame ending at the most recent Present(), but it subtracts the snapshot taken at that Present from the running totals, and a test pins that behaviour.
- CNA-BUG-099: 24 classic SDL_GPU CTests still fail in CNA's latest recorded run; CNA's records name a cause for only three of them — CNA's SDL_GPU records list 26 classic ^SdlGpu CTests still failing just before TARGET; two are analysed in their own entries. Of the other 24, covering formats, render targets, render state and recovery, the records give
- CNA-BUG-184: Core's build files still carry dependencies its sources dropped: -lembind on Emscripten and a link probe that tolerates SDL3 — modules/core/CMakeLists.txt still adds -lembind for every Emscripten consumer although the browser preference reader was rewritten with EM_JS to avoid embind, and probe_core still permits SDL3 although core no longer use
- CNA-BUG-192: metal-macos-ci.yml's push path filter names three CMake files that no longer exist and omits the files that now register the Metal tests — The Metal workflow's push filter lists three removed CMake files, while edits to modules/renderers/metal/CMakeLists.txt or its examples/CMakeLists.txt, where the Metal_* CTests are registered, do not trigger it.
- CNA-BUG-193: general-tests-ci.yml still allowlists EasyGL_GraphicsDevice_ReferenceStencil as a known failure after REMED-GFX-236 fixed it, so a regression of that test cannot turn the job red — The only unfiltered CI job classifies failures against a one-entry KNOWN_FAILURES list whose entry is the EasyGL ReferenceStencil test, which CNA's own record says now passes; if EasyGL's ReferenceStencil override breaks
- CNA-BUG-199: Six CI workflows pin August 2026 sharp-runtime commits that lack components CNA's default closure requests (Xml.Serialization off Windows, Resources everywhere), so they stop at configure — CNA's default sharp-runtime component closure has requested Xml.Serialization on non-Windows targets since 2026-09-06 and Resources on every target since 2026-09-20. The workflows pinned to sharp-runtime bc8dbf41, f23ded
- CNA-BUG-202: In a multi-renderer build, non-default renderers' example CTests are registered but run under the default renderer — The SDL_GPU, SOFTWARE, STUB and VULKAN example blocks are entered for a non-default member of CNA_GRAPHICS_RENDERERS, yet their registrations select no renderer, so each executable runs under the build default.
- CNA-BUG-203: SdlGpu_2D_HeadlessDriver forces headless mode under the x11 video driver that the SDL_GPU constructor refuses for it — On non-Windows hosts the SDL_GPU headless registrations reuse the ordinary test environment (SDL_VIDEODRIVER=x11 by default), but SdlGpu_2D_HeadlessDriver also sets CNA_SDLGPU_TEST_FORCE_HEADLESS=1, which the constructor
- CNA-BUG-247: The EasyGL L7 policy and report justify zero tolerance with 'all 137 renderable assets' while the report's own fields record 140 captured, byte-identical assets — The EasyGL L7 policy's justification, copied into its report, cites 137 renderable assets while the report records 148 assets, 140 byte-identical captures and 8 rejections, and misc/FUTURE.md repeats 137 plus 8.
- CNA-GAP-002: CNA::Logger serialises writes but not level changes, and a sink must not call back into the logger — minimumLevel_ is read and written without synchronisation and the sink runs under a non-recursive mutex, so changing the level while other threads log is a data race and a sink that logs deadlocks; the header states neit
- CNA-GAP-059: In a MinGW-w64 cross tree CnaTests gets a Wine CROSSCOMPILING_EMULATOR only when the default renderer is DIRECTX9/11/12 or DIRECT2D is compiled in — For any other default renderer (for example SDL_RENDERER, the toolchain file's own usage example) gtest discovery (DISCOVERY_MODE PRE_TEST) and ctest try to execute the Windows CnaTests binary directly on the Linux host.
- CNA-VGAP-001: Matrix camera builders have no XNA oracle and no full-matrix value test — CreateLookAt and the perspective and orthographic builders are asserted only on single terms and covered indirectly by consumer tests; CNA's XNA matrix oracle does not include them.
- CNA-VGAP-005: The XNA audio facade on CNA's own ALSA mixer is covered by seven facade tests plus the XACT category suite; the SoundEffect, instance, dynamic-instance, Cue, SoundBank and WaveBank suites run only on SDL3 — Under CNA_AUDIO_PLATFORM=ALSA, eight SDL3_mixer-bound suites (about 440 test definitions) are filtered out, so SoundEffect, SoundEffectInstance and DynamicSoundEffectInstance behaviour on CNA's mixer rests on seven facad
- CNA-VGAP-006: Media paths without tests: the natural song end on SDL3_mixer, the failed visualization install, MediaLibrary accessors after Dispose and SavePicture's IOException — The song-ended callback path on the default SDL3 audio, the visualization branch where no tap can be installed, MediaLibrary disposal semantics and SavePicture's write-failure exception are implemented but untested.
- CNA-VGAP-010: No CI workflow builds or tests the OPENGL4 renderer — OPENGL4, declared Supported and merged at this snapshot with several hundred family CTests, is named by none of the 20 workflows, so its evidence is CNA's recorded runs on one Radeon 780M under Mesa, and Windows and macO
- CNA-VGAP-011: No test exercises PlatformSelection.cmake's reserved-identifier and unknown-name refusal paths — PlatformSelection.cmake fails configure for SDL12, EMSCRIPTEN, WIN32 off Windows, TERMINAL on Windows and unknown names, but the only platform-selection configure test, CnaWaylandPlatformSelection, covers Wayland alone.
- CNA-VGAP-012: No CI workflow selects CNA_PLATFORM=WAYLAND, so the native Wayland backend's suites run only on a developer's machine — The Wayland ctest suites are registered only under CNA_PLATFORM=WAYLAND and launched through tools/platform/wayland_test_server.sh, but no GitHub workflow configures that platform, so the native Wayland backend has no co
- CNA-VGAP-016: A plain ctest run of the tests preset is not a trustworthy result — CNA's own tests preset records that ctest reports unbuilt display-dependent smoke executables as failed and races tests that share hard-coded /tmp fixture paths across processes, so only a direct CnaTests run is authorit
- CNA-VGAP-017: graphics-ext shader-package drift guards have holes: no reproducibility check for shadow_caster, none run in CI, desktop clustered-forward fragment unpinned — Two checked-in packages (shadow_caster and the test package modern_resource_interop) have no ShaderPackageReproducibility CTest, CI installs no shaderc so every such CTest skips, and only the ES clustered-forward fragmen
- CNA-VGAP-018: No test asserts RenderPipeline's fixed post-process order; the order-named tests only count passes — RenderPipeline::end() adds up to fourteen built-in passes and the user passes in a deliberate order, but the three tests named after that order assert only getLastFramePassCount(), so any permutation passes.
- CNA-VGAP-019: ModuleLinkClosure_* link-closure gates report CTest SKIP in every Ninja tree, so no Ninja-preset run and no CI job ever evaluates them — check_module_link_closure.py reads the make-only CMakeFiles/<target>.dir/link.txt and exits 77 when it is missing; the Ninja presets and every CI job either use Ninja or never run these tests, so CNA's module dependency
- CNA-VGAP-020: The hand-written CnaPlatformTests filter misses TerminalPresenterThroughPlatformTest and eight other platform suites, so the platform CI cells never select them — CnaPlatformTests selects suites by hand-written name tokens: TerminalPresenter.* misses TerminalPresenterThroughPlatformTest, and eight more platform suites (key codes, scancodes, filesystem, sensor session, SDL3 device
- CNA-VGAP-051: No CI configuration builds diagnostics at STATS or FULL or enables CNA_BUILD_INSPECTOR, so most Diagnostics tests and every Inspector test are never compiled in CI — CNA_DIAGNOSTICS and CNA_BUILD_INSPECTOR default to OFF and no workflow or preset changes either, so 38 of 40 DiagnosticsTests, the seven audio and graphics diagnostics tests and all 27 Inspector tests run only in local b
- CNA-VGAP-053: No CI workflow builds the C API library or runs its tests — No workflow at this snapshot configures CNA_BUILD_C_API=ON; the C API workflows only run generators, inventories, a release gate and a header compile matrix, so the library and its tests are verified only in local builds
- CNA-VGAP-056: The checked-in shader bytecode headers of VULKAN, SDL_GPU and the Direct3D renderers come from generators that no CTest or workflow runs and that have no --check mode — spirv_shaders.hpp (VULKAN, SDL_GPU), hlsl_shaders.hpp (DIRECTX11/12) and the d3d9_*shaders.hpp headers come from hand-run generators with no --check mode and no CTest or workflow, so a shader source edit without regenera
- CNA-VGAP-060: HeadlessPlatform::InjectEvent, the display-free event seam, has no caller and no test — HeadlessPlatform::InjectEvent is documented as the way to test input, runtime and lifecycle behaviour with no display, yet nothing calls it and no test covers inject-then-PollEvents delivery; the only refill test drives
Related pages
The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.
- Architecture
- Platform architecture · Graphics architecture
- Maintainer workflow
- What to test after changing X · I need to add a regression test
- Tests and validation
- Testing handbook
- Reference
- Test target index · Module index