The C++ engine is exercised by Catch (v1) tests in test/. The
Makefile builds a unit-test binary that links game objects from
obj/src/ with obj/test/*.o (see Makefile targets build-test and
run-tests).
make run-testsEquivalent: make build-test && ./unit-test
make run-coverageGenerates an HTML report at coverage/index.html. The CI coverage
job uploads this as a downloadable artifact on every push. The published
report is also available at
srobbibaro.github.io/krig-game-engine/coverage/
after each merge to master.
lcov compatibility warnings — the run-coverage target passes
--ignore-errors flags to suppress warnings from lcov 2.4's stricter
validation of LLVM gcov's emulated output format. These are a known
toolchain compatibility shim, not indicators of bad data. Line coverage
numbers for our source files are trustworthy. The bulk of the noise
comes from Catch v1's single-header inline functions; upgrading to
Catch2/3 would reduce it, but the underlying lcov 2.4 / LLVM gcov gap
would remain.
Meaningful files — these are directly exercised by the unit tests:
Vector, Matrix, Quaternion, Sphere, Frustum, Plane,
GameLevel, Camera, GameTimer, and their API bindings.
Expected low coverage — Engine.cpp, ModelGameObject.cpp,
Terrain.cpp, audio, and GLUT callbacks require a live display and
audio device. Low numbers in these files are expected, not a gap in test
coverage.
- Regression guard for math, spatial structures, list behavior, and plain C++ game-object state.
- Documentation by example for APIs that are easy to misread (see below).
It is not a full integration harness: there is no automated GL context, OpenAL device, or end-to-end game loop.
Constructing GameLevel can trigger Music / OpenAL buffer
setup; in headless or CI environments you may see AL errors in the
log even when all assertions pass. That is a known limitation of
running tests against the full object graph.
These points come from Engine construction/teardown and from running
tests that link the full engine:
Engine::shutdown()only setsisRunning_to false (Engine.cpp). It does not close Lua, OpenAL, or delete levels. Full cleanup happens in~Engine()viaunloadGame()/unload()(AL context/device,delete currentLevel_, etc.).- Constructor initializes
GameTimer; iftimer_.init()fails, the constructor logs andexit(1)— there is no recoverable error path for embedders or unit tests on that path. getKeyState()andgetSpecialKeyState()always return addresses ofEnginemember structs. They stay valid for the lifetime of theEngineinstance (including aftershutdown()), until the object is destroyed.- Methods such as
initGL,initAL,loadGame,gameCycle, andrenderTextassume a real GLUT/GL and audio environment; the currenttest/engine.cppscenarios intentionally avoid them.
Some assertions match the current engine, not necessarily what
readers assume. The main ones are spelled out in
docs/MATH_AND_TESTING_CONVENTIONS.md (matrix layout, rotations,
quadtree sphere Z, quaternion slerp, etc.). Additional game-object /
lifecycle quirks:
Object::statevsObject::active—setState(DEAD)(and related) updates thestate_field only. It does not clearactive_. You can beDEADand stillgetActive() == trueuntilsetActive(false)is used. Seetest/object.cpp.- Two
setStateoverloads —setState(const unsigned char&)andsetState(const int&)both writestate_. Callers should treat them as the same destination with different argument types (legacy API surface). Object::animate— explicit base call in tests — concrete subclasses overrideanimate(float, Object*), which has a different signature from the baseanimate(const float&, Object*). Virtual dispatch will resolve to the subclass (empty no-op inBasicObject). To test the base-class frame-loop logic (velocity accumulation, rotation velocity, scale rate), callo.Object::animate(dt, nullptr)explicitly.GameLevelminimal buffer load —loadLevelFromBuffer("test=1")is a harness-friendly path that depends on engine bootstrap behavior;getObjectFromId(0)is the camera aftersetCamera, not a general guarantee for arbitrary buffers.- Lua
krig.vector—scalaranddot_productwrap the same underlying vector math in the C API; overlapping behavior is intentional for script compatibility, not a bug to "fix" in tests alone.
For math-specific surprises, always check
MATH_AND_TESTING_CONVENTIONS.md first.
Prefer tests that need only stack allocation and existing public APIs:
- In scope:
Vector,Matrix,Quaternion,Plane,Sphere,Frustum,QuadTree/QuadTreeNode,DisplayList,ObjectList,Object/Camerastate setters,GameLevelmetadata accessors,KeyState,Enginesmoke checks. - Use care: Anything that opens Lua scripts (
Object::setScript,loadScript, level load with real script paths), OpenGL (gl*inprepareGLView, terrain draw), orsetParticleSystem(allocates particle implementations) unless you accept heavier coupling. - Lua API surface:
test/api.cppcovers thekrig.vectorbindings; expanding otherapi_*tests is optional.
Do not require new fixtures (terrain files, golden assets) or engine refactors unless the task explicitly allows it.
| File | Focus |
|---|---|
catch.cpp |
Catch main |
vector.cpp, matrix.cpp, quaternion.cpp, plane.cpp |
Core math |
sphere.cpp, frustum.cpp |
Collision / view primitives |
object.cpp |
Object construction and state |
objectlist.cpp, displaylist.cpp |
Lists |
quadtree.cpp |
QuadTree::buildTree, buildLeafList, node defaults |
camera.cpp |
Camera without GL (e.g. setCamera, update(0), getRotationMatrix) |
gamelevel.cpp |
Level load failures/success, metadata, light, ids |
engine.cpp |
Engine running flag, shutdown, key-state pointers |
keystate.cpp |
Key state tracking |
gametimer.cpp |
Frame timing |
api.cpp |
Lua vector API |
docs/MATH_AND_TESTING_CONVENTIONS.md— Matrix layout, rotation conventions, quadtree XZ / sphere Z sign, quaternionslerpguard, matrix multiply composition.README.md— Build and one-line test invocation.
When you add behavior that is surprising but measurable, add a
short test and a sentence in MATH_AND_TESTING_CONVENTIONS.md
(for math/spatial) or here (for process/coverage).