From c2b0f7e4fb4738afcae1705db8f983dd90a669a4 Mon Sep 17 00:00:00 2001 From: historia Date: Sun, 6 Sep 2026 15:17:57 -0400 Subject: inital commit --- lib/tests/test_cli.py | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 lib/tests/test_cli.py (limited to 'lib/tests/test_cli.py') diff --git a/lib/tests/test_cli.py b/lib/tests/test_cli.py new file mode 100644 index 0000000..e43489c --- /dev/null +++ b/lib/tests/test_cli.py @@ -0,0 +1,152 @@ +import numpy as np +import soundfile as sf +from conftest import speechish + +from producer import io as pio +from producer.cli import _apply_args, build_parser, process_one +from producer.config import Options + + +def _mk_wav(tmp_path, name="in.wav", stereo=False): + x = speechish(3.0, level_dbfs=-30.0) + if stereo: + data = np.stack([x, x * 0.5], axis=1) + sf.write(str(tmp_path / name), data, 44100, subtype="PCM_16") + else: + sf.write(str(tmp_path / name), x, 44100, subtype="PCM_16") + return tmp_path / name + + +def _opts(**kw): + opts = Options() + opts.denoise = "off" + opts.enhance = "off" + for k, v in kw.items(): + setattr(opts, k, v) + return opts + + +def test_decode_stereo_mixdown(tmp_path): + p = _mk_wav(tmp_path, "st.wav", stereo=True) + x, sr = pio.decode(p) + assert sr == 44100 + assert x.dtype.name == "float32" + assert x.ndim == 1 + + +def test_encode_wav_bitdepths(tmp_path): + x = speechish(2.0, level_dbfs=-20.0) + for depth in (16, 24, 32): + out = tmp_path / f"o{depth}.wav" + pio.encode(x, 44100, out, "wav", depth) + y, sr = pio.decode(out) + assert sr == 44100 + assert ( + abs( + float(np.sqrt(np.mean(y.astype(np.float64) ** 2))) + - float(np.sqrt(np.mean(x.astype(np.float64) ** 2))) + ) + < 1e-3 + ) + + +def test_encode_flac(tmp_path): + x = speechish(2.0, level_dbfs=-20.0) + out = tmp_path / "o.flac" + pio.encode(x, 44100, out, "flac", 24) + y, sr = pio.decode(out) + assert sr == 44100 + assert np.corrcoef(x, y)[0, 1] > 0.999 + + +def test_mp3_roundtrip(tmp_path): + import pytest as _pt + + if not pio.ffmpeg_available(): + _pt.skip("ffmpeg missing") + x = speechish(4.0, level_dbfs=-20.0) + out = tmp_path / "o.mp3" + pio.encode(x, 44100, out, "mp3", 16) + y, sr = pio.decode(out) + assert sr == 44100 + from producer import meters + + assert abs(meters.rms_db(x) - meters.rms_db(y)) < 0.7 + + +def test_decode_via_ffmpeg_fallback(tmp_path): + import pytest as _pt + + if not pio.ffmpeg_available(): + _pt.skip("ffmpeg missing") + x = speechish(4.0, level_dbfs=-20.0) + out = tmp_path / "o.mp3" + pio.encode(x, 44100, out, "mp3", 16) + y, sr = pio.decode(out) + assert sr == 44100 + assert y.size > 0 + + +def test_process_one_end_to_end(tmp_path, capsys): + import json + + from producer import meters + + inp = _mk_wav(tmp_path, "e2e.wav") + out = tmp_path / "e2e_master.wav" + rc = process_one(inp, _opts(report=True), single=True) + assert rc == out + assert out.exists() + rep_path = out.with_name("e2e_master.report.json") + assert rep_path.exists() + rep = json.loads(rep_path.read_text()) + y, sr = pio.decode(out) + assert abs(meters.rms_db(y) + 20.0) < 0.6 + assert meters.true_peak_db(y, sr) <= -2.9 + assert rep["after"]["rms_db"] != 0 + + +def test_dry_run_listing(tmp_path, capsys): + from producer.cli import main + + inp = _mk_wav(tmp_path, "dry.wav") + rc = main([str(inp), "--dry-run", "--denoise", "off"]) + assert rc == 0 + out = capsys.readouterr().out + assert "denoise" in out and "dsp" in out and "levelling" in out + + +def test_arg_parsing_precedence(): + parser = build_parser() + args = parser.parse_args( + ["in.wav", "--profile", "podcast", "--warmth", "0.1", "--ceiling", "-2.0"] + ) + opts = Options() + _apply_args(opts, args) + assert opts.profile == "podcast" + assert opts.loudness_mode() == "lufs" + assert opts.strengths["warmth"] == 0.1 + assert opts.ceiling() == -2.0 + assert opts.strengths["air"] is None + + +def test_radio_profile_has_tuned_defaults(): + from producer import pipeline + + opts = Options(profile="radio") + assert opts.eff("tape") > 0 and opts.eff("soothe") > 0 + assert opts.loudness_mode() == "lufs" + stages = pipeline.build_stages(opts) + assert [st.name for st in stages] == ["denoise", "enhance", "dsp", "levelling"] + assert opts.denoise_strength is not None + + +def test_tape_and_soothe_flags_override(): + args = build_parser().parse_args( + ["in.wav", "--profile", "radio", "--tape", "0.4", "--soothe", "0.7"] + ) + opts = Options() + _apply_args(opts, args) + assert opts.profile == "radio" + assert opts.strengths["tape"] == 0.4 + assert opts.strengths["soothe"] == 0.7 -- cgit v1.2.3