diff options
Diffstat (limited to 'tests/test_packager_provided_files.py')
-rw-r--r-- | tests/test_packager_provided_files.py | 416 |
1 files changed, 415 insertions, 1 deletions
diff --git a/tests/test_packager_provided_files.py b/tests/test_packager_provided_files.py index b0e075f..caf4bd1 100644 --- a/tests/test_packager_provided_files.py +++ b/tests/test_packager_provided_files.py @@ -1,11 +1,17 @@ +import os import random -from typing import cast, TYPE_CHECKING +from typing import cast, TYPE_CHECKING, Sequence, Optional, Mapping import pytest from debputy.packager_provided_files import detect_all_packager_provided_files from debputy.plugin.api import DebputyPluginInitializer from debputy.plugin.api.feature_set import PluginProvidedFeatureSet +from debputy.plugin.api.impl import plugin_metadata_for_debputys_own_plugin +from debputy.plugin.api.impl_types import ( + PackagerProvidedFileClassSpec, + DebputyPluginMetadata, +) from debputy.plugin.api.test_api import ( InitializedPluginUnderTest, build_virtual_file_system, @@ -13,6 +19,7 @@ from debputy.plugin.api.test_api import ( from debputy.plugin.api.test_api.test_impl import ( initialize_plugin_under_test_preloaded, ) +from lint_tests.lint_tutil import requires_levenshtein from tutil import faked_binary_package, binary_package_table if TYPE_CHECKING: @@ -210,9 +217,416 @@ def test_packager_provided_files_priority( assert expected_basename == matched.path.name +@pytest.mark.parametrize( + "main_package,secondary_package", + [ + ("foo", "foo-doc"), + ("foo.bar", "foo.bar-doc"), + ], +) +def test_detect_ppf_simple(main_package: str, secondary_package: str) -> None: + plugin_metadata = plugin_metadata_for_debputys_own_plugin() + binary_packages = binary_package_table( + faked_binary_package(main_package, is_main_package=True), + faked_binary_package(secondary_package), + ) + debian_dir = build_virtual_file_system( + [ + "install", + f"{secondary_package}.install", + f"{main_package}.docs", + f"{secondary_package}.docs", + "copyright", + ] + ) + ppf_defs = _ppfs( + _fake_PPFClassSpec( + plugin_metadata, + "install", + ), + _fake_PPFClassSpec( + plugin_metadata, + "docs", + ), + _fake_PPFClassSpec( + plugin_metadata, + "copyright", + packageless_is_fallback_for_all_packages=True, + ), + ) + + results = detect_all_packager_provided_files( + ppf_defs, + debian_dir, + binary_packages, + ) + assert main_package in results + main_matches = results[main_package].auto_installable + assert {m.path.name for m in main_matches} == { + "install", + f"{main_package}.docs", + "copyright", + } + + assert secondary_package in results + sec_matches = results[secondary_package].auto_installable + expected = { + f"{secondary_package}.install", + f"{secondary_package}.docs", + "copyright", + } + assert {m.path.name for m in sec_matches} == expected + + +def test_detect_ppf_name_segment() -> None: + plugin_metadata = plugin_metadata_for_debputys_own_plugin() + binary_packages = binary_package_table( + faked_binary_package("foo", is_main_package=True), + faked_binary_package("bar"), + ) + debian_dir = build_virtual_file_system( + [ + "fuu.install", + ] + ) + ppf_defs = _ppfs( + _fake_PPFClassSpec( + plugin_metadata, + "install", + ), + ) + + results = detect_all_packager_provided_files( + ppf_defs, + debian_dir, + binary_packages, + ) + assert "foo" in results + foo_matches = results["foo"].auto_installable + assert {m.path.name for m in foo_matches} == { + "fuu.install", + } + foo_install = foo_matches[0] + assert foo_install.name_segment == "fuu" + assert foo_install.package_name == "foo" + assert foo_install.architecture_restriction is None + assert not foo_install.fuzzy_match + + assert "bar" in results + assert not results["bar"].auto_installable + + ppf_defs = _ppfs( + _fake_PPFClassSpec( + plugin_metadata, + "install", + allow_name_segment=False, + ), + ) + + results = detect_all_packager_provided_files( + ppf_defs, + debian_dir, + binary_packages, + allow_fuzzy_matches=True, + ) + + assert "foo" in results + foo_matches = results["foo"].auto_installable + assert {m.path.name for m in foo_matches} == {"fuu.install"} + foo_install = foo_matches[0] + assert foo_install.name_segment == "fuu" + assert foo_install.package_name == "foo" + assert foo_install.architecture_restriction is None + + assert "bar" in results + assert not results["bar"].auto_installable + + +def test_detect_ppf_fuzzy_match_bug_950723() -> None: + plugin_metadata = plugin_metadata_for_debputys_own_plugin() + binary_packages = binary_package_table( + faked_binary_package("foo", is_main_package=True), + faked_binary_package("bar"), + ) + debian_dir = build_virtual_file_system( + [ + "bar.service", + "foo@.service", + ] + ) + ppf_defs = _ppfs( + _fake_PPFClassSpec( + plugin_metadata, + "service", + bug_950723=True, + ), + ) + + results = detect_all_packager_provided_files( + ppf_defs, + debian_dir, + binary_packages, + ) + assert "foo" in results + foo_matches = results["foo"].auto_installable + assert {m.path.name for m in foo_matches} == {"foo@.service"} + foo_at_service = foo_matches[0] + # Without bug#950723 AND fuzzy_match, it counts a name segment for the main package. + assert foo_at_service.name_segment == "foo@" + assert foo_at_service.package_name == "foo" + assert foo_at_service.architecture_restriction is None + assert not foo_at_service.fuzzy_match + + assert "bar" in results + bar_matches = results["bar"].auto_installable + assert {m.path.name for m in bar_matches} == {"bar.service"} + bar_service = bar_matches[0] + assert bar_service.name_segment is None + assert bar_service.package_name == "bar" + assert bar_service.architecture_restriction is None + assert not bar_service.fuzzy_match + + results = detect_all_packager_provided_files( + ppf_defs, + debian_dir, + binary_packages, + allow_fuzzy_matches=True, + ) + assert "foo" in results + foo_matches = results["foo"].auto_installable + assert {m.path.name for m in foo_matches} == {"foo@.service"} + foo_at_service = foo_matches[0] + assert foo_at_service.name_segment is None + assert foo_at_service.package_name == "foo" + assert foo_at_service.architecture_restriction is None + + assert "bar" in results + bar_matches = results["bar"].auto_installable + assert {m.path.name for m in bar_matches} == {"bar.service"} + bar_service = bar_matches[0] + assert bar_service.name_segment is None + assert bar_service.package_name == "bar" + assert bar_service.architecture_restriction is None + assert not bar_service.fuzzy_match + + +@requires_levenshtein +def test_detect_ppf_typo() -> None: + plugin_metadata = plugin_metadata_for_debputys_own_plugin() + binary_packages = binary_package_table( + faked_binary_package("foo", is_main_package=True), + faked_binary_package("bar"), + ) + debian_dir = build_virtual_file_system( + [ + "fuu.install", + "bar.intsall", + ] + ) + ppf_defs = _ppfs( + _fake_PPFClassSpec( + plugin_metadata, + "install", + ), + _fake_PPFClassSpec( + plugin_metadata, + "logcheck.violations.d", + ), + ) + + results = detect_all_packager_provided_files( + ppf_defs, + debian_dir, + binary_packages, + detect_typos=True, + ) + assert "foo" in results + foo_matches = results["foo"].auto_installable + assert {m.path.name for m in foo_matches} == {"fuu.install"} + foo_logcheck = foo_matches[0] + # Not a typo due to how name segments works with debhelper compat <= 13 + # (but should probably have been one. + assert foo_logcheck.name_segment == "fuu" + assert foo_logcheck.package_name == "foo" + assert foo_logcheck.architecture_restriction is None + + assert "bar" in results + bar_matches = results["bar"].auto_installable + assert {m.path.name for m in bar_matches} == {"bar.intsall"} + bar_logcheck = bar_matches[0] + assert os.path.basename(bar_logcheck.expected_path) == "bar.install" + assert bar_logcheck.name_segment is None + assert bar_logcheck.package_name == "bar" + assert bar_logcheck.architecture_restriction is None + + debian_dir = build_virtual_file_system( + [ + # Typo'ed by intention + "logchcek.violations.d", + "bar.logchcek.violations.d", + ] + ) + + results = detect_all_packager_provided_files( + ppf_defs, + debian_dir, + binary_packages, + detect_typos=True, + ) + + assert "foo" in results + foo_matches = results["foo"].auto_installable + assert {m.path.name for m in foo_matches} == {"logchcek.violations.d"} + foo_logcheck = foo_matches[0] + assert foo_logcheck.name_segment is None + assert foo_logcheck.package_name == "foo" + assert foo_logcheck.architecture_restriction is None + assert os.path.basename(foo_logcheck.expected_path) == "logcheck.violations.d" + + assert "bar" in results + bar_matches = results["bar"].auto_installable + assert {m.path.name for m in bar_matches} == { + "bar.logchcek.violations.d", + } + bar_logcheck = bar_matches[0] + assert bar_logcheck.name_segment is None + assert bar_logcheck.package_name == "bar" + assert bar_logcheck.architecture_restriction is None + assert os.path.basename(bar_logcheck.expected_path) == "bar.logcheck.violations.d" + + +def test_debhelper_overlapping_stems() -> None: + plugin_metadata = plugin_metadata_for_debputys_own_plugin() + binary_packages = binary_package_table( + faked_binary_package("foo", is_main_package=True), + ) + debian_dir = build_virtual_file_system( + [ + "foo.user.service", + ] + ) + ppf_defs = _ppfs( + _fake_PPFClassSpec( + plugin_metadata, + "service", + ), + _fake_PPFClassSpec( + plugin_metadata, + "user.service", + ), + ) + + results = detect_all_packager_provided_files( + ppf_defs, + debian_dir, + binary_packages, + ) + assert "foo" in results + foo_matches = results["foo"].auto_installable + assert {m.path.name for m in foo_matches} == { + "foo.user.service", + } + matched_file = foo_matches[0] + assert matched_file.definition.stem == "user.service" + assert matched_file.name_segment is None + assert matched_file.package_name == "foo" + assert matched_file.architecture_restriction is None + + debian_dir = build_virtual_file_system( + [ + "foo.named.service", + ] + ) + results = detect_all_packager_provided_files( + ppf_defs, + debian_dir, + binary_packages, + ) + assert "foo" in results + foo_matches = results["foo"].auto_installable + assert {m.path.name for m in foo_matches} == {"foo.named.service"} + matched_file = foo_matches[0] + assert matched_file.definition.stem == "service" + assert matched_file.name_segment == "named" + assert matched_file.package_name == "foo" + assert matched_file.architecture_restriction is None + + +@requires_levenshtein +def test_debhelper_overlapping_stems_typo_check() -> None: + plugin_metadata = plugin_metadata_for_debputys_own_plugin() + binary_packages = binary_package_table( + faked_binary_package("foo", is_main_package=True), + ) + debian_dir = build_virtual_file_system( + [ + "foo.uxxr.service", + ] + ) + ppf_defs = _ppfs( + _fake_PPFClassSpec( + plugin_metadata, + "service", + ), + _fake_PPFClassSpec( + plugin_metadata, + "user.service", + ), + ) + results = detect_all_packager_provided_files( + ppf_defs, + debian_dir, + binary_packages, + detect_typos=True, + ) + assert "foo" in results + foo_matches = results["foo"].auto_installable + assert {m.path.name for m in foo_matches} == {"foo.uxxr.service"} + foo_install = foo_matches[0] + # While it could just as well be a named segment, we assume it is a typo. + assert foo_install.definition.stem == "user.service" + assert foo_install.name_segment is None + assert foo_install.package_name == "foo" + assert foo_install.architecture_restriction is None + assert os.path.basename(foo_install.expected_path) == "foo.user.service" + + def _fetch_debputy_plugin_feature_set( plugin: InitializedPluginUnderTest, ) -> PluginProvidedFeatureSet: # Very much not public API, but we need it to avoid testing on production data (also, it is hard to find # relevant debputy files for all the cases we want to test). return cast("InitializedPluginUnderTestImpl", plugin)._feature_set + + +def _fake_PPFClassSpec( + debputy_plugin_metadata: DebputyPluginMetadata, + stem: str, + *, + default_priority: Optional[int] = None, + packageless_is_fallback_for_all_packages: bool = False, + bug_950723: bool = False, + has_active_command: bool = False, + allow_name_segment: bool = True, +) -> PackagerProvidedFileClassSpec: + return PackagerProvidedFileClassSpec( + debputy_plugin_metadata, + stem, + "not-a-real-ppf", + allow_architecture_segment=True, + allow_name_segment=allow_name_segment, + default_priority=default_priority, + default_mode=0o644, + post_formatting_rewrite=None, + packageless_is_fallback_for_all_packages=packageless_is_fallback_for_all_packages, + reservation_only=False, + formatting_callback=None, + bug_950723=bug_950723, + has_active_command=has_active_command, + ) + + +def _ppfs( + *ppfs: PackagerProvidedFileClassSpec, +) -> Mapping[str, PackagerProvidedFileClassSpec]: + return {ppf.stem: ppf for ppf in ppfs} |