Files
computer-playbook/tests/unit/python/utils/packages/test_installed.py
T
kevinveenbirkenbachandClaude Opus 5.5 5f85f382e5 perf(packages): skip the package manager when its database already shows the state
The apt module loads its whole cache before it finds that every name is installed, about 2.4 s per call; an update deploy spent ~28 s on twelve such no-op calls from roles outside the batch. package_install now asks dpkg-query, pacman --query or rpm --query under LC_ALL=C first and returns unchanged only when every plain name is proven to be in the requested state; anything it cannot read in full runs the module as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-29 01:28:46 +01:00

208 lines
8.2 KiB
Python

import unittest
from utils.packages.calls import (
GENERIC_PACKAGE,
STATE_ABSENT,
STATE_PRESENT,
ModuleCall,
package_call,
)
from utils.packages.installed import query_argv, state_holds
def _holds(pkg_mgr, names, state, rc, stdout="", stderr="", **extra):
result = {"rc": rc, "stdout": stdout, "stderr": stderr, **extra}
return state_holds(package_call(names, state), pkg_mgr, result)
def _present(pkg_mgr, names, rc, stdout="", stderr=""):
return _holds(pkg_mgr, names, STATE_PRESENT, rc, stdout, stderr)
def _absent(pkg_mgr, names, rc, stdout="", stderr=""):
return _holds(pkg_mgr, names, STATE_ABSENT, rc, stdout, stderr)
class TestQueryArgv(unittest.TestCase):
def test_apt_asks_dpkg_for_every_name_in_the_c_locale(self):
argv = query_argv(package_call(["git", "curl"], STATE_PRESENT), "apt")
self.assertEqual(argv[:3], ["env", "LC_ALL=C", "dpkg-query"])
self.assertEqual(argv[-3:], ["--", "git", "curl"])
def test_pacman_asks_the_local_database(self):
argv = query_argv(package_call(["git"], STATE_ABSENT), "pacman")
self.assertEqual(argv[2:5], ["pacman", "--query", "--"])
def test_every_rpm_manager_asks_rpm(self):
for pkg_mgr in ("dnf", "dnf5", "yum"):
with self.subTest(pkg_mgr=pkg_mgr):
argv = query_argv(package_call(["git"], STATE_PRESENT), pkg_mgr)
self.assertEqual(argv[2], "rpm")
self.assertEqual(argv[-1], "git")
def test_an_unknown_manager_is_not_queried(self):
self.assertIsNone(query_argv(package_call(["git"], STATE_PRESENT), "zypper"))
def test_a_name_that_is_not_plain_is_not_queried(self):
for name in (
"nginx=1.24",
"libc6:i386",
"/tmp/pkg.deb",
"https://example.org/pkg.rpm",
"python3-*",
"-x",
"",
):
with self.subTest(name=name):
call = package_call(["git", name], STATE_PRESENT)
self.assertIsNone(query_argv(call, "apt"))
def test_a_concrete_module_is_not_queried(self):
call = ModuleCall(
"community.general.pacman", {"name": ["git"], "state": "present"}
)
self.assertIsNone(query_argv(call, "pacman"))
def test_a_call_under_another_user_is_not_queried(self):
call = ModuleCall(
GENERIC_PACKAGE, {"name": ["git"], "state": "present"}, become_user="b"
)
self.assertIsNone(query_argv(call, "pacman"))
def test_a_call_with_further_arguments_is_not_queried(self):
call = ModuleCall(
GENERIC_PACKAGE,
{"name": ["git"], "state": "present", "update_cache": True},
)
self.assertIsNone(query_argv(call, "apt"))
def test_an_empty_name_list_is_not_queried(self):
self.assertIsNone(query_argv(package_call([], STATE_PRESENT), "apt"))
class TestDpkg(unittest.TestCase):
def test_every_name_installed_holds_present(self):
self.assertTrue(_present("apt", ["git", "curl"], 0, "git\tii \ncurl\tii "))
def test_a_held_package_counts_as_installed(self):
self.assertTrue(_present("apt", ["git"], 0, "git\thi "))
def test_a_trimmed_last_line_still_parses(self):
self.assertTrue(_present("apt", ["git"], 0, "git\tii"))
def test_one_unknown_name_breaks_present(self):
stderr = "dpkg-query: no packages found matching curl"
self.assertFalse(_present("apt", ["git", "curl"], 1, "git\tii ", stderr))
def test_one_installed_name_breaks_absent(self):
stderr = "dpkg-query: no packages found matching curl"
self.assertFalse(_absent("apt", ["git", "curl"], 1, "git\tii ", stderr))
def test_unknown_and_config_files_only_hold_absent(self):
stderr = "dpkg-query: no packages found matching postfix-lmdb"
self.assertTrue(
_absent("apt", ["postfix", "postfix-lmdb"], 1, "postfix\trc ", stderr)
)
def test_config_files_only_breaks_present(self):
self.assertFalse(_present("apt", ["postfix"], 0, "postfix\trc "))
def test_a_half_installed_package_holds_neither_state(self):
self.assertFalse(_present("apt", ["git"], 0, "git\tiU "))
self.assertFalse(_absent("apt", ["git"], 0, "git\tiU "))
def test_a_package_that_needs_reinstalling_is_not_installed(self):
self.assertFalse(_present("apt", ["git"], 0, "git\tiiR"))
def test_every_architecture_must_be_installed(self):
self.assertTrue(_present("apt", ["libc6"], 0, "libc6\tii \nlibc6\tii "))
self.assertFalse(_present("apt", ["libc6"], 0, "libc6\tii \nlibc6\trc "))
self.assertFalse(_absent("apt", ["libc6"], 0, "libc6\tii \nlibc6\trc "))
class TestPacman(unittest.TestCase):
def test_every_name_installed_holds_present(self):
self.assertTrue(
_present("pacman", ["git", "curl"], 0, "git 2.51-1\ncurl 8.16-1")
)
def test_one_missing_name_breaks_present(self):
stderr = "error: package 'curl' was not found"
self.assertFalse(_present("pacman", ["git", "curl"], 1, "git 2.51-1", stderr))
def test_no_name_installed_holds_absent(self):
stderr = (
"error: package 'postfix' was not found\n"
"error: package 'postfix-lmdb' was not found"
)
self.assertTrue(_absent("pacman", ["postfix", "postfix-lmdb"], 1, "", stderr))
def test_one_installed_name_breaks_absent(self):
stderr = "error: package 'postfix-lmdb' was not found"
self.assertFalse(
_absent("pacman", ["postfix", "postfix-lmdb"], 1, "postfix 3.11-2", stderr)
)
def test_a_name_answered_by_its_provider_proves_nothing(self):
self.assertFalse(_present("pacman", ["sh"], 0, "bash 5.3-1"))
self.assertFalse(_absent("pacman", ["sh"], 0, "bash 5.3-1"))
class TestRpm(unittest.TestCase):
def test_every_name_installed_holds_present(self):
self.assertTrue(_present("dnf", ["git", "curl"], 0, "git\ncurl"))
def test_one_missing_name_holds_neither_state(self):
stdout = "git\npackage curl is not installed"
self.assertFalse(_present("dnf5", ["git", "curl"], 1, stdout))
self.assertFalse(_absent("dnf5", ["git", "curl"], 1, stdout))
def test_no_name_installed_holds_absent(self):
stdout = (
"package postfix is not installed\npackage postfix-lmdb is not installed"
)
self.assertTrue(_absent("yum", ["postfix", "postfix-lmdb"], 2, stdout))
def test_the_not_installed_message_may_come_on_stderr(self):
stderr = "package postfix is not installed"
self.assertTrue(_absent("dnf", ["postfix"], 1, "", stderr))
def test_a_version_match_under_another_name_proves_nothing(self):
self.assertFalse(_present("dnf", ["foo-1.0"], 0, "foo"))
def test_a_translated_message_proves_nothing(self):
stdout = "Paket postfix ist nicht installiert"
self.assertFalse(_absent("dnf", ["postfix"], 1, stdout))
class TestDoubtRunsTheModule(unittest.TestCase):
def test_a_check_mode_skip_proves_nothing(self):
self.assertFalse(_holds("apt", ["git"], STATE_ABSENT, 0, skipped=True))
def test_a_failure_without_a_not_found_message_proves_nothing(self):
self.assertFalse(_absent("apt", ["git"], 1))
self.assertFalse(_absent("pacman", ["git"], 1))
self.assertFalse(_absent("dnf", ["git"], 1))
def test_success_with_a_not_found_message_proves_nothing(self):
stderr = "dpkg-query: no packages found matching git"
self.assertFalse(_absent("apt", ["git"], 0, "", stderr))
def test_a_name_left_unanswered_proves_nothing(self):
self.assertFalse(_present("apt", ["git", "curl"], 0, "git\tii "))
def test_a_foreign_stderr_line_proves_nothing(self):
stderr = "dpkg-query: error: parsing file '/var/lib/dpkg/status'"
self.assertFalse(_present("apt", ["git"], 0, "git\tii ", stderr))
def test_a_foreign_stdout_line_proves_nothing(self):
self.assertFalse(_present("apt", ["git"], 0, "git\tii \ncurl\tii "))
def test_a_result_without_output_proves_nothing(self):
call = package_call(["git"], STATE_ABSENT)
self.assertFalse(state_holds(call, "apt", {"failed": True, "msg": "boom"}))
if __name__ == "__main__":
unittest.main()