noaa-goes-visualization/tests/test_composite.py

91 lines
3.3 KiB
Python
Raw Permalink Normal View History

"""Tests for the composite renderer.
The blending in ``merger_FITS`` is the product's visual identity -- years of tuning
that no test can meaningfully assert the *correctness* of. What these tests protect
is that it does not change: it was lifted out of a worker loop so the bench could
render reconstructed frames, and the only thing that makes such a refactor safe is
proof the pixels came out the same.
"""
import numpy as np
import pytest
import merger_FITS as M
from conftest import solar_disc
@pytest.fixture
def six_bands():
"""Six distinct 1280x1280 bands, bright enough to exercise every blend stage."""
return [
solar_disc(peak=peak, radius=386, active_region=True)
for peak in (0.06, 0.12, 1.2, 1.6, 1.2, 3.0)
]
def test_renders_a_composite_of_the_expected_shape(six_bands):
img = M.composite_from_arrays(six_bands, 1715212800)
# 1280 trimmed by 2*64 in x and 2*100 in y, then padded by a third either side.
assert img.size == (1920, 1080)
assert img.mode == "RGB"
def test_rendering_is_deterministic(six_bands):
first = M.composite_from_arrays(six_bands, 1715212800).tobytes()
second = M.composite_from_arrays(six_bands, 1715212800).tobytes()
assert first == second
def test_the_timestamp_is_drawn_on_the_image(six_bands):
"""Two timestamps must differ only where the caption is."""
a = np.asarray(M.composite_from_arrays(six_bands, 1715212800))
b = np.asarray(M.composite_from_arrays(six_bands, 1715299200))
assert not np.array_equal(a, b)
# The caption sits in the top strip; everything below it is identical.
assert np.array_equal(a[60:], b[60:])
def test_every_band_contributes(six_bands):
"""Changing any one band must change the output.
Guards against an indexing slip in the extracted function silently dropping a
band -- the composite would still render, and still look plausible.
"""
reference = np.asarray(M.composite_from_arrays(six_bands, 1715212800))
for index in range(6):
altered = list(six_bands)
altered[index] = altered[index] * 1.5
assert not np.array_equal(
np.asarray(M.composite_from_arrays(altered, 1715212800)), reference
), f"band {index} made no difference to the composite"
def test_band_order_matters(six_bands):
"""The six arrays are positional; a caller passing them shuffled must not match."""
shuffled = [six_bands[i] for i in (5, 4, 3, 2, 1, 0)]
assert not np.array_equal(
np.asarray(M.composite_from_arrays(shuffled, 1715212800)),
np.asarray(M.composite_from_arrays(six_bands, 1715212800)),
)
def test_render_assets_are_built_once():
"""The first four colormaps are mutated in place, so building twice would
re-apply the mutation to already-mutated objects."""
first = M._render_assets()
second = M._render_assets()
assert first is second
assert first["cmaps"][0] is second["cmaps"][0]
def test_tolerates_a_filled_frame_containing_no_signal(six_bands):
"""Reconstructed frames can be blank; rendering must not raise on them."""
blanked = list(six_bands)
blanked[2] = np.zeros_like(blanked[2])
img = M.composite_from_arrays(blanked, 1715212800)
assert img.size == (1920, 1080)
def test_mapping_constants_line_up_with_the_bands():
assert len(M.VMINS) == len(M.VMAXS) == len(M.GAMMAS) == len(M.image_names) == 6