Web frontend
This commit is contained in:
128
python-backend/tests/test_startup.py
Normal file
128
python-backend/tests/test_startup.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""What the app does when the config leaves the hardware out.
|
||||
|
||||
Both of these are deliberately warnings rather than errors: a spare machine running
|
||||
only the web front-end is a supported way to use this, and it should say so once at
|
||||
startup rather than fail or - worse - look like it is working when it is not.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from musicmouse.__main__ import _build_player, wants_hardware
|
||||
from musicmouse.bus import EventBus
|
||||
from musicmouse.clock import RealClock
|
||||
from musicmouse.config import SIMULATE, Config, ConfigError, load_config
|
||||
from musicmouse.devices.null_transport import NullTransport
|
||||
from musicmouse.simulator.fake_player import FakePlayer
|
||||
from tests.conftest import VALID_CONFIG, write_config
|
||||
|
||||
|
||||
def _config(config_dir: Path, *, drop: tuple[str, ...] = (), **general: Any) -> Config:
|
||||
data = copy.deepcopy(VALID_CONFIG)
|
||||
data["general"].update(general)
|
||||
for key in drop:
|
||||
data["general"].pop(key, None)
|
||||
return load_config(write_config(config_dir, data))
|
||||
|
||||
|
||||
# ------------------------------------------------------------------- serial port
|
||||
|
||||
|
||||
def test_a_missing_serial_port_is_an_error(config_dir: Path) -> None:
|
||||
"""Simulation has to be asked for. A config that lost the line must not boot."""
|
||||
with pytest.raises(ConfigError) as excinfo:
|
||||
_config(config_dir, drop=("serial_port",))
|
||||
assert "general.serial_port" in str(excinfo.value)
|
||||
assert "required" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_a_missing_alsa_device_is_an_error(config_dir: Path) -> None:
|
||||
with pytest.raises(ConfigError) as excinfo:
|
||||
_config(config_dir, drop=("alsa_device",))
|
||||
assert "general.alsa_device" in str(excinfo.value)
|
||||
assert "required" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_both_missing_are_reported_together(config_dir: Path) -> None:
|
||||
with pytest.raises(ConfigError) as excinfo:
|
||||
_config(config_dir, drop=("serial_port", "alsa_device"))
|
||||
message = str(excinfo.value)
|
||||
assert "2 problems" in message
|
||||
assert "general.serial_port" in message
|
||||
assert "general.alsa_device" in message
|
||||
|
||||
|
||||
def test_simulate_switches_off_the_serial_link(
|
||||
config_dir: Path, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
config = _config(config_dir, serial_port=SIMULATE)
|
||||
assert config.general.serial_simulated is True
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
assert wants_hardware(config, False) is False
|
||||
assert "running without the mouse" in caplog.text
|
||||
|
||||
|
||||
def test_a_real_port_is_used(config_dir: Path) -> None:
|
||||
config = _config(config_dir)
|
||||
assert config.general.serial_simulated is False
|
||||
assert wants_hardware(config, False) is True
|
||||
|
||||
|
||||
def test_the_flag_wins_over_a_configured_port(config_dir: Path) -> None:
|
||||
assert wants_hardware(_config(config_dir), True) is False
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ audio device
|
||||
|
||||
|
||||
async def test_simulate_gives_a_silent_player(
|
||||
config_dir: Path, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
bus = EventBus()
|
||||
await bus.start()
|
||||
try:
|
||||
general = _config(config_dir, alsa_device=SIMULATE).general
|
||||
assert general.audio_simulated is True
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
player = _build_player(bus, general, clock=RealClock())
|
||||
|
||||
assert isinstance(player, FakePlayer)
|
||||
assert "alsa_device" in caplog.text
|
||||
assert "nothing will be audible" in caplog.text
|
||||
# The clamps still come from the config, so volume behaves the same either way.
|
||||
assert player.volume == 40
|
||||
player.set_volume(999)
|
||||
assert player.volume == 60
|
||||
finally:
|
||||
player.close()
|
||||
await bus.stop()
|
||||
|
||||
|
||||
async def test_an_alsa_device_asks_for_the_real_player(config_dir: Path) -> None:
|
||||
"""Only that it tries: instantiating VlcPlayer needs libVLC, which CI has not got."""
|
||||
bus = EventBus()
|
||||
await bus.start()
|
||||
try:
|
||||
general = _config(config_dir, alsa_device="default").general
|
||||
assert general.audio_simulated is False
|
||||
with pytest.raises(Exception): # noqa: B017 - ImportError or an OSError from libVLC
|
||||
_build_player(bus, general, clock=RealClock())
|
||||
finally:
|
||||
await bus.stop()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- null transport
|
||||
|
||||
|
||||
def test_the_null_transport_drops_what_it_is_handed() -> None:
|
||||
transport = NullTransport()
|
||||
transport.write(b"\x00\x01\x02")
|
||||
assert transport.connected is False
|
||||
Reference in New Issue
Block a user