astra / tests /test_apt_cache.py
Lorenzob's picture
Upload folder using huggingface_hub
859a779 verified
raw
history blame
15.9 kB
#!/usr/bin/python3
#
# Copyright (C) 2010 Julian Andres Klode <[email protected]>
# 2010 Michael Vogt <[email protected]>
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.
"""Unit tests for verifying the correctness of check_dep, etc in apt_pkg."""
from __future__ import print_function
import glob
import logging
import os
import shutil
import sys
import tempfile
import unittest
from test_all import get_library_dir
libdir = get_library_dir()
if libdir:
sys.path.insert(0, libdir)
import apt
import apt_pkg
import testcommon
def if_sources_list_is_readable(f):
def wrapper(*args, **kwargs):
if os.access("/etc/apt/sources.list", os.R_OK):
f(*args, **kwargs)
else:
logging.warning(
"skipping '%s' because sources.list is not readable" % f)
return wrapper
def get_open_file_descriptors():
try:
fds = os.listdir("/proc/self/fd")
except OSError:
logging.warning("failed to list /proc/self/fd")
return set([])
return set(map(int, fds))
class TestAptCache(testcommon.TestCase):
""" test the apt cache """
def setUp(self):
testcommon.TestCase.setUp(self)
apt_pkg.config.clear("APT::Update::Post-Invoke")
apt_pkg.config.clear("APT::Update::Post-Invoke-Success")
@if_sources_list_is_readable
def test_apt_cache(self):
"""cache: iterate all packages and all dependencies """
cache = apt.Cache()
# number is not meaningful and just need to be "big enough",
# the important bit is the test against __len__
self.assertTrue(len(cache) > 100)
# go over the cache and all dependencies, just to see if
# that is possible and does not crash
for pkg in cache:
if pkg.candidate:
for or_deps in pkg.candidate.dependencies:
for dep in or_deps:
self.assertTrue(dep.name)
self.assertTrue(isinstance(dep.relation, str))
self.assertTrue(dep.pre_depend in (True, False))
# accessing record should take a reasonable time; in
# particular, when using compressed indexes, it should not use
# tons of seek operations
r = pkg.candidate.record
self.assertEqual(r['Package'], pkg.shortname)
self.assertTrue('Version' in r)
self.assertTrue(len(r['Description']) > 0)
self.assertTrue(
str(r).startswith('Package: %s\n' % pkg.shortname))
@if_sources_list_is_readable
def test_cache_close_leak_fd(self):
fds_before_open = get_open_file_descriptors()
cache = apt.Cache()
opened_fd = get_open_file_descriptors().difference(fds_before_open)
cache.close()
fds_after_close = get_open_file_descriptors()
unclosed_fd = opened_fd.intersection(fds_after_close)
self.assertEqual(fds_before_open, fds_after_close)
self.assertEqual(unclosed_fd, set())
def test_cache_open_twice_leaks_fds(self):
cache = apt.Cache()
fds_before_open = get_open_file_descriptors()
cache.open()
fds_after_open_twice = get_open_file_descriptors()
self.assertEqual(fds_before_open, fds_after_open_twice)
@if_sources_list_is_readable
def test_cache_close_download_fails(self):
cache = apt.Cache()
self.assertEqual(cache.required_download, 0)
cache.close()
with self.assertRaises(apt.cache.CacheClosedException):
cache.required_download
def test_get_provided_packages(self):
apt.apt_pkg.config.set("Apt::architecture", "i386")
cache = apt.Cache(rootdir="./data/test-provides/")
cache.open()
if len(cache) == 0:
logging.warning(
"skipping test_get_provided_packages, cache empty?!?")
return
# a true virtual pkg
li = cache.get_providing_packages("mail-transport-agent")
self.assertTrue(len(li) > 0)
self.assertTrue("postfix" in [p.name for p in li])
self.assertTrue(
"mail-transport-agent" in cache["postfix"].candidate.provides)
def test_low_level_pkg_provides(self):
apt.apt_pkg.config.set("Apt::architecture", "i386")
# create highlevel cache and get the lowlevel one from it
highlevel_cache = apt.Cache(rootdir="./data/test-provides")
if len(highlevel_cache) == 0:
logging.warning(
"skipping test_log_level_pkg_provides, cache empty?!?")
return
# low level cache provides list of the pkg
cache = highlevel_cache._cache
li = cache["mail-transport-agent"].provides_list
# arbitrary number, just needs to be higher enough
self.assertEqual(len(li), 2)
for (providesname, providesver, version) in li:
self.assertEqual(providesname, "mail-transport-agent")
if version.parent_pkg.name == "postfix":
break
else:
self.assertNotReached()
@if_sources_list_is_readable
def test_dpkg_journal_dirty(self):
# create tmp env
tmpdir = tempfile.mkdtemp()
dpkg_dir = os.path.join(tmpdir, "var", "lib", "dpkg")
os.makedirs(os.path.join(dpkg_dir, "updates"))
open(os.path.join(dpkg_dir, "status"), "w").close()
apt_pkg.config.set("Dir::State::status",
os.path.join(dpkg_dir, "status"))
cache = apt.Cache()
# test empty
self.assertFalse(cache.dpkg_journal_dirty)
# that is ok, only [0-9] are dpkg jounral entries
open(os.path.join(dpkg_dir, "updates", "xxx"), "w").close()
self.assertFalse(cache.dpkg_journal_dirty)
# that is a dirty journal
open(os.path.join(dpkg_dir, "updates", "000"), "w").close()
self.assertTrue(cache.dpkg_journal_dirty)
@if_sources_list_is_readable
def test_apt_update(self):
rootdir = "./data/tmp"
if os.path.exists(rootdir):
shutil.rmtree(rootdir)
try:
os.makedirs(os.path.join(rootdir, "var/lib/apt/lists/partial"))
except OSError:
pass
state_dir = os.path.join(rootdir, "var/lib/apt")
lists_dir = os.path.join(rootdir, "var/lib/apt/lists")
old_state = apt_pkg.config.find("dir::state")
apt_pkg.config.set("dir::state", state_dir)
# set a local sources.list that does not need the network
base_sources = os.path.abspath(os.path.join(rootdir, "sources.list"))
old_source_list = apt_pkg.config.find("dir::etc::sourcelist")
old_source_parts = apt_pkg.config.find("dir::etc::sourceparts")
apt_pkg.config.set("dir::etc::sourcelist", base_sources)
# TODO: /dev/null is not a dir, perhaps find something better
apt_pkg.config.set("dir::etc::sourceparts", "/dev/null")
# main sources.list
sources_list = base_sources
with open(sources_list, "w") as f:
repo = os.path.abspath("./data/test-repo2")
f.write("deb [allow-insecure=yes] copy:%s /\n" % repo)
# test single sources.list fetching
sources_list = os.path.join(rootdir, "test.list")
with open(sources_list, "w") as f:
repo_dir = os.path.abspath("./data/test-repo")
f.write("deb [allow-insecure=yes] copy:%s /\n" % repo_dir)
self.assertTrue(os.path.exists(sources_list))
# write marker to ensure listcleaner is not run
open("./data/tmp/var/lib/apt/lists/marker", "w").close()
# update a single sources.list
cache = apt.Cache()
cache.update(sources_list=sources_list)
# verify we just got the excpected package file
needle_packages = glob.glob(
lists_dir + "/*tests_data_test-repo_Packages*")
self.assertEqual(len(needle_packages), 1)
# verify that we *only* got the Packages file from a single source
all_packages = glob.glob(lists_dir + "/*_Packages*")
self.assertEqual(needle_packages, all_packages)
# verify that the listcleaner was not run and the marker file is
# still there
self.assertTrue("marker" in os.listdir(lists_dir))
# now run update again (without the "normal" sources.list that
# contains test-repo2 and verify that we got the normal sources.list
cache.update()
needle_packages = glob.glob(
lists_dir + "/*tests_data_test-repo2_Packages*")
self.assertEqual(len(needle_packages), 1)
all_packages = glob.glob(lists_dir + "/*_Packages*")
self.assertEqual(needle_packages, all_packages)
# and another update with a single source only
cache = apt.Cache()
cache.update(sources_list=sources_list)
all_packages = glob.glob(lists_dir + "/*_Packages*")
self.assertEqual(len(all_packages), 2)
apt_pkg.config.set("dir::state", old_state)
apt_pkg.config.set("dir::etc::sourcelist", old_source_list)
apt_pkg.config.set("dir::etc::sourceparts", old_source_parts)
def test_package_cmp(self):
cache = apt.Cache(rootdir="/")
li = []
li.append(cache["intltool"])
li.append(cache["python3"])
li.append(cache["apt"])
li.sort()
self.assertEqual([p.name for p in li],
["apt", "intltool", "python3"])
def test_get_architectures(self):
main_arch = apt.apt_pkg.config.get("APT::Architecture")
arches = apt_pkg.get_architectures()
self.assertTrue(main_arch in arches)
def test_apt_cache_reopen_is_safe(self):
"""cache: check that we cannot use old package objects after reopen"""
cache = apt.Cache()
old_depcache = cache._depcache
old_package = cache["apt"]
old_pkg = old_package._pkg
old_version = old_package.candidate
old_ver = old_version._cand
cache.open()
new_depcache = cache._depcache
new_package = cache["apt"]
new_pkg = new_package._pkg
new_version = new_package.candidate
new_ver = new_version._cand
# get candidate
self.assertRaises(ValueError, old_depcache.get_candidate_ver,
new_pkg)
self.assertRaises(ValueError, new_depcache.get_candidate_ver,
old_pkg)
self.assertEqual(new_ver, new_depcache.get_candidate_ver(new_pkg))
self.assertEqual(old_package.candidate._cand, old_ver) # Remap success
self.assertEqual(old_package.candidate._cand,
new_depcache.get_candidate_ver(new_pkg))
# set candidate
new_package.candidate = old_version
old_depcache.set_candidate_ver(old_pkg, old_ver)
self.assertRaises(ValueError, old_depcache.set_candidate_ver,
old_pkg, new_ver)
self.assertRaises(ValueError, new_depcache.set_candidate_ver,
old_pkg, old_ver)
self.assertRaises(ValueError, new_depcache.set_candidate_ver,
old_pkg, new_ver)
self.assertRaises(ValueError, new_depcache.set_candidate_ver,
new_pkg, old_ver)
new_depcache.set_candidate_ver(new_pkg, new_ver)
@staticmethod
def write_status_file(packages):
with open(apt_pkg.config["Dir::State::Status"], "w") as fobj:
for package in packages:
print("Package:", package, file=fobj)
print("Status: install ok installed", file=fobj)
print("Priority: optional", file=fobj)
print("Section: admin", file=fobj)
print("Installed-Size: 1", file=fobj)
print("Maintainer: X <[email protected]>", file=fobj)
print("Architecture: all", file=fobj)
print("Version: 1", file=fobj)
print("Description: blah", file=fobj)
print("", file=fobj)
def test_apt_cache_reopen_is_safe_out_of_bounds(self):
"""Check that out of bounds access is remapped correctly."""
with tempfile.NamedTemporaryFile() as status:
apt_pkg.config["Dir::Etc::SourceList"] = "/dev/null"
apt_pkg.config["Dir::Etc::SourceParts"] = "/dev/null"
apt_pkg.config["Dir::State::Status"] = status.name
apt_pkg.init_system()
self.write_status_file("abcdefghijklmnopqrstuvwxyz")
c = apt.Cache()
p = c["z"]
p_id = p.id
self.write_status_file("az")
apt_pkg.init_system()
c.open()
self.assertNotEqual(p.id, p_id)
self.assertLess(p.id, 2)
p.mark_delete()
self.assertEqual([p], c.get_changes())
def test_apt_cache_reopen_is_safe_out_of_bounds_no_match(self):
"""Check that installing gone package raises correct exception"""
with tempfile.NamedTemporaryFile() as status:
apt_pkg.config["Dir::Etc::SourceList"] = "/dev/null"
apt_pkg.config["Dir::Etc::SourceParts"] = "/dev/null"
apt_pkg.config["Dir::State::Status"] = status.name
apt_pkg.init_system()
self.write_status_file("abcdefghijklmnopqrstuvwxyz")
c = apt.Cache()
p = c["z"]
p_id = p.id
self.write_status_file("a")
apt_pkg.init_system()
c.open()
self.assertEqual(p.id, p_id) # Could not be remapped
self.assertRaises(apt_pkg.CacheMismatchError, p.mark_delete)
def test_apt_cache_reopen_is_safe_swap(self):
"""Check that swapping a and b does not mark the wrong package."""
with tempfile.NamedTemporaryFile() as status:
apt_pkg.config["Dir::Etc::SourceList"] = "/dev/null"
apt_pkg.config["Dir::Etc::SourceParts"] = "/dev/null"
apt_pkg.config["Dir::State::Status"] = status.name
apt_pkg.init_system()
self.write_status_file("abcdefghijklmnopqrstuvwxyz")
c = apt.Cache()
p = c["a"]
a_id = p.id
p_hash = hash(p)
set_of_p = set([p])
self.write_status_file("baz")
apt_pkg.init_system()
c.open()
# b now has the same id as a in the old cache
self.assertEqual(c["b"].id, a_id)
self.assertNotEqual(p.id, a_id)
# Marking a should still mark a, not b.
p.mark_delete()
self.assertEqual([p], c.get_changes())
# Ensure that p can still be found in a set of it, as a test
# for bug https://bugs.launchpad.net/bugs/1780099
self.assertEqual(hash(p), p_hash)
self.assertIn(p, set_of_p)
def test_apt_cache_iteration_safe(self):
"""Check that iterating does not produce different results.
This failed in 1.7.0~alpha2, because one part of the code
looked up packages in the weak dict using the pretty name,
and the other using the full name."""
with tempfile.NamedTemporaryFile() as status:
apt_pkg.config["Dir::Etc::SourceList"] = "/dev/null"
apt_pkg.config["Dir::Etc::SourceParts"] = "/dev/null"
apt_pkg.config["Dir::State::Status"] = status.name
apt_pkg.init_system()
self.write_status_file("abcdefghijklmnopqrstuvwxyz")
c = apt.Cache()
c["a"].mark_delete()
self.assertEqual([c["a"]], [p for p in c if p.marked_delete])
if __name__ == "__main__":
unittest.main()