Spaces:
Running on Zero
Running on Zero
| """Tests for app.py wiring: on-upload analysis, demo loading, and the full | |
| finish_song pipeline with the SA3 engine stubbed (no GPU, no weights). Verifies | |
| the real DSP (analyze, enhance, stitch) runs end-to-end on the bundled demo and | |
| produces a valid 44.1 kHz stereo file. Skips if gradio isn't installed.""" | |
| import os | |
| import numpy as np | |
| import pytest | |
| import soundfile as sf | |
| pytest.importorskip("gradio") | |
| import app # noqa: E402 | |
| import engine # noqa: E402 | |
| DEMO = app.PUSHBACK_DEMO | |
| _HAVE_DEMO = os.path.exists(DEMO) | |
| need_demo = pytest.mark.skipif(not _HAVE_DEMO, reason="bundled demo clip absent") | |
| class _Prog: | |
| """stand-in for gr.Progress — records the (frac, desc) stages it's told.""" | |
| def __init__(self): | |
| self.calls = [] | |
| def __call__(self, frac, desc=None): | |
| self.calls.append((frac, desc)) | |
| def test_load_demo_returns_clip(): | |
| assert app.load_demo() == DEMO | |
| assert os.path.exists(DEMO) | |
| def test_analyze_on_upload_reports_and_enables(): | |
| info_update, btn_update = app.analyze_on_upload(DEMO) | |
| md = info_update["value"] | |
| assert "KEY" in md and "TEMPO" in md and "BPM" in md | |
| assert info_update["visible"] is True | |
| assert btn_update["interactive"] is True | |
| def test_analyze_on_upload_empty_disables_button(): | |
| _, btn_update = app.analyze_on_upload(None) | |
| assert btn_update["interactive"] is False | |
| def test_finish_song_pipeline_with_stubbed_engine(monkeypatch): | |
| """stub engine.continue_audio with a fast synthetic tail; the rest of the | |
| pipeline (enhance, analyze, stitch, write) runs for real.""" | |
| def fake_continue_audio(clip_path, total_seconds, prompt="", progress=None, | |
| **kw): | |
| if progress: | |
| progress("reading") | |
| progress("composing") | |
| progress("finalizing") | |
| new_secs = total_seconds - 29.53 # demo is ~29.53s | |
| n = int(max(new_secs, 5) * engine.SR) | |
| t = np.arange(n) / engine.SR | |
| wave = 0.2 * np.sin(2 * np.pi * 220 * t).astype(np.float32) | |
| tail = np.stack([wave, wave]) | |
| return tail, 29.53, engine.SR | |
| monkeypatch.setattr(engine, "continue_audio", fake_continue_audio) | |
| prog = _Prog() | |
| out_path, summary = app.finish_song(DEMO, 60, "", False, progress=prog) | |
| assert os.path.exists(out_path) | |
| y, sr = sf.read(out_path) | |
| assert sr == 44100 | |
| assert y.ndim == 2 and y.shape[1] == 2 # 44.1k stereo | |
| assert len(y) / sr > 50 # ~60s finished | |
| assert float(np.abs(y).max()) <= 1.0 | |
| assert float(np.sqrt(np.mean(y ** 2))) > 1e-3 # not silent | |
| assert "BPM" in summary and "Stable Audio 3" in summary | |
| # progress streamed several stages and ended at 100% | |
| assert len(prog.calls) >= 4 | |
| assert prog.calls[-1][0] == 1.0 | |
| def test_finish_song_remaster_path(monkeypatch): | |
| def fake_continue_audio(clip_path, total_seconds, prompt="", progress=None, | |
| **kw): | |
| n = int(15 * engine.SR) | |
| wave = 0.2 * np.sin(2 * np.pi * 220 * np.arange(n) / engine.SR) | |
| return np.stack([wave, wave]).astype(np.float32), 29.53, engine.SR | |
| monkeypatch.setattr(engine, "continue_audio", fake_continue_audio) | |
| out_path, _ = app.finish_song(DEMO, 45, "", True, progress=_Prog()) | |
| assert os.path.exists(out_path) | |
| def test_finish_song_rejects_empty_input(): | |
| with pytest.raises(Exception): | |
| app.finish_song(None, 60, "", False, progress=_Prog()) | |