Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions tests/test_blacklist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#!/usr/bin/python3

# Blacklist matching in the update list (AptUpdater) and in mintupdate-cli.
# Run with: /usr/bin/python3 -m unittest discover -s tests
# Nothing is read from APT or dconf: packages are stubs and GSettings uses
# the memory backend.

import contextlib
import io
import os
import runpy
import sys
import unittest
from types import SimpleNamespace
from unittest import mock

os.environ["GSETTINGS_BACKEND"] = "memory"
myPath = os.path.dirname(os.path.abspath(__file__))
libPath = os.path.join(myPath, "..", "usr", "lib", "linuxmint", "mintUpdate")
sys.path.insert(0, libPath)

import aptUpdater
from aptUpdater import AptUpdater

def make_package(name, source_name, source_version, version, old_version):
candidate = SimpleNamespace(source_name=source_name, source_version=source_version,
version=version, size=1, raw_description="", description="",
origins=[], section="admin")
return SimpleNamespace(name=name, candidate=candidate, is_installed=True,
installed=SimpleNamespace(version=old_version))

# bsdutils has an epoch, the other util-linux binaries don't
UTIL_LINUX = [
make_package("util-linux", "util-linux", "2.39.3-9ubuntu6.6", "2.39.3-9ubuntu6.6", "2.39.3-9ubuntu6"),
make_package("mount", "util-linux", "2.39.3-9ubuntu6.6", "2.39.3-9ubuntu6.6", "2.39.3-9ubuntu6"),
make_package("bsdutils", "util-linux", "2.39.3-9ubuntu6.6", "1:2.39.3-9ubuntu6.6", "1:2.39.3-9ubuntu6"),
]
# -signed source packages have their own version, unrelated to the binary one
GRUB_SIGNED = [
make_package("grub-efi-amd64-signed", "grub2-signed", "1.202.5", "1.202.5+2.12-1ubuntu7.3", "1.202+2.12-1ubuntu7"),
]
PACKAGES = UTIL_LINUX + GRUB_SIGNED

class FakeSettings():

def __init__(self, blacklist):
self.blacklist = blacklist

def get_strv(self, key):
return list(self.blacklist)

class FakeAptUpdater(AptUpdater):
# Real AptUpdater.add_update() on stub packages, with an empty GUI blacklist
# unless one is given

gui_blacklist = []

def __init__(self, ui_window=None):
super().__init__(ui_window)
self.settings = FakeSettings(self.gui_blacklist)

def load_cache(self):
self.cache = {package.name: package for package in PACKAGES}

def find_changes(self):
self.updates = {}
for package in PACKAGES:
self.add_update(package)

def listed_updates(gui_blacklist):
FakeAptUpdater.gui_blacklist = gui_blacklist
updater = FakeAptUpdater()
updater.load_cache()
updater.find_changes()
return {source_name: sorted(update.package_names) for source_name, update in updater.updates.items()}

def run_cli(*args):
# Runs mintupdate-cli.py as a script, on the stub packages, without /etc/mintupdate.blacklist
real_exists = os.path.exists
def exists(path):
return False if path == "/etc/mintupdate.blacklist" else real_exists(path)
FakeAptUpdater.gui_blacklist = []
output = io.StringIO()
with mock.patch.object(aptUpdater, "AptUpdater", FakeAptUpdater), \
mock.patch("os.path.exists", exists), \
mock.patch.object(sys, "argv", ["mintupdate-cli"] + list(args)), \
contextlib.redirect_stdout(output):
runpy.run_path(os.path.join(libPath, "mintupdate-cli.py"), run_name="__main__")
return [line.split()[1] for line in output.getvalue().splitlines()]

class TestUpdateList(unittest.TestCase):

def test_nothing_ignored(self):
self.assertEqual(listed_updates([]), {
"util-linux": ["bsdutils", "mount", "util-linux"],
"grub2-signed": ["grub-efi-amd64-signed"],
})

def test_ignore_current_update_epoch(self):
# "Ignore the current update" stores source=source_version
self.assertNotIn("util-linux", listed_updates(["util-linux=2.39.3-9ubuntu6.6"]))

def test_ignore_current_update_signed(self):
self.assertNotIn("grub2-signed", listed_updates(["grub2-signed=1.202.5"]))

def test_ignore_binary_version(self):
# A version typed in the preferences, as shown in the update list
self.assertNotIn("grub2-signed", listed_updates(["grub2-signed=1.202.5+2.12-1ubuntu7.3"]))

def test_ignore_all_versions_pattern(self):
self.assertNotIn("grub2-signed", listed_updates(["grub2-*"]))

def test_other_version_not_ignored(self):
self.assertEqual(listed_updates(["util-linux=2.39.3-9ubuntu6", "grub2-signed=1.202"]),
listed_updates([]))

class TestCli(unittest.TestCase):

def test_nothing_ignored(self):
self.assertEqual(run_cli("list"), ["grub2-signed", "util-linux"])

def test_ignore_current_update_epoch(self):
self.assertEqual(run_cli("list", "-i", "util-linux=2.39.3-9ubuntu6.6"), ["grub2-signed"])

def test_ignore_current_update_signed(self):
self.assertEqual(run_cli("list", "-i", "grub2-signed=1.202.5"), ["util-linux"])

def test_ignore_binary_version(self):
self.assertEqual(run_cli("list", "-i", "grub2-signed=1.202.5+2.12-1ubuntu7.3"), ["util-linux"])

def test_ignore_all_versions_pattern(self):
self.assertEqual(run_cli("list", "-i", "grub2-*,util-*"), [])

def test_other_version_not_ignored(self):
self.assertEqual(run_cli("list", "-i", "util-linux=2.39.3-9ubuntu6,grub2-signed=1.202"),
["grub2-signed", "util-linux"])

if __name__ == "__main__":
unittest.main()
30 changes: 18 additions & 12 deletions usr/lib/linuxmint/mintUpdate/aptUpdater.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,23 @@
# packages which description is incorrect in Ubuntu (usually those which were replaced by snap dependencies)
NON_TRANSLATED_PKGS = ["firefox", "thunderbird"]

def is_blacklisted(blacklist, candidate):
# Blacklist entries are "source" or "source=version", where source can be
# a pattern. "Ignore the current update" stores the source version, while
# a version typed in the preferences is usually the binary version shown
# in the update list. Both are accepted: they differ for epochs, -signed
# packages and binNMUs.
for blacklist_entry in blacklist:
if "=" in blacklist_entry:
(bl_pkg, bl_ver) = blacklist_entry.split("=", 1)
else:
bl_pkg = blacklist_entry
bl_ver = None
if fnmatch.fnmatch(candidate.source_name, bl_pkg) and \
(not bl_ver or bl_ver in (candidate.source_version, candidate.version)):
return True
return False

class AptUpdater():

def __init__(self, ui_window=None):
Expand Down Expand Up @@ -224,9 +241,9 @@
with open("/usr/lib/linuxmint/mintUpdate/aliases") as alias_file:
for line in alias_file:
if not line.startswith('#'):
splitted = line.split("#####")

Check failure on line 244 in usr/lib/linuxmint/mintUpdate/aptUpdater.py

View workflow job for this annotation

GitHub Actions / build / build (mint22, linuxmintd/mint22.3-amd64, Mint 22, true) / Mint 22

splitted ==> split
if len(splitted) == 4:

Check failure on line 245 in usr/lib/linuxmint/mintUpdate/aptUpdater.py

View workflow job for this annotation

GitHub Actions / build / build (mint22, linuxmintd/mint22.3-amd64, Mint 22, true) / Mint 22

splitted ==> split
(alias_packages, alias_name, alias_short_description, alias_description) = splitted

Check failure on line 246 in usr/lib/linuxmint/mintUpdate/aptUpdater.py

View workflow job for this annotation

GitHub Actions / build / build (mint22, linuxmintd/mint22.3-amd64, Mint 22, true) / Mint 22

splitted ==> split
alias_object = Alias(alias_name, alias_short_description, alias_description)
for alias_package in alias_packages.split(','):
alias_package = alias_package.strip()
Expand All @@ -250,17 +267,6 @@
if (pkg.marked_upgrade or pkg.marked_downgrade):
self.add_update(pkg)

def is_blacklisted(self, source_name, version):
for blacklist in self.settings.get_strv("blacklisted-packages"):
if "=" in blacklist:
(bl_pkg, bl_ver) = blacklist.split("=", 1)
else:
bl_pkg = blacklist
bl_ver = None
if fnmatch.fnmatch(source_name, bl_pkg) and (not bl_ver or bl_ver == version):
return True
return False

def get_kernel_version_from_meta_package(self, pkg):
for dependency in pkg.dependencies:
if not dependency.target_versions or dependency.rawtype != "Depends":
Expand Down Expand Up @@ -300,7 +306,7 @@
source_name = package.candidate.source_name

# ignore packages blacklisted by the user
if self.is_blacklisted(package.candidate.source_name, package.candidate.version):
if is_blacklisted(self.settings.get_strv("blacklisted-packages"), package.candidate):
return

if source_name in PRIORITY_UPDATES:
Expand Down
17 changes: 3 additions & 14 deletions usr/lib/linuxmint/mintUpdate/mintupdate-cli.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
#!/usr/bin/python3

import argparse
import fnmatch
import os
import subprocess
import sys
import traceback

from aptUpdater import AptUpdater
from aptUpdater import AptUpdater, is_blacklisted
from Classes import PRIORITY_UPDATES

if __name__ == "__main__":

def is_blacklisted(blacklisted_packages, source_name, version):
for blacklist in blacklisted_packages:
if "=" in blacklist:
(bl_pkg, bl_ver) = blacklist.split("=", 1)
else:
bl_pkg = blacklist
bl_ver = None
if fnmatch.fnmatch(source_name, bl_pkg) and (not bl_ver or bl_ver == version):
return True
return False

parser = argparse.ArgumentParser(prog="mintupdate-cli")
parser.add_argument("command", help="command to run (possible commands are: list, upgrade)")

Expand Down Expand Up @@ -65,7 +53,8 @@ def is_blacklisted(blacklisted_packages, source_name, version):
continue
elif args.only_security and update.type != "security":
continue
elif is_blacklisted(blacklisted, update.real_source_name, update.new_version):
elif any(is_blacklisted(blacklisted, check.cache[name].candidate) for name in update.package_names):
# skip the whole update, not just the matching packages
continue
else:
updates.append(update)
Expand Down
Loading