When a new test file is added to the project (e.g., tests/fl/fixed_point/s16x16.cpp), test.py fails with exit code 1 on the first run and on several subsequent retries. The failure occurs silently after "Preparing build (quick mode)..." with no error message. The test compiles and passes when built/run manually outside of test.py.
Three bugs were identified and fixed:
File: ci/meson/test_discovery.py
The function used meson introspect --targets and filtered for type == "executable", but test targets are shared library type in the DLL-based test architecture. This meant fuzzy candidate resolution never found any test targets.
Fix: Changed to use meson introspect --tests which returns registered test names directly, regardless of the underlying target type.
File: ci/meson/runner.py
When running a specific test, the runner only looked for {test_name}.exe. In the DLL-based test architecture, tests are run as runner.exe <test.dll>. When the specific test's runner copy (.exe) hadn't been compiled yet, the runner reported "test executable not found."
Fix: Added fallback logic: if {test_name}.exe doesn't exist, look for the shared runner.exe + {test_name}.dll combination.
File: tests/test_metadata_cache.py
compute_test_files_hash() used glob("*.cpp") (non-recursive) for subdirectories, so files in nested directories like fl/fixed_point/s16x16.cpp were never included in the hash. Also hardcoded ["fl", "ftl", "fx"] instead of using TEST_SUBDIRS from test_config.py.
Fix: Changed to rglob("*.cpp") and imported TEST_SUBDIRS from test_config.py to stay in sync with test discovery configuration.
File: tests/test_config.py
Added "fl/fixed_point" to TEST_SUBDIRS so discover_tests.py finds tests in the new subdirectory.
The test infrastructure has a multi-layer caching system:
-
Source hash layer (
build_config.py):get_source_files_hash()tracks all source/test files viarglob. Detects adds/removes and triggersmeson setup --reconfigure. -
Test metadata cache (
test_metadata_cache.py): Caches the output oforganize_tests.pyusing a hash of test file metadata (path + mtime + size). Used bytests/meson.buildto avoid re-running test discovery on every reconfigure. -
Test list cache (
build_config.py):test_list_cache.txttracks the list of discovered test files for detecting test adds/removes. -
Meson target resolution (
test_discovery.py,runner.py): Maps user-provided test names to meson build targets and executables.
The original failure chain was:
- User adds
tests/fl/fixed_point/s16x16.cpp - Source hash detects change → triggers reconfigure
- During reconfigure,
test_metadata_cache.py --checkrecomputes hash butglob()misses the nested file → stale cache returned - Even after cache issues resolved,
get_fuzzy_test_candidatesfilters fortype == "executable"→ never finds theshared librarytest target - Runner tries
test_s16x16ands16x16as target names → neither exists (actual name isfl_fixed_point_s16x16) - Even if compilation succeeds via fuzzy match, runner looks for
.exefile that doesn't exist yet
Test discovery relied on a manually-maintained TEST_SUBDIRS list in tests/test_config.py with 12 leaf-directory entries. Every new test subdirectory required updating this list, and forgetting to do so silently hid tests.
Replaced TEST_SUBDIRS (inclusion list) with EXCLUDED_TEST_DIRS (exclusion list) + automatic recursive discovery.
Before: discover_tests.py used glob("*.cpp") per explicitly-listed subdirectory.
After: discover_tests.py uses a single rglob("*.cpp") over the entire tests/ tree, filtering out:
- Files in
EXCLUDED_TEST_FILES(doctest_main.cpp,test_runner.cpp) - Directories in
EXCLUDED_TEST_DIRS(shared,testing,data,manual,build,builddir,x64,bin,example_compile_direct,fastled_js) - Hidden directories (names starting with
.— covers.build-*,__pycache__) - MSVC artifact dirs (names ending with
.dir)
This immediately found 10 previously-invisible tests in nested subdirectories that were never listed in TEST_SUBDIRS:
fl/chipsets/clockless_block_generic.cpp,fl/chipsets/lcd50.cppfl/codec/gif.cpp,fl/codec/jpeg.cpp,fl/codec/mp3.cpp,fl/codec/mpeg1.cpp,fl/codec/pixel.cpp,fl/codec/vorbis.cppfx/video/frame_tracker.cppplatforms/esp/32/drivers/rmt/rmt_5/rmt5_nibble_lut.cpp
Adding tests/any/nested/path/new_test.cpp now Just Works with zero config changes.
- Platform: Windows (MSYS_NT-10.0-19045)
- Meson: 1.10.0
- Compiler: clang 21.1.5 via clang-tool-chain-sccache
- Build mode: quick