90 lines
3.3 KiB
Python
90 lines
3.3 KiB
Python
"""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
|