blob: b6671ec7f05a37ba0b628316bac6189498e79304 [file]
"""Tests for FFmpeg component extraction and config header generation."""
import ast
import tempfile
import unittest
from pathlib import Path
from generate_component_defs import (
DECLARATIONS,
SYNTHETIC_COMPONENTS,
component_declarations,
configure_dependencies,
format_assignment,
generate_definitions,
literal_assignments,
policy_definitions,
)
from generate_config_defs import (
generate_avconfig_h,
generate_config_h_in,
generate_ffversion_h,
resolve_list,
)
class ComponentFormattingTest(unittest.TestCase):
def setUp(self):
self.policy = (Path(__file__).parent / "7.1.1.bcr.beta.7/overlay/component_defs.bzl").read_text(
encoding="utf-8",
)
def test_generated_assignments_preserve_previous_layout(self):
names = {
"COMPONENT_TYPES",
"COMPONENT_REGISTRY",
"CONFIG_EXTRA_REGISTRY",
"ALWAYS_AVAILABLE_LIBS",
"PROFILE_MINIMAL",
"FILTER_SYMBOL_MAP",
}
for node in ast.parse(self.policy).body:
if not isinstance(node, ast.Assign) or not isinstance(node.targets[0], ast.Name):
continue
name = node.targets[0].id
if name in names:
with self.subTest(name=name):
self.assertEqual(
format_assignment(name, ast.literal_eval(node.value)),
ast.get_source_segment(self.policy, node),
)
def test_config_list_has_one_entry_per_line(self):
self.assertEqual(
format_assignment("CONFIG_LIST", ["avcodec", "libjxl"]),
'CONFIG_LIST = [\n "avcodec",\n "libjxl",\n]',
)
def test_profile_everything_has_one_entry_per_line(self):
self.assertEqual(
format_assignment("PROFILE_EVERYTHING", ["aac_decoder", "libjxl_decoder"]),
'PROFILE_EVERYTHING = [\n "aac_decoder",\n "libjxl_decoder",\n]',
)
def test_unchanged_policy_preserves_comments(self):
values = literal_assignments(self.policy)
expected = self.policy[
self.policy.index("# ---------------------------------------------------------------------------") :
]
self.assertEqual(
policy_definitions(self.policy, set(values["COMPONENT_REGISTRY"])),
expected.rstrip(),
)
class ComponentDefinitionsTest(unittest.TestCase):
def setUp(self):
self.directory = tempfile.TemporaryDirectory()
self.addCleanup(self.directory.cleanup)
self.source = Path(self.directory.name)
declarations = {}
for kind, (filename, c_type, suffix) in DECLARATIONS.items():
if kind == "parser":
c_type = "FFCodecParser"
elif kind == "hwaccel":
c_type = "struct FFHWAccel"
declarations.setdefault(filename, []).append(f"extern const {c_type} ff_sample_{suffix};")
declarations["libavfilter/allfilters.c"] = [
"extern const FFFilter ff_vf_scale;",
"extern const FFFilter ff_af_anull;",
# configure excludes buffer filters by their doubled space.
"extern const FFFilter ff_vsrc_buffer;",
]
for filename, lines in declarations.items():
path = self.source / filename
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("\n".join(lines), encoding="utf-8")
(self.source / "configure").write_text(
'CONFIG_EXTRA="cbs cbs_av1"\n'
'CONFIG_LIST="network pixelutils avutil"\n'
'sample_decoder_select="cbs_av1"\n'
'cbs_av1_select="cbs"\n'
'scale_filter_deps="swscale"\n',
encoding="utf-8",
)
def test_declaration_types_and_device_names(self):
types, symbols = component_declarations(self.source)
self.assertEqual(types["parser"], ["sample_parser"])
self.assertEqual(types["hwaccel"], ["sample_hwaccel"])
self.assertEqual(types["indev"], ["sample_indev"])
self.assertEqual(types["outdev"], ["sample_outdev"])
self.assertEqual(symbols, {"anull_filter": "af_anull", "scale_filter": "vf_scale"})
self.assertNotIn("buffer_filter", types["filter"])
def test_dependency_assignments(self):
actual = configure_dependencies(
'decoder_select="cbs_av1\n itut_t35"\n'
'decoder_deps_any="aom dav1d"\n'
'decoder_suggest="zlib"\n'
'decoder_conflict="other_decoder"\n'
)
self.assertEqual(
actual["decoder"],
{
"select": ["cbs_av1", "itut_t35"],
"deps_any": ["aom", "dav1d"],
"suggest": ["zlib"],
"conflict": ["other_decoder"],
},
)
def test_rejects_unhandled_shell_expansion(self):
with self.assertRaises(ValueError):
configure_dependencies('decoder_select="$OTHER_SELECT"\n')
def test_generation_preserves_policy_and_synthetic_settings(self):
policy = (Path(__file__).parent / "7.1.1.bcr.beta.7/overlay/component_defs.bzl").read_text(
encoding="utf-8",
)
first = generate_definitions(self.source, policy, "7.1.1.bcr.beta.7")
self.assertEqual(first, generate_definitions(self.source, policy, "7.1.1.bcr.beta.7"))
values = literal_assignments(first)
self.assertEqual(values["ALWAYS_AVAILABLE_LIBS"], ["bzlib", "lzma", "threads", "zlib"])
self.assertEqual(values["PROFILE_MINIMAL"], ["anull_filter"])
self.assertEqual(values["MACOS_COMPONENTS"], [])
self.assertEqual(values["LINUX_COMPONENTS"], [])
self.assertEqual(
values["COMPONENT_REGISTRY"]["sample_decoder"],
{
"select": ["cbs_av1"],
"type": "decoder",
},
)
self.assertEqual(values["CONFIG_EXTRA_REGISTRY"]["cbs_av1"], {"select": ["cbs"]})
self.assertTrue(set(SYNTHETIC_COMPONENTS) <= values["CONFIG_EXTRA_REGISTRY"].keys())
self.assertNotIn("exif", values["CONFIG_EXTRA_REGISTRY"])
self.assertIn("def _compute_select_closure():", first)
class ConfigHeadersTest(unittest.TestCase):
def test_suffix_expansion(self):
variables = {
"ARCH_EXT_LIST": "neon clmul",
"HAVE_LIST": "$ARCH_EXT_LIST $(add_suffix _external $ARCH_EXT_LIST)",
}
self.assertEqual(
resolve_list("HAVE_LIST", variables),
[
"neon",
"clmul",
"neon_external",
"clmul_external",
],
)
def test_public_config_uses_target_checks(self):
header = "\n".join(generate_avconfig_h(["bigendian", "fast_unaligned"]))
self.assertIn("#undef AV_HAVE_BIGENDIAN", header)
self.assertIn("#undef AV_HAVE_FAST_UNALIGNED", header)
self.assertNotIn("#define AV_HAVE_", header)
def test_version_uses_package_info(self):
header = "\n".join(generate_ffversion_h())
self.assertIn("#undef PACKAGE_VERSION", header)
self.assertIn("#define FFMPEG_VERSION PACKAGE_VERSION", header)
def test_config_extra_is_not_overridden(self):
derived = [
"dwt",
"error_resilience",
"gplv3",
"libcodec2",
"libdav1d",
"libgsm",
"libjxl",
"libjxl_threads",
"libmp3lame",
"libopenjpeg",
"librav1e",
"libshine",
"libspeex",
"libtwolame",
"libvorbis",
"libvorbisenc",
"libxvid",
"lsp",
"network",
"pixelutils",
"version3",
]
header = "\n".join(
generate_config_h_in(
["aarch64"],
["sve", "sme"],
derived + ["resource_compression", "unstable"],
)
)
for name in derived:
self.assertNotIn(f"#define CONFIG_{name.upper()}", header)
self.assertIn('#include "config_extra.h"', header)
self.assertNotIn("#define FFMPEG_LICENSE", header)
self.assertIn("#define CONFIG_RESOURCE_COMPRESSION 0", header)
self.assertIn("#define CONFIG_UNSTABLE 0", header)
self.assertIn("#define HAVE_SVE 0", header)
self.assertIn("#define HAVE_SME 0", header)
self.assertIn("#define OS_NAME @OS_NAME@", header)
if __name__ == "__main__":
unittest.main()