OpenTranscode/tests/test_skip_existing.py

194 lines
7.6 KiB
Python
Executable File

"""
v4.3.0: skip-existing detection tests.
When the output file already exists with a matching video+audio codec,
the file is skipped instead of re-encoded. This is the default
(--skip-existing); pass --force-reencode to disable.
"""
from __future__ import annotations
import json
import subprocess
from pathlib import Path
from unittest.mock import MagicMock
from conftest import make_minimal_worker
def _ffprobe_result(payload: dict) -> subprocess.CompletedProcess:
return subprocess.CompletedProcess(
args=["ffprobe"], returncode=0,
stdout=json.dumps(payload), stderr="",
)
def _av1_opus_output() -> dict:
"""ffprobe JSON for an AV1+Opus file (matches VIDEO_CODECS[0] + AUDIO_PROFILES[0])."""
return {
"streams": [
{"index": 0, "codec_type": "video", "codec_name": "av1",
"width": 1920, "height": 1080},
{"index": 1, "codec_type": "audio", "codec_name": "opus"},
],
"format": {"duration": "10.0"},
}
def _h264_aac_output() -> dict:
"""ffprobe JSON for an H.264+AAC file (does NOT match AV1+Opus profile)."""
return {
"streams": [
{"index": 0, "codec_type": "video", "codec_name": "h264",
"width": 1920, "height": 1080},
{"index": 1, "codec_type": "audio", "codec_name": "aac"},
],
"format": {"duration": "10.0"},
}
def test_skip_existing_returns_false_when_output_missing(opentranscode_module, mock_env, tmp_path):
"""No output file → don't skip (proceed with encode)."""
worker = make_minimal_worker(opentranscode_module, env=mock_env)
worker.skip_existing = True
# Set up the codec profile so ffprobe_codec_name is populated.
worker.video_codec = opentranscode_module.VIDEO_CODECS[0] # AV1
worker.audio_profile = opentranscode_module.AUDIO_PROFILES[0] # Opus
worker.resolution = opentranscode_module.RESOLUTION_PRESETS[0] # Original
output_f = tmp_path / "nonexistent_archived.mkv"
assert not worker._output_already_encoded(tmp_path / "source.mkv", output_f)
def test_skip_existing_returns_true_when_codec_matches(opentranscode_module, mock_env, tmp_path, monkeypatch):
"""Output exists + ffprobe reads it + codec matches → skip."""
worker = make_minimal_worker(opentranscode_module, env=mock_env)
worker.skip_existing = True
worker.video_codec = opentranscode_module.VIDEO_CODECS[0] # AV1 → ffprobe_codec_name="av1"
worker.audio_profile = opentranscode_module.AUDIO_PROFILES[0] # Opus → "opus"
worker.resolution = opentranscode_module.RESOLUTION_PRESETS[0] # Original (no scaling)
output_f = tmp_path / "output_archived.mkv"
output_f.write_bytes(b"fake mkv content")
monkeypatch.setattr(
"subprocess.run",
MagicMock(return_value=_ffprobe_result(_av1_opus_output())),
)
assert worker._output_already_encoded(tmp_path / "source.mkv", output_f) is True
def test_skip_existing_returns_false_when_video_codec_mismatches(opentranscode_module, mock_env, tmp_path, monkeypatch):
"""Output exists but video codec is h264 (not av1) → don't skip."""
worker = make_minimal_worker(opentranscode_module, env=mock_env)
worker.skip_existing = True
worker.video_codec = opentranscode_module.VIDEO_CODECS[0] # AV1
worker.audio_profile = opentranscode_module.AUDIO_PROFILES[0] # Opus
worker.resolution = opentranscode_module.RESOLUTION_PRESETS[0]
output_f = tmp_path / "output_archived.mkv"
output_f.write_bytes(b"fake mkv content")
# ffprobe says h264/aac, but we selected AV1/Opus → mismatch → don't skip.
monkeypatch.setattr(
"subprocess.run",
MagicMock(return_value=_ffprobe_result(_h264_aac_output())),
)
assert worker._output_already_encoded(tmp_path / "source.mkv", output_f) is False
def test_skip_existing_returns_false_when_ffprobe_fails(opentranscode_module, mock_env, tmp_path, monkeypatch):
"""Output exists but ffprobe returns None (corrupt) → don't skip."""
worker = make_minimal_worker(opentranscode_module, env=mock_env)
worker.skip_existing = True
worker.video_codec = opentranscode_module.VIDEO_CODECS[0]
worker.audio_profile = opentranscode_module.AUDIO_PROFILES[0]
worker.resolution = opentranscode_module.RESOLUTION_PRESETS[0]
output_f = tmp_path / "output_archived.mkv"
output_f.write_bytes(b"corrupt content")
# ffprobe returns non-zero (corrupt file).
monkeypatch.setattr(
"subprocess.run",
MagicMock(return_value=subprocess.CompletedProcess(
args=["ffprobe"], returncode=1, stdout="", stderr="error",
)),
)
assert worker._output_already_encoded(tmp_path / "source.mkv", output_f) is False
def test_skip_existing_returns_false_when_no_ffprobe(opentranscode_module, mock_env, tmp_path):
"""No ffprobe available → can't verify codec → don't skip (safe default)."""
worker = make_minimal_worker(opentranscode_module, env=mock_env)
worker.skip_existing = True
worker.video_codec = opentranscode_module.VIDEO_CODECS[0]
worker.audio_profile = opentranscode_module.AUDIO_PROFILES[0]
worker.resolution = opentranscode_module.RESOLUTION_PRESETS[0]
output_f = tmp_path / "output_archived.mkv"
output_f.write_bytes(b"content")
# Simulate no ffprobe.
worker.env.ffprobe_path = None
assert worker._output_already_encoded(tmp_path / "source.mkv", output_f) is False
def test_skip_existing_checks_resolution_when_scaling_requested(opentranscode_module, mock_env, tmp_path, monkeypatch):
"""When scaling is requested, output resolution must match the target."""
worker = make_minimal_worker(opentranscode_module, env=mock_env)
worker.skip_existing = True
worker.video_codec = opentranscode_module.VIDEO_CODECS[0] # AV1
worker.audio_profile = opentranscode_module.AUDIO_PROFILES[0] # Opus
# Select a target resolution (720p = 1280x720, index 2).
# The mock ffprobe returns 1920x1080, so they WON'T match → don't skip.
worker.resolution = opentranscode_module.RESOLUTION_PRESETS[2] # 720p
output_f = tmp_path / "output_archived.mkv"
output_f.write_bytes(b"content")
# ffprobe says 1920x1080, but we want 1280x720 → mismatch → don't skip.
monkeypatch.setattr(
"subprocess.run",
MagicMock(return_value=_ffprobe_result(_av1_opus_output())),
)
assert worker._output_already_encoded(tmp_path / "source.mkv", output_f) is False
# ── CLI flag tests ─────────────────────────────────────────────────────────
def test_cli_skip_existing_default_true():
"""Without --force-reencode, skip_existing defaults to True."""
from opentranscode.cli import build_parser
args = build_parser().parse_args([])
assert args.skip_existing is True
def test_cli_force_reencode_sets_false():
"""--force-reencode sets skip_existing to False."""
from opentranscode.cli import build_parser
args = build_parser().parse_args(["--force-reencode"])
assert args.skip_existing is False
def test_cli_skip_existing_explicit():
"""--skip-existing explicitly sets skip_existing to True."""
from opentranscode.cli import build_parser
args = build_parser().parse_args(["--skip-existing"])
assert args.skip_existing is True
def test_launch_gui_signature_accepts_skip_existing():
"""launch_gui() accepts the skip_existing kwarg (v4.3.0)."""
import inspect
from opentranscode import launch_gui
sig = inspect.signature(launch_gui)
assert "skip_existing" in sig.parameters
# Default must be True (skip by default).
assert sig.parameters["skip_existing"].default is True