test(visualizer): cover the new modes and the full v cycle
This commit is contained in:
1 parent
6d188c00bc
commit
a42678ef30
2 files changed
+106
-2
No files matched your search
+3
-2
@@ -6,6 +6,7 @@ from datetime import datetime, timedelta, timezone
|
||||
|
||||
import pytest
|
||||
|
||||
from somafm_tui import config
|
||||
from somafm_tui.api import Song
|
||||
from somafm_tui.app import SomaFMApp
|
||||
from somafm_tui.widgets.help import HelpScreen
|
||||
@@ -70,11 +71,11 @@ async def test_boot_navigation_filter_favourite_visualizer_help() -> None:
|
||||
assert app.index == 0
|
||||
|
||||
modes = set()
|
||||
for _ in range(3):
|
||||
for _ in range(len(config.VISUALIZERS)):
|
||||
await pilot.press("v")
|
||||
await pilot.pause()
|
||||
modes.add(app.visualizer)
|
||||
assert modes == {"both", "bars", "wave"}
|
||||
assert modes == set(config.VISUALIZERS)
|
||||
|
||||
await pilot.press("question_mark")
|
||||
await pilot.pause()
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
"""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)
|
||||
Reference in new issue
Block a user