import subprocess import pytest from aircox.test import Interface from aircox.controllers import sound_stats sox_output = """ DC offset 0.000000\n Min level 0.000000\n Max level 0.000000\n Pk lev dB -inf\n RMS lev dB -inf\n RMS Pk dB -inf\n RMS Tr dB -inf\n Crest factor 1.00\n Flat factor 179.37\n Pk count 1.86G\n Bit-depth 0/0\n Num samples 930M\n Length s 19383.312\n Scale max 1.000000\n Window s 0.050\n """ sox_values = { "DC offset": 0.0, "Min level": 0.0, "Max level": 0.0, "Pk lev dB": float("-inf"), "RMS lev dB": float("-inf"), "RMS Pk dB": float("-inf"), "RMS Tr dB": float("-inf"), "Flat factor": 179.37, "length": 19383.312, } @pytest.fixture def sox_interfaces(): process = Interface( None, {"communicate": ("", sox_output.encode("utf-8"))} ) subprocess = Interface.inject( sound_stats, "subprocess", {"Popen": lambda *_, **__: process} ) yield {"process": process, "subprocess": subprocess} subprocess._irelease() @pytest.fixture def sox_stats(sox_interfaces): return sound_stats.SoxStats() @pytest.fixture def stats(): return sound_stats.SoundStats("/tmp/audio.wav", sample_length=10) @pytest.fixture def stats_interfaces(stats): def iw(path, **kw): kw["path"] = path kw.setdefault("length", stats.sample_length * 2) return kw SxS = sound_stats.SoxStats sound_stats.SoxStats = iw yield iw sound_stats.SoxStats = SxS class TestSoxStats: def test_parse(self, sox_stats): values = sox_stats.parse(sox_output) assert values == sox_values def test_analyse(self, sox_stats, sox_interfaces): sox_stats.analyse("fake_path", 1, 2) assert sox_interfaces["subprocess"]._trace("Popen") == ( (["sox", "fake_path", "-n", "trim", "1", "2", "stats"],), {"stdout": subprocess.PIPE, "stderr": subprocess.PIPE}, ) assert sox_stats.values == sox_values class TestSoundStats: def test_get_file_stats(self, stats): file_stats = {"a": 134} stats.stats = [file_stats] assert stats.get_file_stats() is file_stats def test_get_file_stats_none(self, stats): stats.stats = [] assert stats.get_file_stats() is None def test_analyse(self, stats, stats_interfaces): stats.analyse() assert stats.stats == [ {"path": stats.path, "length": stats.sample_length * 2}, {"path": stats.path, "at": 0, "length": stats.sample_length}, {"path": stats.path, "at": 10, "length": stats.sample_length}, ] def test_analyse_no_sample_length(self, stats, stats_interfaces): stats.sample_length = 0 stats.analyse() assert stats.stats == [{"length": 0, "path": stats.path}] def test_check(self, stats): good = [{"val": i} for i in range(0, 11)] bad = [{"val": i} for i in range(-10, 0)] + [ {"val": i} for i in range(11, 20) ] stats.stats = good + bad calls = {} stats.resume = lambda *_: calls.setdefault("resume", True) stats.check("val", 0, 10) assert calls == {"resume": True} assert all(i < len(good) for i in stats.good) assert all(i >= len(good) for i in stats.bad)