Repository navigation
Coverage tool
coverage/. A source-to-source instrumenter and a report that measure line, branch, condition and MC/DC coverage. Mojo ships no coverage tool, and the toolchain has no llvm-cov to build one on.
This is original work with no three.js lineage.
-
build_cli.mojorewrites every covered module intocoverage/build/, with a probe before each statement and around each decision. It writes a manifest of everything the probes can report. - The coverage tool and the suites are copied into
coverage/build/as well, and the suites run from there. Each probe writes one record tostderr.stdoutis unchanged. -
report_cli.mojogroups the records, matches them to the manifest, and prints the table. It exits with an error when anything is uncovered.
make coverage runs all three. See How to measure coverage.
Mojo 1.1 resolves a module beside the file being compiled before it looks at any -I path. A suite run from the repository root therefore imports the real library, whatever the search path says.
The suites used to run with -I coverage/build -I . and the first -I decided. Under 1.1 that arrangement measured nothing at all and reported a clean zero, which is the worst way for a coverage tool to fail. Copying the suites in makes the instrumented copies the ones beside them.
The copies of the tool itself are never instrumented. Measuring the tool with the tool is still not attempted.
| Module | Job |
|---|---|
scanner.mojo |
Find statements, decisions and loop headers in a source file. |
instrument.mojo |
Emit the probes. Split and and or conditions. |
runtime.mojo |
The probe functions the instrumented code calls. |
mcdc.mojo |
Reconstruct decision vectors from the ordered record stream. |
report.mojo |
Build the report and decide whether it is complete. |
build_cli.mojo, report_cli.mojo
|
The two commands the Makefile runs. |
| Metric | Question |
|---|---|
| Line | Did this statement run? |
| Branch | Did this decision go both ways? A for loop counts, including running zero times. |
| Condition | Did each and or or operand take both values? |
| MC/DC | Did each operand change the outcome on its own? |
MC/DC is the masking variant. Short-circuit evaluation makes unique-cause MC/DC unreachable for most compound decisions.
- The Makefile's
COVEREDlist is every CPU module.COVERAGE_EXCLUDEnames the GPU modules,render/gpu.mojoandrender/gpu_vxgi.mojo, because a kernel has nostderr. -
# pragma: no branchopts one decision out. Use it only when the other outcome is provably unreachable. - Probes cannot reach compile-time code, and the instrumenter recognizes
defandasync defonly. - The tool does not measure itself.
Two threads reporting one decision at once would interleave their records. The renderer therefore defaults to one worker, and the coverage run uses it.
Each statement that runs writes one record, so the capture grows with the work a suite does. A PMREM blur reads its source about half a million times, and each read ran about 150 statements. That made test_pmrem write 14 GB and run for six and a half minutes.
Keep the innermost helpers short for this reason:
- A small value type uses
@fieldwise_init. Its constructor then has no body and writes no record.FloatColorandVector3do this. - A helper that runs per texel or per tap is one statement where the code stays clear.
Vector3.crossand theCLAMPcase ofwrap_indexare. - Work that is the same for every texel is done once, outside the loop. The PMREM blur finds its weights, sines, cosines and copy position once per pass.
See Benchmarks for the numbers.
A Bool loop flag read after nested loops hangs the Mojo compiler. The instrumenter emits an Int counter instead. See The Mojo compiler hang.
The Makefile reduces each suite's stderr as it arrives, and compresses what is left. The report reads two things: the set of distinct probe payloads, and each decision's distinct MC/DC evaluations. A probe in a loop repeats both millions of times. tools/coverage_io.py's Reducer keeps each payload once, as a COVLINE: record, and each distinct evaluation once, rebuilt from its pending conditions when its decision closes. The report then sees the same payloads and the same evaluations, in the same order of first closing.
test_rasterizer writes 9.9 million records. The reduced capture holds 3,477 lines, and the report reads it in 0.02 s instead of 8 s, with an identical result. The reduction runs on the capture runners, in parallel, and the report reads the captures on one core.
The reporter reads the captures through named pipes, one suite at a time. Other output from a suite passes through unchanged, for the summary of a failed suite.
Tutorials
How-to
Reference
- Scene graph
- Rotations
- Cameras
- Geometry
- Geometry addons
- Lofts and clipping groups
- Sculptor
- Procedural generators
- Meshes and assets
- Lines
- Points and sprites
- Helpers
- Scene objects
- Animated objects
- Raycasting
- Curves and paths
- Animation
- Skinning
- Materials
- Node materials
- TSL functions
- Lights
- Lighting addons
- Fog
- Textures
- Renderer
- Renderer hooks and material flags
- Rasterization
- GPU backend
- Render target
- Post-processing
- GPU computation
- Compute nodes
- Progressive light map
- Gaussian splats
- Voxel global illumination
- SVG renderer
- Image files
- Image decode queue
- Model files
- More model files
- Exporters
- Scene JSON
- Windowing and controls
- Math
- Norm consumers
- Math addons
- Units
- Coverage tool
- Commands
- Examples
- Benchmarks
- Extensions
- Shared physics
- Physics query snapshots
- Continuous collision
- Tick force restoration
- Water
- Streamline
- Animals
- Buildings
- CARLA
- Femur
- Tibia
- Fibula
- Patella
- Knee
- Muscles
- Vessels
- Lymph
- Nerves
- Integument
- Leg
- Foot
- Pelvis
- Torso
- Arm
- Hand
- Head
- Genome
- Game humanoid
- Humanoid fidelity
- Audio-aligned game faces
- Converted assets
- Mesh quality
- Segment inertia
Explanation
- Software rasterizer
- Scene graph as an array
- Linear color
- Normals are not colors
- Perspective correction
- Fixed-point coverage
- Per-fragment shading
- Mipmaps
- Sorted transparency
- Types and checks
- Render hooks as a trait
- Shared CPU and GPU code
- Bands and threads
- PNG structure
- Extensions beside the port
- CUDA teardown hang
- Mojo compiler hang