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 Engine.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.

color_ratio

Return the fraction of pixels matching color within tolerance.

assert_color_ratio

Assert approximately expected_ratio of pixels match color.

assert_region_color

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

assert_no_color

Assert no pixel matches color.

assert_not_blank

Assert the image is not a single solid color.

save_png

Save RGBA uint8 pixels (H, W, 4) as a PNG file. Pure Python, no Pillow.

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.__all__

[‘assert_pixel’, ‘assert_color_ratio’, ‘assert_region_color’, ‘assert_no_color’, ‘assert_not_blank’,…

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.color_ratio(pixels: numpy.ndarray, color: tuple[int, ...], tolerance: int = 10) float

Return the fraction of pixels matching color within tolerance.

simvx.graphics.testing.assert_color_ratio(pixels: numpy.ndarray, color: tuple[int, ...], expected_ratio: float, tolerance: float = 0.02, color_tolerance: int = 10) None

Assert approximately expected_ratio of pixels match color.

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

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

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

Assert no pixel matches color.

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

Assert the image is not a single solid color.

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

Save RGBA uint8 pixels (H, W, 4) as a PNG file. Pure Python, no Pillow.

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

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

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

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)

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.