import shlex from os.path import abspath from pathlib import Path from typing import TYPE_CHECKING, Callable import click import pytest from click.testing import CliRunner import ovgenpy.channel from ovgenpy import cli from ovgenpy.cli import YAML_NAME from ovgenpy.config import yaml from ovgenpy.outputs import FFmpegOutputConfig from ovgenpy.ovgenpy import Config, Ovgen from ovgenpy.util import pushd if TYPE_CHECKING: import pytest_mock def call_main(argv): return CliRunner().invoke(cli.main, argv, catch_exceptions=False, standalone_mode=False) # ovgenpy configuration sinks @pytest.fixture def yaml_sink(mocker: 'pytest_mock.MockFixture') -> Callable: def _yaml_sink(command): dump = mocker.patch.object(yaml, 'dump') argv = shlex.split(command) + ['-w'] call_main(argv) dump.assert_called_once() (cfg, stream), kwargs = dump.call_args assert isinstance(cfg, Config) return cfg, stream return _yaml_sink @pytest.fixture def player_sink(mocker) -> Callable: def _player_sink(command): Ovgen = mocker.patch.object(cli, 'Ovgen') argv = shlex.split(command) + ['-p'] call_main(argv) Ovgen.assert_called_once() args, kwargs = Ovgen.call_args cfg = args[0] assert isinstance(cfg, Config) return cfg, return _player_sink def test_sink_fixture(yaml_sink, player_sink): """ Ensure we can use yaml_sink and player_sink as a fixture directly """ pass @pytest.fixture(params=[yaml_sink, player_sink]) def any_sink(request, mocker): sink = request.param return sink(mocker) # ovgenpy configuration sources def test_no_files(any_sink): with pytest.raises(click.ClickException): any_sink('') @pytest.mark.parametrize('wav_dir', '. tests'.split()) def test_file_dirs(any_sink, wav_dir): """ Ensure loading files from `dir` places `dir/*.wav` in config. """ wavs = Path(wav_dir).glob('*.wav') wavs = sorted(str(x) for x in wavs) cfg = any_sink(wav_dir)[0] assert isinstance(cfg, Config) assert [chan.wav_path for chan in cfg.channels] == wavs def q(path: Path) -> str: return shlex.quote(str(path)) def test_write_dir(yaml_sink): """ Loading `--audio another/dir` should write YAML to current dir. Writing YAML to audio dir: causes relative paths (relative to pwd) to break. """ audio_path = Path('tests/sine440.wav') arg_str = f'tests -a {q(audio_path)}' cfg, outpath = yaml_sink(arg_str) # type: Config, Path assert isinstance(outpath, Path) # Ensure YAML config written to current dir. assert outpath.parent == Path() assert outpath.name == str(outpath) assert str(outpath) == audio_path.with_suffix(YAML_NAME).name # Ensure config paths are valid. assert outpath.parent / cfg.master_audio == audio_path @pytest.mark.usefixtures('Popen') def test_load_yaml_another_dir(yaml_sink, mocker, Popen): """ YAML file located in `another/dir` should resolve `master_audio`, `channels[]. wav_path`, and video `path` from `another/dir`. """ subdir = 'tests' wav = 'sine440.wav' mp4 = 'sine440.mp4' with pushd(subdir): arg_str = f'{wav} -a {wav}' cfg, outpath = yaml_sink(arg_str) # type: Config, Path cfg.begin_time = 100 # To skip all actual rendering # Log execution of Ovgen().play() Wave = mocker.spy(ovgenpy.channel, 'Wave') # Issue: this test does not use cli.main() to compute output path. # Possible solution: Call cli.main() via Click runner. output = FFmpegOutputConfig(cli.get_path(cfg.master_audio, cli.VIDEO_NAME)) ovgen = Ovgen(cfg, subdir, [output]) ovgen.play() # Compute absolute paths wav_abs = abspath(f'{subdir}/{wav}') mp4_abs = abspath(f'{subdir}/{mp4}') # Test `wave_path` args, kwargs = Wave.call_args cfg, wave_path = args assert wave_path == wav_abs # Test output `master_audio` and video `path` args, kwargs = Popen.call_args argv = args[0] assert argv[-1] == mp4_abs assert f'-i {wav_abs}' in ' '.join(argv) # TODO integration test without --audio