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 file. Auto-detects format from extension (.png or .ppm).

save_diff_image

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

Data

API

simvx.graphics.testing.log[source]

‘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[source]

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[source]

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[source]

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[source]

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[source]

Assert no pixel matches colour.

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

Assert the image is not a single solid colour.

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

Save RGBA pixels to file. Auto-detects format from extension (.png or .ppm).

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

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

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

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

Initialization

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

Save frame as PNG, return path.

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

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

add_description(text: str) None[source]

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

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

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

class simvx.graphics.testing.VisualRegression(baseline_dir: pathlib.Path)[source]

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[source]

Compare pixels against stored baseline.

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

Force-overwrite baseline (for intentional visual changes).

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

Fraction of pixels that differ beyond tolerance per channel.