aboutsummaryrefslogtreecommitdiff
path: root/lib/tests/test_cli.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-09-06 15:17:57 -0400
committerhistoria <historiavg@proton.me>2026-09-06 15:22:52 -0400
commitc2b0f7e4fb4738afcae1705db8f983dd90a669a4 (patch)
treeffcb4bfea9227b40646ab0836decfbac55b9371b /lib/tests/test_cli.py
downloadproducer-c2b0f7e4fb4738afcae1705db8f983dd90a669a4.tar.gz
inital commit
Diffstat (limited to 'lib/tests/test_cli.py')
-rw-r--r--lib/tests/test_cli.py152
1 files changed, 152 insertions, 0 deletions
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