diff --git a/plinth/package.py b/plinth/package.py index ca7c56407..14efcecf2 100644 --- a/plinth/package.py +++ b/plinth/package.py @@ -69,8 +69,11 @@ class Package(PackageExpression): def actual(self) -> str: cache = apt.Cache() - if self.name in cache: - # TODO: Also return version and suite to install from + if self.name in cache and cache[self.name].candidate: + # cache[package].candidate returns installation candidate. If the + # package is not installable due to the available versions being of + # priority less than 0, then .candidate will be None. TODO: Also + # return version and suite to install from. return self.name raise MissingPackageError(self.name) diff --git a/plinth/tests/test_package.py b/plinth/tests/test_package.py index b7aea85c3..c5153479d 100644 --- a/plinth/tests/test_package.py +++ b/plinth/tests/test_package.py @@ -80,11 +80,21 @@ def test_packages_init(): assert component.rerun_setup_on_upgrade -def test_packages_get_actual_packages(): +@patch('apt.Cache') +def test_packages_get_actual_packages(cache): """Test resolving of package expressions to actual packages.""" + cache.return_value = { + 'python3': Mock(name='python3', candidate='1.0'), + 'janus': Mock(name='janus', candidate=None) + } + component = Packages('test-component', ['python3']) assert component.get_actual_packages() == ['python3'] + component = Packages('test-component', ['unknown-package']) + with pytest.raises(MissingPackageError): + component.get_actual_packages() + component = Packages('test-component', [Package('unknown-package') | Package('python3')]) assert component.get_actual_packages() == ['python3'] @@ -94,6 +104,10 @@ def test_packages_get_actual_packages(): conflicts_action=Packages.ConflictsAction.IGNORE) assert component.get_actual_packages() == [] + component = Packages('test-component', ['janus']) + with pytest.raises(MissingPackageError): + component.get_actual_packages() + @patch('plinth.package.install') def test_packages_setup(install): @@ -356,17 +370,23 @@ def test_packages_is_available(path_class, cache, refresh_package_lists): '/etc/apt/sources.list': sources_list }[path] + def get_cache_packages(packages): + return { + package: Mock(name=package, candidate='1.0') + for package in packages + } + path_class.side_effect = path_side_effect # Packages found in cache component = Packages('test-component', ['package1', 'package2']) - cache.return_value = ['package1', 'package2'] + cache.return_value = get_cache_packages(['package1', 'package2']) assert component.is_available() path_class.assert_not_called() refresh_package_lists.assert_not_called() # Packages not found, cache exists and is fresh - cache.return_value = ['package1'] + cache.return_value = get_cache_packages(['package1']) package_cache.exists.return_value = True package_cache.stat.return_value.st_mtime = time.time() sources_list.stat.return_value.st_mtime = time.time() - 100 @@ -374,13 +394,13 @@ def test_packages_is_available(path_class, cache, refresh_package_lists): refresh_package_lists.assert_not_called() # Packages not found, cache does not exist - cache.return_value = ['package1'] + cache.return_value = get_cache_packages(['package1']) package_cache.exists.return_value = False assert not component.is_available() refresh_package_lists.assert_called_once() # Packages not found, cache is stale because it older than 1 hour - cache.return_value = ['package1'] + cache.return_value = get_cache_packages(['package1']) refresh_package_lists.reset_mock() package_cache.exists.return_value = True package_cache.stat.return_value.st_mtime = time.time() - 7200 @@ -388,7 +408,7 @@ def test_packages_is_available(path_class, cache, refresh_package_lists): refresh_package_lists.assert_called_once() # Packages not found, cache is stale because it older than sources.list - cache.return_value = ['package1'] + cache.return_value = get_cache_packages(['package1']) refresh_package_lists.reset_mock() package_cache.exists.return_value = True package_cache.stat.return_value.st_mtime = time.time() - 100 @@ -397,7 +417,10 @@ def test_packages_is_available(path_class, cache, refresh_package_lists): refresh_package_lists.assert_called_once() # Packages not found, cache is stale, but packages found after refresh - cache.side_effect = [['package1'], ['package1', 'package2']] + cache.side_effect = [ + get_cache_packages(['package1']), + get_cache_packages(['package1', 'package2']) + ] refresh_package_lists.reset_mock() assert component.is_available()