Testing
Lambent’s tests run inside a real game client, because what they check is what ends up on screen.
Every jar includes the self-test, but it does nothing unless the game is started with
-Dlambent.selftest=true.
Running it
gradlew :1.20.1:forge:runClient -Plambent.selftest
gradlew :1.21.1:neoforge:runClient -Plambent.selftest
gradlew :26.3:neoforge:runClient -Plambent.selftest -Plambent.only=26.3
The client starts, creates a flat test world at night, runs every case, writes a report and closes itself. A full run takes a few minutes.
- Report:
versions/<mc>/<loader>/run/client/lambent-selftest.txt - Screenshots of each case:
versions/<mc>/<loader>/run/client/lambent-selftest/ - Log: every line has the
[Lambent self-test]prefix.
What it checks
The test world is a flat floor with a stone pillar in front of the camera. Each case lights the scene, waits for the frame to settle, reads back the screen, and compares the brightness of chosen spots against the same spots with no lights.
| Cases | What passes |
|---|---|
baseline |
Lights are not broken, and the frame time with nothing lit. |
pair, point, glow, lens |
Each kind of light brightens the pixels it should. |
spot-shadow, point-shadow |
The floor in the light comes up. The floor behind the pillar stays dark. |
limits-* |
Each kind takes exactly its room per frame (Lights.MAX_*) and refuses the next one. |
fade |
frame.fade eases a light in without ever dimming it. |
failing-source |
A source that throws is switched off, and the other sources keep lighting. |
clear-cache |
After the resource reload path (LightRenderer.clearCache), shadows build again. |
fabulous-* |
The baseline and both shadow cases again under Fabulous graphics. |
fancy-again |
Back to Fancy, nothing left broken by the switch. |
perf |
Frame time with one of every kind lit at once. |
after-lights |
AfterLights runs every frame, always with a camera. |
Every case is followed by a -healthy check: the light pass is still on, and none of the test’s own
sources has thrown.
The thresholds are relative to the unlit frame, so they hold on any GPU and at any window size. The shadow cases need the floor behind the pillar to stay below a fraction of what the lit floor gets.
Last results
Run on an RTX 2060 at 854×480. The times are whole frames (the time from one frame to the next, in the small test world), so the difference between the two columns is roughly what the lights cost.
| Version | Result | Frame, nothing lit | Frame, every kind lit |
|---|---|---|---|
| 1.20.1 Forge | 43/43 | 0.49 ms | 0.66 ms |
| 1.21.1 NeoForge | 43/43 | 0.51 ms | 0.67 ms |
| 26.1.2 NeoForge | 43/43 | 0.66 ms | 0.92 ms |
| 26.2 NeoForge | 43/43 | 0.78 ms | 1.45 ms |
| 26.3 NeoForge | 43/43 | 0.55 ms | 0.84 ms |
Timings vary by a few tenths of a millisecond from run to run.
When a case fails
- Look at that case’s screenshot first. A missing light, a light in the wrong place and a missing shadow each look different.
- A
-healthyfailure means a stack trace is in the log. Search forfailed and is off. - The
failing-sourcecase logs anIllegalStateException: thrown on purpose by the self-test. That error is expected.