Release 0.10.7-bearhub.6

Complete M1 identity work, fix runtime URL regressions, repair the test
suite, add CI, migrate packaging/AppImage branding to Bearhub, and
document the Qt6/PySide6 migration plan.
This commit is contained in:
Sebastian Palencsar
2026-06-26 19:22:28 +02:00
parent 63698709a6
commit 4a119968bc
35 changed files with 1984 additions and 224 deletions

View File

@@ -3,7 +3,7 @@ import warnings
from unittest import TestCase
from unittest.mock import patch, Mock
from bauh import __app_name__
from bauh.gems.arch import pacman
FILE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -29,7 +29,7 @@ class PacmanTest(TestCase):
self.assertIsNotNone(ignored)
self.assertEqual(0, len(ignored))
@patch(f'{__app_name__}.gems.arch.pacman.run_cmd', return_value="""
@patch("bauh.gems.arch.pacman.run_cmd", return_value="""
Name : package-test
Version : 3.4.4-1
Description : Test
@@ -42,7 +42,7 @@ Required By : None
run_cmd.assert_called_once_with('pacman -Qi package-test')
self.assertEqual({'package-test': {}}, res)
@patch(f'{__app_name__}.gems.arch.pacman.run_cmd', return_value="""
@patch("bauh.gems.arch.pacman.run_cmd", return_value="""
Name : package-test
Version : 3.4.4-1
Description : Test
@@ -55,7 +55,7 @@ Required By : None
run_cmd.assert_called_once_with('pacman -Qi package-test')
self.assertEqual({'package-test': {'lib32-vulkan-icd-loader': 'Vulkan support'}}, res)
@patch(f'{__app_name__}.gems.arch.pacman.run_cmd', return_value="""
@patch("bauh.gems.arch.pacman.run_cmd", return_value="""
Name : package-test
Version : 3.4.4-1
Description : Test
@@ -68,7 +68,7 @@ Required By : None
run_cmd.assert_called_once_with('pacman -Qi package-test')
self.assertEqual({'package-test': {'pipewire-alsa': ''}}, res)
@patch(f'{__app_name__}.gems.arch.pacman.run_cmd', return_value="""
@patch("bauh.gems.arch.pacman.run_cmd", return_value="""
Name : package-test
Version : 3.4.4-1
Description : Test
@@ -81,7 +81,7 @@ Required By : None
run_cmd.assert_called_once_with('pacman -Qi package-test')
self.assertEqual({'package-test': {}}, res)
@patch(f'{__app_name__}.gems.arch.pacman.run_cmd', return_value="""
@patch("bauh.gems.arch.pacman.run_cmd", return_value="""
Name : package-test
Version : 3.4.4-1
Description : Test

View File

@@ -1,7 +1,6 @@
from unittest import TestCase
from unittest.mock import patch, Mock, MagicMock
from bauh import __app_name__
from bauh.api.abstract.controller import UpgradeRequirement
from bauh.gems.arch.dependencies import DependenciesAnalyser
from bauh.gems.arch.model import ArchPackage
@@ -28,7 +27,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
"prefer_repository_provider": True,
"check_dependency_breakage": True}
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__should_not_return_installed_to_remove_when_conflict_with_installed_version_fails(self, pacman: Mock):
"""
If the newest version o package A conflicts with an installed package named B, but the conflict expression
@@ -76,7 +75,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertFalse(res.to_install)
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__should_return_installed_to_remove_when_conflict_with_installed_version_matches(self, pacman: Mock):
"""
If the newest version o package A conflicts with an installed package named B and the conflict expression
@@ -130,7 +129,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="B", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__should_return_installed_to_remove_when_conflict_with_installed_matches(self, pacman: Mock):
"""
If the newest version o package A conflicts with an installed package named B,
@@ -184,7 +183,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="B", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__should_not_return_installed_to_remove_when_conflict_with_is_self_conflict(self, pacman: Mock):
"""
If the newest version o package A conflicts with itself, then A should not be marked as a package to be removed.
@@ -226,7 +225,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertFalse(res.to_install)
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__should_not_return_installed_to_remove_when_conflict_with_provided_version_fails(self, pacman: Mock):
"""
If the newest version o package A conflicts with a provided package C (by installed package B),
@@ -273,7 +272,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertFalse(res.to_install)
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__not_return_to_remove_when_conflict_with_provided_virtual_package_with_version_fails(self, pacman: Mock):
"""
Scenario:
@@ -325,7 +324,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertFalse(res.to_install)
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_installed_virtual_with_defined_version_to_remove_when_conflict_version_matches(self, pacman: Mock):
"""
Scenario:
@@ -390,7 +389,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="X", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'V'", extra_size=1)], res.to_remove)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_installed_virtual_with_defined_version_to_remove_when_conflict_matches__case_2(self, pacman: Mock):
"""
This test case covers the same scenario as the above, but adds an additional provider for the same
@@ -466,7 +465,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="X", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'V'", extra_size=1)], res.to_remove)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__should_return_installed_package_to_remove_when_conflict_with_provided_matches_version(self, pacman: Mock):
"""
If the newest version o package A conflicts with a provided package C (by installed package B),
@@ -523,7 +522,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="B", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__should_return_installed_package_to_remove_when_conflict_with_provided_matches(self, pacman: Mock):
"""
If the newest version o package A conflicts with a provided package C (by installed package B),
@@ -579,7 +578,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="B", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__should_not_return_installed_to_remove_when_conflict_with_provider_is_self_conflict(self, pacman: Mock):
"""
If the newest version o package A conflicts with a provided package C (by A itself),
@@ -622,7 +621,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertFalse(res.to_install)
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_as_cannot_upgrade_when_several_to_upgrade_conflict_with_each_other(self, pacman: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -682,7 +681,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertIn(UpgradeRequirement(pkg=pkg_a, reason=" 'B'"), res.cannot_upgrade)
self.assertIn(UpgradeRequirement(pkg=pkg_b, reason=" 'A'"), res.cannot_upgrade)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_as_cannot_upgrade_when_several_to_upgrade_conflict_with_provided_by_each_other(self, pacman: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -744,7 +743,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertIn(UpgradeRequirement(pkg=pkg_a, reason=" 'B'"), res.cannot_upgrade)
self.assertIn(UpgradeRequirement(pkg=pkg_b, reason=" 'A'"), res.cannot_upgrade)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_a_package_to_upgrade_conflicts_with_another(self, pacman: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -806,7 +805,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_a_package_to_upgrade_conflicts_with_provided_by_another(self, pacman: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -871,7 +870,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_a_package_to_upgrade_conflicts_with_provided_version_by_another(self, pacman: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -937,8 +936,8 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
@patch(f"{__app_name__}.gems.arch.dependencies.pacman")
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.dependencies.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_as_cannot_upgrade_when_several_packages_to_install_conflict_with_each_other(self, *mocks: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -1039,8 +1038,8 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertIn(UpgradeRequirement(pkg=pkg_a, reason="'C' 'D'"), res.cannot_upgrade)
self.assertIn(UpgradeRequirement(pkg=pkg_b, reason="'C' 'D'"), res.cannot_upgrade)
@patch(f"{__app_name__}.gems.arch.dependencies.pacman")
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.dependencies.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_as_cannot_upgrade_when_packages_to_install_conflict_with_version_of_each_other(self, *mocks: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -1141,8 +1140,8 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertIn(UpgradeRequirement(pkg=pkg_a, reason="'C' 'D'"), res.cannot_upgrade)
self.assertIn(UpgradeRequirement(pkg=pkg_b, reason="'C' 'D'"), res.cannot_upgrade)
@patch(f"{__app_name__}.gems.arch.dependencies.pacman")
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.dependencies.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_to_update_conflicts_with_to_install(self, *mocks: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -1228,8 +1227,8 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, extra_size=1, reason=" 'C'")], res.to_remove)
@patch(f"{__app_name__}.gems.arch.dependencies.pacman")
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.dependencies.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_to_update_conflicts_with_specific_version_of_install(self, *mocks: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -1315,8 +1314,8 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, extra_size=1, reason=" 'C'")], res.to_remove)
@patch(f"{__app_name__}.gems.arch.dependencies.pacman")
@patch(f"{__app_name__}.gems.arch.updates.pacman")
@patch("bauh.gems.arch.dependencies.pacman")
@patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_to_update_conflicts_with_to_install_and_it_has_deps(self, *mocks: Mock):
"""
Consider package A and B are selected to be upgraded:

View File

@@ -1,14 +1,14 @@
from unittest import TestCase
from unittest.mock import patch, Mock
from bauh import __app_name__
from bauh.gems.flatpak import flatpak, VERSION_1_2
class FlatpakTest(TestCase):
@patch(f'{__app_name__}.gems.flatpak.flatpak.SimpleProcess')
@patch(f'{__app_name__}.gems.flatpak.flatpak.ProcessHandler.handle_simple', return_value=(True, """
@patch("bauh.gems.flatpak.flatpak.SimpleProcess")
@patch("bauh.gems.flatpak.flatpak.ProcessHandler.handle_simple", return_value=(True, """
Looking for updates...
\tID\tArch\tBranch\tRemote\tDownload

View File

@@ -4,6 +4,8 @@ from unittest.mock import Mock, patch
from bauh.gems.web.controller import DEFAULT_LANGUAGE_HEADER
from bauh.gems.web.controller import WebApplicationManager
LOCALE_TARGET = 'bauh.gems.web.controller.locale.getlocale'
class ControllerTest(TestCase):
@@ -16,35 +18,35 @@ class WebApplicationManagerTest(TestCase):
def setUp(self):
self.manager = WebApplicationManager(context=Mock())
@patch('locale.getdefaultlocale', side_effect=Exception)
def test_get_accept_language_header__must_return_default_locale_when_exception_raised(self, getdefaultlocale: Mock):
@patch(LOCALE_TARGET, side_effect=Exception)
def test_get_accept_language_header__must_return_default_locale_when_exception_raised(self, getlocale: Mock):
returned = self.manager.get_accept_language_header()
self.assertEqual(DEFAULT_LANGUAGE_HEADER, returned)
getdefaultlocale.assert_called_once()
getlocale.assert_called_once()
@patch('locale.getdefaultlocale', return_value=None)
def test_get_accept_language_header__must_return_default_locale_when_no_locale_is_returned(self, getdefaultlocale: Mock):
@patch(LOCALE_TARGET, return_value=(None, None))
def test_get_accept_language_header__must_return_default_locale_when_no_locale_is_returned(self, getlocale: Mock):
returned = self.manager.get_accept_language_header()
self.assertEqual(DEFAULT_LANGUAGE_HEADER, returned)
getdefaultlocale.assert_called_once()
getlocale.assert_called_once()
@patch('locale.getdefaultlocale', return_value=['es_AR'])
def test_get_accept_language_header__must_return_the_system_locale_without_underscore_plus_default_locale(self, getdefaultlocale: Mock):
@patch(LOCALE_TARGET, return_value=('es_AR', 'UTF-8'))
def test_get_accept_language_header__must_return_the_system_locale_without_underscore_plus_default_locale(self, getlocale: Mock):
returned = self.manager.get_accept_language_header()
self.assertEqual(f'es-AR, es, {DEFAULT_LANGUAGE_HEADER}', returned)
getdefaultlocale.assert_called_once()
getlocale.assert_called_once()
@patch('locale.getdefaultlocale', return_value=['es'])
def test_get_accept_language_header__must_return_the_simple_system_locale_plus_default_locale(self, getdefaultlocale: Mock):
@patch(LOCALE_TARGET, return_value=('es', 'UTF-8'))
def test_get_accept_language_header__must_return_the_simple_system_locale_plus_default_locale(self, getlocale: Mock):
returned = self.manager.get_accept_language_header()
self.assertEqual(f'es, {DEFAULT_LANGUAGE_HEADER}', returned)
getdefaultlocale.assert_called_once()
getlocale.assert_called_once()
@patch('locale.getdefaultlocale', return_value=['en_IN'])
def test_get_accept_language_header__must_not_concatenate_default_locale_if_system_locale_has_it(self, getdefaultlocale: Mock):
@patch(LOCALE_TARGET, return_value=('en_IN', 'UTF-8'))
def test_get_accept_language_header__must_not_concatenate_default_locale_if_system_locale_has_it(self, getlocale: Mock):
returned = self.manager.get_accept_language_header()
self.assertEqual(f'en-IN, en', returned)
getdefaultlocale.assert_called_once()
self.assertEqual('en-IN, en', returned)
getlocale.assert_called_once()
def test_strip_url_protocol__http_no_www(self):
res = self.manager.strip_url_protocol('http://test.com')
@@ -60,4 +62,4 @@ class WebApplicationManagerTest(TestCase):
def test_strip_url_protocol__https_with_www(self):
res = self.manager.strip_url_protocol('https://www.test.com')
self.assertEqual('test.com', res)
self.assertEqual('test.com', res)