First 10, 50 and 100 hours with CNA
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. Presets, targets and test names were checked against CMakePresets.json, cmake/UnitTests.cmake and the test sources at the snapshot; none of the commands was executed for this page.
The hour labels name stages of competence, not promises about how long CNA takes to learn. Read implementation beside tests and CMake. At each stage, produce a small diagram or trace yourself; if you cannot explain a relationship without the page open, repeat the route before editing that area. Every file, symbol, preset and test named below exists at snapshot 009d40f5.
Before the first command. The presets and CTest filters on this page were checked against CMakePresets.json and cmake/UnitTests.cmake at this snapshot; they were not executed for this page. They need a CNA checkout of the next branch with its sibling repositories and submodules (Building: sibling repositories, submodules). The unit preset selects the STUB renderer but keeps the default SDL3 platform, so the SDL submodules are still needed, and window-creating tests need a display (Running the tests). There are configure and build presets but no test presets, so CTest is driven with --test-dir.
Stage 1: first 10 hours — find the running system
- Capture the exact tree and configuration. Read
CMakeLists.txt,modules/CMakeLists.txtandCMakePresets.jsonin that order. Identify the physical module helper (cna_add_module), theCNAumbrella target, the difference between configure presets (17 visible plus the hiddenbase-ninja) and build presets (which only pick targets), and the platform, audio and renderer selectors (cmake/PlatformSelection.cmake,cmake/AudioPlatformSelection.cmake,cmake/RendererSelection.cmake). Exercise: explain whyunitcompilesSTUBbut does not validate Vulkan presentation. Then configure, build and list:cmake --preset unit # STUB, Debug, CNA_BUILD_TESTS=ON; examples, C API, net, video, Draco OFF cmake --build --preset unit # builds the aggregate CnaTests into cmake-build-unit/ ctest --test-dir cmake-build-unit -N # this configuration's CTest inventoryCnaTestscases are registered bygtest_discover_tests(... DISCOVERY_MODE PRE_TEST), so the list is produced from the built binary; build before listing. - Trace construction before the loop. Read
demo_2d Main.cppfor the caller,Game.hppfor member declaration order (platform_first, thenplatformInstallation_, …,GraphicsDevice_,Content_,Window_) andGame.cppfor the constructor,InstallPlatformandDoInitialize. Exercise: draw which objects already exist when the derivedInitializebegins, and explain whyInstallPlatformcannot move into the constructor body (every member afterplatform_may reach the ambient platform while it is constructed; the body runs after all of them). CheckGamePlatformOwnershipTests.cpp— especiallyExplicitPlatformIsOwnedAndInstalledBeforeGameMembersandAFailedConstructionLeavesNothingInstalled— andGameTest.RunExecutesLifecycleInDocumentedOrderinGameTests.cpp. - Trace one frame and one failure. Follow
Game::Run→RunLoop→TickinGame.cpp, then the one-frame trace. Mark where platform events enter (Game::PollEvents), where fixed-step updates can repeat (thewhileoveraccumulatedElapsedTime_), and where draw and present can be suppressed (suppressDraw_,BeginDraw()returning false). Run the focused CTest filter in the configured build:
Exercise: explain why a passing lifecycle test cannot prove a real window appeared. (Read the test: it asserts call counts — onectest --test-dir cmake-build-unit -R 'GameTest\.RunExecutesLifecycleInDocumentedOrder' --output-on-failure ctest --test-dir cmake-build-unit -R 'GamePlatformOwnershipTest\.' --output-on-failureInitialize, oneLoadContent, at least oneUpdateandDraw— after probing for a hidden 64×64 window, and it callsGTEST_SKIPwhen the selected platform cannot create one, so on a host without a display GoogleTest reports it as skipped — current CMake versions pass that through to CTest as a skip, older ones may count the case as passed.) - Separate the three selected backends. Read the build selectors, renderer descriptor and registry selection and the platform contract. Exercise: for
SDL3platform +VULKANrenderer +NULLaudio, list which component owns events, GPU commands and audio silence. Then explain whyTERMINAL+VULKANis rejected at configure time (the_cna_terminal_rendererscheck incmake/RendererSelection.cmakeallows onlySOFTWARE,PORTABLEGL,HEADLESSandSTUB, and runs before any renderer dependency probe).
Stage outcome. The stage is complete when you can explain startup, one frame and ordinary exit on a whiteboard, with the selected platform and renderer named separately. Do not memorize every API.
Stage 2: first 50 hours — recover ownership and variants
- Draw the lifetime graph. Study the ownership master map, then Game and GraphicsDevice destruction and resource registration. In
GraphicsDevice.cpp, inspectGraphicsDevice::Dispose(bool),destroyNativeResourcesandAddResourceReference; inGraphicsResource.cpp, the resource side. Exercise: predict what happens when aTexture2DsurvivesGamedestruction; locate the weak lifetime token that makes late cleanup safe (resourceDeviceLifetime_in the device,graphicsDeviceLifetime_in the resource). Explain why resource disposal precedes the renderer reset inDispose(bool). - Compare implementations of one contract. For the platform, compare SDL3 with Win32 or X11, and contrast Headless. For graphics, compare Vulkan command recording with EasyGL context-bound drawing or SDL_gpu queued passes. Exercise: trace the same resize or draw responsibility on two backends and write down what is contract-level and what is native. Do not infer parity merely because both compile.
- Trace non-render paths. Read events versus snapshots, the mixer and callbacks, the build-time asset pipeline and runtime load and cache. Exercise: draw a source texture → processed asset →
ContentManagerobject path and mark which process owns each stage. Build the focused content tests after theunitconfigure and list what they contain:cmake --build --preset unit-content # builds CnaContentTests only ./cmake-build-unit/CnaContentTests --gtest_list_tests # run from the repository rootCnaContentTestsis a focused iteration executable: it is not registered with CTest, soctestwill not run it and actest -Rfilter after building only this target finds nothing to execute. Inspect the real test names before choosing a filter, and run it from the repository root, which is the working directory CTest gives theCnaTestscases. - Learn how tests are assembled. Read
cmake/UnitTests.cmakebeside the test architecture. Exercise: for one test source, locate its object group (cna_<group>_test_objects, where the group is the owning module directory,renderers, orintegrationfor root tests), its focused executable (theCNA_TEST_FOCUSED_TARGET_<group>table:CnaRuntimeTests,CnaGraphicsTests,CnaPlatformModuleTests, …), the aggregateCnaTestsregistration, its working directory and the CI cell that runs it. Note that no workflow at this snapshot configures theunitpreset; the general CI job configuresOPENGLES3with tests and examples on under Xvfb (what CI covers). Explain why a CTest skip (return code 77) or aGTEST_SKIPand a green result are not equivalent evidence.
Changed recently in UnitTests.cmake. The discovered CnaTests cases now carry an output gate that joins the Vulkan validation pattern with an OpenGL4 GL-error pattern (cna_append_vulkan_validation_gate_pattern, cna_append_opengl4_gl_error_gate_pattern), so a case can fail on its log output even when its assertions pass; and the EasyGL test-suite gate lists only the five EasyGL identities, because OPENGL4 is its own family. A pure-CMake test, CnaSdlPrebuiltFingerprint, checks that the persistent vendored-SDL install is rebuilt when its inputs change.
Stage outcome. At this stage you should be able to identify the owner of an ordinary bug and tell whether the fix changes a common contract, a selected backend, or a serialized/ABI boundary.
Stage 3: first 100 hours — author and review changes
- Perform an independent investigation. Take one narrow issue and use the twelve source checks. Write the call path, ownership path, mutation points, test mapping and selected build axes before modifying source. Read the test's implementation, not just its name.
- Make one local patch, then one cross-cutting review. The worked changes rehearse a component lifetime regression, shared render-target behaviour, a Vulkan present/resize path, a platform event change and public API propagation. First add or adjust a failing test in the owning module; then make the smallest change. For graphics, run a second, independent backend or state explicitly why it could not be run. For public API, decide explicitly whether the C ABI needs a new route, and inspect binding ABI/version gates before claiming language support.
- Exercise failure modes. Reproduce constructor failure, explicit disposal, the normal destructor, a resize or second frame, or callback teardown as appropriate. Run the change→test matrix, then inspect
ctest -Nand the skipped cases for the actual configuration. Where native-host validation is unavailable, record the gap rather than silently upgrading compile evidence to runtime evidence. - Review without an AI summary. From the diff alone, explain every changed owner, every possible invalidation of a borrowed pointer, each backend-capability assumption, each test assertion and every changed build configuration. If you cannot reproduce the author's trace from source, the patch is not yet reviewable.
Stage outcome. "100 hours" is not a credential. Readiness is demonstrated by an evidence-backed patch and an explanation of what remains unproven, not by time spent. Keep a local notebook of verified call paths. When a discovery is reusable, turn it into a correction to the Development pages at a new source revision with its links re-checked, following the maintenance & pin policy.
Related pages
The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.
- User guide
- Building: CMake presets · Building: running the tests
- Architecture
- Runtime lifecycle · CMake architecture
- Maintainer workflow
- How to understand code you did not write · Add a regression test
- Tests and validation
- Test architecture · What to test after changing X
- Reference
- Test target index · CMake option index