"""Validate generated FCPXML against Apple's official DTDs (v0.8.0). These tests use the DTDs shipped inside the local Final Cut Pro app bundle — the only authoritative FCPXML spec (Apple's online docs stopped at 1.10). On machines without FCP (CI runners), the whole module skips. """ import shutil from pathlib import Path import pytest from fcpxml.dtd import ( available_dtd_versions, find_apple_dtd, validate_against_dtd, ) from fcpxml.templates import ClipSpec, apply_template from fcpxml.writer import FCPXMLModifier SAMPLE = Path(__file__).parent.parent / "examples" / "sample.fcpxml" HAVE_DTDS = bool(available_dtd_versions()) and shutil.which("xmllint") pytestmark = pytest.mark.skipif( not HAVE_DTDS, reason="Apple FCPXML DTDs not available (Final Cut Pro not installed) " "or xmllint missing", ) def test_dtds_present_through_1_13(): versions = available_dtd_versions() assert "1.11" in versions assert "1.13" in versions def test_find_apple_dtd_rejects_garbage(): assert find_apple_dtd("../../etc/passwd") is None assert find_apple_dtd("99.99") is None def test_template_output_is_dtd_valid(tmp_path): clips = { "main_content": ClipSpec(src="/test/main.mov", name="Main", duration=10.0), } out = str(tmp_path / "template.fcpxml") apply_template("lower_thirds", clips, out) ok, detail = validate_against_dtd(out) assert ok is True, detail # DTD-valid 1.13 fixture (asset uses the required media-rep form; # markers live inside clips, never at sequence level). VALID_113 = ( '\n' '' '' '' '' '' '' '' '' '' '' '' ) def test_modifier_roundtrip_stays_dtd_valid(tmp_path): src = tmp_path / "valid.fcpxml" src.write_text(VALID_113) ok, detail = validate_against_dtd(str(src)) assert ok is True, f"fixture itself invalid: {detail}" out = str(tmp_path / "valid_modified.fcpxml") modifier = FCPXMLModifier(str(src)) modifier.add_marker("A", "00:00:01:00", "dtd-check") modifier.trim_clip("B", trim_end="-24/24s") modifier.save(out) ok, detail = validate_against_dtd(out) assert ok is True, detail def test_repo_sample_fixture_known_nonconformant(): """examples/sample.fcpxml predates media-rep and uses sequence-level chapter markers — Apple's 1.11 DTD rejects both. Documented here so the day it gets fixed, this test flips and forces a fixture review. """ ok, detail = validate_against_dtd(str(SAMPLE)) assert ok is False assert "media-rep" in detail or "asset" in detail def test_invalid_xml_fails_dtd(tmp_path): bad = tmp_path / "bad.fcpxml" bad.write_text( '\n' '' ) ok, detail = validate_against_dtd(str(bad)) assert ok is False assert detail def test_validate_missing_file_unavailable(): ok, detail = validate_against_dtd("/nonexistent/file.fcpxml") assert ok is None assert "not found" in detail.lower() def test_generated_titles_stay_dtd_valid(tmp_path): """Regression for 2026-08-17: text-style ids derived from the caption text (spaces, accents, leading digits) are not valid XML Names, and xmllint rejected the document with "Syntax of value for attribute id of text-style-def is not valid" — FCP refused to import the file. """ src = tmp_path / "titles.fcpxml" src.write_text(VALID_113) modifier = FCPXMLModifier(str(src)) modifier.add_text_title("A", "3 coisas que você precisa saber", duration="24/24s") modifier.generate_dynamic_subtitles( "B", [ {"word": "Olá", "start": 0.1, "end": 0.4}, {"word": "mundo!", "start": 0.5, "end": 0.9}, ], ) out = str(tmp_path / "titles_out.fcpxml") modifier.save(out) ok, detail = validate_against_dtd(out) assert ok is True, detail