Add pytest suite and CI (roadmap A1)
37 headless tests covering the logic that is cheap to break: - split planner: plan_disc_split, chapter helpers (whole-fit, multi-disc partition, single-movie chapter/even-cut split, contiguous coverage) - High-profile fill-disc bitrate math (disc count + bounds + capacity) - ffmpeg command generation: profiles, text/image hardsub, 2-pass, nice, progress-to-file, even pad/crop + positive offsets, -ss seek, and the AC3 448k audio-clamp regression - ffprobe subtitle detection: text/image classification, tags/disposition tests/conftest.py makes the in-tree package importable and installs the gettext _ builtin so modules run headless. Forgejo Actions workflow runs the suite on push/PR. Update CLAUDE.md's testing section accordingly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b5a6cfe25f
commit
691664095b
9 changed files with 559 additions and 8 deletions
67
tests/test_high_profile_bitrate.py
Normal file
67
tests/test_high_profile_bitrate.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
"""Tests for the High-quality profile fill-the-disc bitrate math."""
|
||||
|
||||
from devedeng.project import compute_high_profile_bitrate as calc
|
||||
|
||||
CAP_KB = 3_780_000.0 # 4.2 GB * 0.90 margin, in kBytes
|
||||
FLOOR = 3000
|
||||
CAP = 9000
|
||||
|
||||
|
||||
def audio_kbits(rate_kbps, runtime_s, streams=1):
|
||||
return rate_kbps * streams * runtime_s
|
||||
|
||||
|
||||
def test_short_movie_one_disc_at_cap():
|
||||
# 30 min: tiny, so bitrate is pinned at the 9000k cap, one disc
|
||||
n, br = calc(1800, audio_kbits(448, 1800), CAP_KB)
|
||||
assert n == 1
|
||||
assert br == CAP
|
||||
|
||||
|
||||
def test_two_hours_fills_one_disc():
|
||||
n, br = calc(7200, audio_kbits(448, 7200), CAP_KB)
|
||||
assert n == 1
|
||||
assert FLOOR <= br <= CAP
|
||||
# should be comfortably above the floor (filling the disc)
|
||||
assert br > 3500
|
||||
|
||||
|
||||
def test_four_hours_two_discs():
|
||||
n, br = calc(14400, audio_kbits(448, 14400), CAP_KB)
|
||||
assert n == 2
|
||||
assert FLOOR <= br <= CAP
|
||||
|
||||
|
||||
def test_eight_hours_multiple_discs():
|
||||
n, br = calc(28800, audio_kbits(448, 28800), CAP_KB)
|
||||
assert n >= 3
|
||||
assert br >= FLOOR
|
||||
|
||||
|
||||
def test_zero_runtime_guard():
|
||||
assert calc(0, 0, CAP_KB) == (1, CAP)
|
||||
|
||||
|
||||
def test_bitrate_always_within_bounds():
|
||||
for rt in (300, 1800, 3600, 7200, 14400, 30000, 60000):
|
||||
n, br = calc(rt, audio_kbits(448, rt), CAP_KB)
|
||||
assert FLOOR <= br <= CAP, (rt, br)
|
||||
assert n >= 1
|
||||
|
||||
|
||||
def test_filled_content_does_not_exceed_disc_capacity():
|
||||
# the chosen bitrate must not overflow the n discs it claims to fill
|
||||
for rt in (1800, 7200, 14400, 28800):
|
||||
ak = audio_kbits(448, rt)
|
||||
n, br = calc(rt, ak, CAP_KB)
|
||||
used_kbits = br * rt + ak
|
||||
assert used_kbits <= n * CAP_KB * 8 + 1 # +1 for rounding slack
|
||||
|
||||
|
||||
def test_menu_reservation_reduces_video_budget():
|
||||
rt = 7200
|
||||
ak = audio_kbits(448, rt)
|
||||
n0, br0 = calc(rt, ak, CAP_KB, menu_kbytes=0)
|
||||
n1, br1 = calc(rt, ak, CAP_KB, menu_kbytes=200_000)
|
||||
# with a menu eating space, the fill bitrate can only be <= the no-menu one
|
||||
assert br1 <= br0
|
||||
Loading…
Add table
Add a link
Reference in a new issue