Skip to content

Property-Based Tests

Property-based tests verify that a function satisfies a mathematical invariant across many randomly generated inputs, rather than a single fixed example. This catches edge cases that hand-written tests miss.

This project uses Hypothesis for property-based testing.

When to Use Property-Based Tests

Property-based tests are well suited for:

  • Mathematical invariants in color math (roundtrips, monotonicity, range constraints)
  • Shape contracts that must hold for any valid input size
  • Functions where the correct output can be described as a rule rather than a specific value

They are not suited for:

  • Tests that require a specific expected output value
  • Tests that depend on model weights or hardware

Installing Hypothesis

Hypothesis is already in the dev dependencies for corridorkey-core. Run uv sync --all-groups to install it if you haven't already.

[dependency-groups]
dev = [
  "pytest>=9.0.2",
  "pytest-cov>=7.0.0",
  "hypothesis>=6.151.9",
]

Examples

Color space roundtrip

The sRGB transfer function must be its own inverse. For any value in [0, 1], converting to linear and back must return the original value.

from hypothesis import given, settings
from hypothesis import strategies as st
import numpy as np
from corridorkey_core.compositing import linear_to_srgb, srgb_to_linear

@given(st.floats(min_value=0.0, max_value=1.0, allow_nan=False))
def test_srgb_linear_roundtrip(value: float):
    x = np.array([value], dtype=np.float32)
    assert np.allclose(srgb_to_linear(linear_to_srgb(x)), x, atol=1e-5)

Output range constraint

linear_to_srgb must always return values in [0, 1] for any non-negative input.

@given(st.floats(min_value=0.0, max_value=1.0, allow_nan=False))
def test_linear_to_srgb_output_range(value: float):
    x = np.array([value], dtype=np.float32)
    result = linear_to_srgb(x)
    assert 0.0 <= result[0] <= 1.0

Compositing identity

Compositing a foreground over any background with full alpha must return the foreground exactly.

@given(
    st.floats(min_value=0.0, max_value=1.0),
    st.floats(min_value=0.0, max_value=1.0),
)
def test_composite_full_alpha_identity(fg_val: float, bg_val: float):
    fg = np.full((4, 4, 3), fg_val, dtype=np.float32)
    bg = np.full((4, 4, 3), bg_val, dtype=np.float32)
    alpha = np.ones((4, 4, 1), dtype=np.float32)
    result = composite_straight(fg, bg, alpha)
    assert np.allclose(result, fg, atol=1e-6)

Suppressing Hypothesis Output in CI

Hypothesis prints a summary when it finds a failing example. This is useful locally but noisy in CI. Add a settings profile to conftest.py if needed:

from hypothesis import settings
settings.register_profile("ci", max_examples=50)
settings.load_profile("ci")