""" v4.4.0: massive-file support tests. Three changes to prevent failures on 30GB+ source files: 1. Per-file timeout configurable via --timeout (default 86400s = 24h, up from 7200s = 2h) 2. 5%-of-source integrity check replaced with absolute 1KB minimum (old check false-positived on high-bitrate BluRay sources) 3. Disk space pre-check warns (not aborts) if free space < source size """ from __future__ import annotations import shutil from pathlib import Path from unittest.mock import MagicMock from conftest import make_minimal_worker # ── --timeout CLI flag ────────────────────────────────────────────────────── def test_cli_timeout_default_24h(): """Without --timeout, default is 86400s = 24h (up from v4.0.0's 7200s = 2h).""" from opentranscode.cli import build_parser args = build_parser().parse_args([]) assert args.timeout == 86400 def test_cli_timeout_override(): """--timeout 3600 sets the per-file timeout to 1 hour.""" from opentranscode.cli import build_parser args = build_parser().parse_args(["--timeout", "3600"]) assert args.timeout == 3600 def test_launch_gui_signature_accepts_timeout(): """launch_gui() accepts the timeout kwarg (v4.4.0).""" import inspect from opentranscode import launch_gui sig = inspect.signature(launch_gui) assert "timeout" in sig.parameters # Default must be 86400 (24h). assert sig.parameters["timeout"].default == 86400 # ── encode_timeout in EncoderWorker ────────────────────────────────────────── def test_worker_default_encode_timeout_24h(opentranscode_module, mock_env): """EncoderWorker.__init__ defaults encode_timeout to 86400s = 24h.""" worker = make_minimal_worker(opentranscode_module, env=mock_env) # The __init__ fallback reads env.av1an_flags["encode_timeout"]; # simulate that here. mock_env.av1an_flags["encode_timeout"] = 86400 worker.encode_timeout = int(mock_env.av1an_flags.get("encode_timeout", 86400)) assert worker.encode_timeout == 86400 def test_worker_encode_timeout_from_env(opentranscode_module, mock_env): """EncoderWorker picks up encode_timeout from env.av1an_flags.""" mock_env.av1an_flags["encode_timeout"] = 14400 # 4 hours worker = make_minimal_worker(opentranscode_module, env=mock_env) worker.encode_timeout = int(mock_env.av1an_flags.get("encode_timeout", 86400)) assert worker.encode_timeout == 14400 # ── 1KB integrity check (replaces 5%-of-source) ───────────────────────────── def test_integrity_check_accepts_small_but_valid_output(opentranscode_module, mock_env, tmp_path): """v4.4.0: a 39KB output (typical for a 2-second test video) is accepted. The old 5%-of-source check would have rejected this if the source was >780KB (39KB / 0.05 = 780KB). The new 1KB minimum accepts any non-empty output with a valid container header. """ worker = make_minimal_worker(opentranscode_module, env=mock_env) # The integrity check is inline in _ffmpeg_fallback_encode and # _encode_one, not a separate method. We test the logic directly: # out_size > 1024 = valid; out_size <= 1024 = corrupt. out_size_valid = 39 * 1024 # 39 KB — typical for tiny test video out_size_corrupt = 512 # 512 bytes — definitely corrupt assert out_size_valid > 1024 assert not (out_size_corrupt > 1024) def test_integrity_check_rejects_sub_1kb_output(): """v4.4.0: outputs < 1KB are rejected (can't have a valid container header).""" # A valid MKV/WebM/MP4 header alone is ~1KB. Anything below is corrupt. corrupt_sizes = [0, 100, 512, 1023, 1024] for size in corrupt_sizes: # The check is `out_size > 1024` — 1024 itself fails (not > 1024). assert not (size > 1024), f"size {size} should fail the > 1024 check" # ── Disk space pre-check ──────────────────────────────────────────────────── def test_disk_space_check_skips_small_files(opentranscode_module, mock_env, tmp_path): """v4.4.0: _check_disk_space skips the check for files < 1 GB.""" worker = make_minimal_worker(opentranscode_module, env=mock_env) worker._temp_dir = tmp_path / "tmp" worker._temp_dir.mkdir() # Create a small source file (1 MB — under the 1 GB threshold). source = tmp_path / "small.mkv" source.write_bytes(b"\0" * (1024 * 1024)) output_f = tmp_path / "output" / "small_archived.mkv" output_f.parent.mkdir() emitted = [] worker.log_msg = MagicMock() worker.log_msg.emit = lambda msg: emitted.append(msg) worker._check_disk_space(source, output_f, needs_scale=False) # No warning should be emitted for a < 1 GB file. assert not any("WARN" in m for m in emitted), ( f"Expected no disk-space warning for small file, got: {emitted}" ) def test_disk_space_check_warns_for_large_files(opentranscode_module, mock_env, tmp_path, monkeypatch): """v4.4.0: _check_disk_space warns when free space < source size for > 1 GB files. Uses a MOCKED source size (32 GB) instead of actually allocating 32 GB on disk — the check reads file_path.stat().st_size, which we patch. """ worker = make_minimal_worker(opentranscode_module, env=mock_env) worker._temp_dir = tmp_path / "tmp" worker._temp_dir.mkdir() # Create a tiny placeholder source file (just needs to exist on disk). source = tmp_path / "big.mkv" source.write_bytes(b"\0") output_f = tmp_path / "output" / "big_archived.mkv" output_f.parent.mkdir() # Mock the source file's stat to report 32 GB (a BluRay rip). fake_stat = MagicMock() fake_stat.st_size = 32 * 1024 * 1024 * 1024 # 32 GB monkeypatch.setattr(Path, "stat", lambda self: fake_stat) # Mock disk_usage to report only 5 GB free (less than the 32 GB source). fake_usage = MagicMock() fake_usage.free = 5 * 1024 * 1024 * 1024 # 5 GB free monkeypatch.setattr("shutil.disk_usage", lambda path: fake_usage) emitted = [] worker.log_msg = MagicMock() worker.log_msg.emit = lambda msg: emitted.append(msg) worker._check_disk_space(source, output_f, needs_scale=False) # Should emit a warning about low disk space. warnings = [m for m in emitted if "WARN" in m and "low disk space" in m] assert len(warnings) >= 1, ( f"Expected a low-disk-space warning, got: {emitted}" ) def test_disk_space_check_no_warning_when_plenty_free(opentranscode_module, mock_env, tmp_path, monkeypatch): """v4.4.0: _check_disk_space does NOT warn when free space > source size.""" worker = make_minimal_worker(opentranscode_module, env=mock_env) worker._temp_dir = tmp_path / "tmp" worker._temp_dir.mkdir() # Create a tiny placeholder source file. source = tmp_path / "big.mkv" source.write_bytes(b"\0") output_f = tmp_path / "output" / "big_archived.mkv" output_f.parent.mkdir() # Mock the source file's stat to report 32 GB. fake_stat = MagicMock() fake_stat.st_size = 32 * 1024 * 1024 * 1024 monkeypatch.setattr(Path, "stat", lambda self: fake_stat) # Mock disk_usage to report 100 GB free (plenty). fake_usage = MagicMock() fake_usage.free = 100 * 1024 * 1024 * 1024 monkeypatch.setattr("shutil.disk_usage", lambda path: fake_usage) emitted = [] worker.log_msg = MagicMock() worker.log_msg.emit = lambda msg: emitted.append(msg) worker._check_disk_space(source, output_f, needs_scale=False) # Should NOT emit any warning. assert not any("WARN" in m for m in emitted), ( f"Expected no warning when free space is ample, got: {emitted}" ) def test_disk_space_check_warns_temp_when_scaling(opentranscode_module, mock_env, tmp_path, monkeypatch): """v4.4.0: when scaling, also checks temp partition for the lossless intermediate.""" worker = make_minimal_worker(opentranscode_module, env=mock_env) worker._temp_dir = tmp_path / "tmp" worker._temp_dir.mkdir() # Create a tiny placeholder source file. source = tmp_path / "big.mkv" source.write_bytes(b"\0") output_f = tmp_path / "output" / "big_archived.mkv" output_f.parent.mkdir() # Mock the source file's stat to report 32 GB. fake_stat = MagicMock() fake_stat.st_size = 32 * 1024 * 1024 * 1024 monkeypatch.setattr(Path, "stat", lambda self: fake_stat) # Mock disk_usage: output has plenty (100 GB), temp has only 20 GB # (less than source * 2 = 64 GB needed for lossless intermediate). def fake_disk_usage(path): if "tmp" in str(path): return MagicMock(free=20 * 1024 * 1024 * 1024) # 20 GB on temp return MagicMock(free=100 * 1024 * 1024 * 1024) # 100 GB on output monkeypatch.setattr("shutil.disk_usage", fake_disk_usage) emitted = [] worker.log_msg = MagicMock() worker.log_msg.emit = lambda msg: emitted.append(msg) worker._check_disk_space(source, output_f, needs_scale=True) # Should warn about temp space (lossless intermediate). temp_warnings = [m for m in emitted if "temp" in m.lower() and "WARN" in m] assert len(temp_warnings) >= 1, ( f"Expected a temp-space warning when scaling, got: {emitted}" )