simvx.graphics.testing

Visual testing utilities: pixel assertions for headless Vulkan rendering.

All functions operate on numpy arrays (H, W, 4) uint8 in RGBA order, as returned by Renderer.capture_frame(). No external dependencies beyond numpy.

Usage::

from simvx.graphics.testing import assert_pixel, assert_not_blank
pixels = app.run_headless(scene, frames=5, capture_frames=[4])[0]
assert_not_blank(pixels)
assert_pixel(pixels, 160, 120, (255, 0, 0, 255), tolerance=10)

Module Contents

Classes

ScreenshotReport

Collects screenshots during a test run and produces CI-friendly output.

VisualRegression

Manages baseline screenshots and compares rendered frames against them.

Functions

assert_pixel

Assert the pixel at (x, y) matches expected_rgba within tolerance per channel.

colour_ratio

Return the fraction of pixels matching colour within tolerance.

assert_colour_ratio

Assert approximately expected_ratio of pixels match colour.

assert_region_colour

Assert all pixels in rect (x, y, w, h) match expected_colour.

assert_no_colour

Assert no pixel matches colour.

assert_not_blank

Assert the image is not a single solid colour.

save_image

Save RGBA pixels to a file, format taken from the extension (.png or .ppm).

save_diff_image

Save a visual diff image highlighting differences (5x amplified).

Data

API

simvx.graphics.testing.log

‘getLogger(…)’

simvx.graphics.testing.__all__

[‘assert_pixel’, ‘assert_colour_ratio’, ‘assert_region_colour’, ‘assert_no_colour’, ‘assert_not_blan…

simvx.graphics.testing.assert_pixel(pixels: numpy.ndarray, x: int, y: int, expected_rgba: tuple[int, ...], tolerance: int = 2) None

Assert the pixel at (x, y) matches expected_rgba within tolerance per channel.

simvx.graphics.testing.colour_ratio(pixels: numpy.ndarray, colour: tuple[int, ...], tolerance: int = 10) float

Return the fraction of pixels matching colour within tolerance.

simvx.graphics.testing.assert_colour_ratio(pixels: numpy.ndarray, colour: tuple[int, ...], expected_ratio: float, tolerance: float = 0.02, colour_tolerance: int = 10) None

Assert approximately expected_ratio of pixels match colour.

simvx.graphics.testing.assert_region_colour(pixels: numpy.ndarray, rect: tuple[int, int, int, int], expected_colour: tuple[int, ...], tolerance: int = 5) None

Assert all pixels in rect (x, y, w, h) match expected_colour.

simvx.graphics.testing.assert_no_colour(pixels: numpy.ndarray, colour: tuple[int, ...], tolerance: int = 5) None

Assert no pixel matches colour.

simvx.graphics.testing.assert_not_blank(pixels: numpy.ndarray) None

Assert the image is not a single solid colour.

simvx.graphics.testing.save_image(pixels: numpy.ndarray, path: str | pathlib.Path) None

Save RGBA pixels to a file, format taken from the extension (.png or .ppm).

Data first, destination second, as in :func:~simvx.graphics.assets.image_loader.save_png and every other writer in the engine.

simvx.graphics.testing.save_diff_image(actual: numpy.ndarray, expected: numpy.ndarray, path: str | pathlib.Path) None

Save a visual diff image highlighting differences (5x amplified).

Data first, destination last, like :func:save_image.

class simvx.graphics.testing.ScreenshotReport(output_dir: pathlib.Path)

Collects screenshots during a test run and produces CI-friendly output.

Initialization

capture(pixels: numpy.ndarray, label: str) pathlib.Path

Save frame as PNG, return path.

capture_with_diff(actual: numpy.ndarray, expected: numpy.ndarray, label: str) pathlib.Path

Save actual + expected + diff as side-by-side composite PNG.

add_description(text: str) None

Attach a text description (e.g., from scene_describe()).

finalize(test_name: str = '', status: str = 'passed') pathlib.Path

Write report.json and report.html to output_dir. Return path to JSON.

class simvx.graphics.testing.VisualRegression(baseline_dir: pathlib.Path, *, require_baseline: bool = False)

Manages baseline screenshots and compares rendered frames against them.

On first run (no baseline exists), saves the current frame as the baseline and passes. On subsequent runs, compares against baseline and fails if pixels differ beyond threshold. On failure, saves actual frame and diff image alongside baseline for inspection.

Usage::

def test_red_cube(capture, regression):
    frames = capture(RedCubeScene(), frames=3, capture_frames=[2])
    regression.assert_matches(frames[0], "red_cube")

Initialization

assert_matches(pixels: numpy.ndarray, name: str, threshold: float = 0.001) None

Compare pixels against stored baseline.

update_baseline(pixels: numpy.ndarray, name: str) None

Force-overwrite baseline (for intentional visual changes).

static pixel_diff_ratio(a: numpy.ndarray, b: numpy.ndarray, tolerance: int = 2) float

Fraction of pixels that differ beyond tolerance per channel.