104 lines
3.7 KiB
Python
104 lines
3.7 KiB
Python
"""Rendering tests for the waterfall, radial and braille visualisers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
|
|
from somafm_tui import config
|
|
from somafm_tui.spectrum import Frame, SpectrumEngine
|
|
from somafm_tui.widgets.spectrum import (
|
|
VISUALIZER_LABELS,
|
|
WATERFALL_HISTORY,
|
|
SpectrumPanel,
|
|
)
|
|
|
|
|
|
def make_frame(level: float = 0.7) -> Frame:
|
|
bands = tuple(0.12 + 0.8 * max(0.0, math.sin(index / 4.0)) for index in range(72))
|
|
peaks = tuple(min(1.0, value + 0.05) for value in bands)
|
|
wave = tuple(level * math.sin(point / 9.0) for point in range(192))
|
|
return Frame(bands=bands, peaks=peaks, wave=wave, level=level, status="monitor")
|
|
|
|
|
|
def make_panel() -> SpectrumPanel:
|
|
return SpectrumPanel(SpectrumEngine())
|
|
|
|
|
|
def test_every_visualizer_has_a_border_label() -> None:
|
|
assert set(VISUALIZER_LABELS) == set(config.VISUALIZERS)
|
|
|
|
|
|
def test_waterfall_records_one_column_per_tick() -> None:
|
|
panel = make_panel()
|
|
frame = make_frame()
|
|
panel._record(frame, tick=1)
|
|
panel._record(frame, tick=2)
|
|
panel._record(frame, tick=2) # a second render in the same tick
|
|
assert len(panel._history) == 2
|
|
for tick in range(3, WATERFALL_HISTORY + 20):
|
|
panel._record(frame, tick)
|
|
assert len(panel._history) == WATERFALL_HISTORY
|
|
|
|
|
|
def test_waterfall_scrolls_spectrogram_with_bass_at_the_bottom() -> None:
|
|
panel = make_panel()
|
|
# Quieter highs, louder bass, so the bottom rows must be fuller.
|
|
rising = Frame(
|
|
bands=tuple((71 - index) / 71 for index in range(72)),
|
|
peaks=tuple((71 - index) / 71 for index in range(72)),
|
|
wave=(0.0,) * 192,
|
|
level=0.5,
|
|
status="monitor",
|
|
)
|
|
for tick in range(30):
|
|
panel._record(rising, tick)
|
|
lines = panel._waterfall(40, 12, dim=False)
|
|
assert len(lines) == 12
|
|
assert {len(line.plain) for line in lines} == {40}
|
|
chars = "".join(line.plain for line in lines)
|
|
assert any(char in chars for char in "░▒▓█")
|
|
# History that does not fill the panel is padded blank on the left.
|
|
assert lines[0].plain[:9].strip() == ""
|
|
filled = [sum(1 for char in line.plain if char != " ") for line in lines]
|
|
assert filled[-1] > filled[0]
|
|
|
|
|
|
def test_radial_spectrum_draws_a_ring_inside_the_panel() -> None:
|
|
panel = make_panel()
|
|
lines = panel._radial(make_frame(), 61, 21, phase=0, dim=False)
|
|
assert len(lines) == 21
|
|
assert {len(line.plain) for line in lines} == {61}
|
|
text = "".join(line.plain for line in lines)
|
|
assert text.count("█") > 10
|
|
assert text.count("·") > 4
|
|
# The corners are outside the circle.
|
|
assert lines[2].plain[:6].strip() == ""
|
|
assert lines[2].plain[-6:].strip() == ""
|
|
|
|
|
|
def test_radial_falls_back_to_bars_when_too_small() -> None:
|
|
panel = make_panel()
|
|
lines = panel._radial(make_frame(), 12, 4, phase=0, dim=False)
|
|
assert len(lines) == 4
|
|
assert {len(line.plain) for line in lines} == {12}
|
|
assert any(char in "".join(line.plain for line in lines) for char in "█▄▅")
|
|
|
|
|
|
def test_braille_oscilloscope_uses_subcell_dots() -> None:
|
|
panel = make_panel()
|
|
lines = panel._braille(make_frame(0.9), 40, 8, phase=0, dim=False)
|
|
assert len(lines) == 8
|
|
assert {len(line.plain) for line in lines} == {40}
|
|
chars = "".join(line.plain for line in lines)
|
|
assert all(char == " " or 0x2800 <= ord(char) <= 0x28FF for char in chars)
|
|
dots = [char for char in chars if 0x2800 <= ord(char) <= 0x28FF]
|
|
assert len(dots) > 20
|
|
|
|
|
|
def test_braille_keeps_a_baseline_when_silent() -> None:
|
|
panel = make_panel()
|
|
silent = Frame((0.0,) * 72, (0.0,) * 72, (0.0,) * 192, 0.0, "idle")
|
|
lines = panel._braille(silent, 20, 6, phase=0, dim=True)
|
|
chars = "".join(line.plain for line in lines)
|
|
assert any(0x2800 <= ord(char) <= 0x28FF for char in chars)
|