[arch] fix -> regressions from commit db5eeabf

This commit is contained in:
Vinicius Moreira
2021-06-18 15:14:20 -03:00
parent 6e2a4222c8
commit c2493befda
6 changed files with 110 additions and 76 deletions

View File

@@ -7,7 +7,7 @@ from bauh.api.abstract.handler import ProcessWatcher
from bauh.gems.arch import pacman, message, sorting, confirmation from bauh.gems.arch import pacman, message, sorting, confirmation
from bauh.gems.arch.aur import AURClient from bauh.gems.arch.aur import AURClient
from bauh.gems.arch.exceptions import PackageNotFoundException from bauh.gems.arch.exceptions import PackageNotFoundException
from bauh.gems.arch.version import compare_versions from bauh.gems.arch.version import match_required_version
from bauh.view.util.translation import I18n from bauh.view.util.translation import I18n
@@ -229,7 +229,7 @@ class DependenciesAnalyser:
if len(split_dep) == 3 and split_dep[0] == dep_name: if len(split_dep) == 3 and split_dep[0] == dep_name:
version_provided = split_dep[2] version_provided = split_dep[2]
if compare_versions(version_provided, exp_op, version_required): if match_required_version(version_provided, exp_op, version_required):
matched_providers.add(p) matched_providers.add(p)
break break
@@ -275,9 +275,9 @@ class DependenciesAnalyser:
split_informed_dep = self.re_dep_operator.split(dep_exp) split_informed_dep = self.re_dep_operator.split(dep_exp)
try: try:
version_required = split_informed_dep[2] version_required = split_informed_dep[2]
exp_op = split_informed_dep[1] if split_informed_dep[1] != '=' else '==' exp_op = split_informed_dep[1].strip()
if compare_versions(dep_version, exp_op, version_required): if match_required_version(dep_version, exp_op, version_required):
aur_deps.add(dep_name) aur_deps.add(dep_name)
missing_deps.add((dep_name, 'aur')) missing_deps.add((dep_name, 'aur'))
except: except:
@@ -333,9 +333,9 @@ class DependenciesAnalyser:
if version_found: if version_found:
version_found = version_found[0].split('=')[1] version_found = version_found[0].split('=')[1]
version_required = dep_split[2] version_required = dep_split[2]
op = dep_split[1] if dep_split[1] != '=' else '==' op = dep_split[1].strip()
if not compare_versions(version_found, op, version_required): if not match_required_version(version_found, op, version_required):
self._fill_missing_dep(dep_name=dep_name, dep_exp=dep, aur_index=aur_index, self._fill_missing_dep(dep_name=dep_name, dep_exp=dep, aur_index=aur_index,
missing_deps=missing_deps, missing_deps=missing_deps,
remote_provided_map=remote_provided_map, remote_provided_map=remote_provided_map,

View File

@@ -5,7 +5,7 @@ from typing import Optional
from bauh.api.abstract.model import PackageStatus from bauh.api.abstract.model import PackageStatus
from bauh.api.http import HttpClient from bauh.api.http import HttpClient
from bauh.gems.arch.model import ArchPackage from bauh.gems.arch.model import ArchPackage
from bauh.gems.arch.version import compare_versions from bauh.gems.arch.version import normalize_version
from bauh.view.util.translation import I18n from bauh.view.util.translation import I18n
URL_PKG_DOWNLOAD = 'https://aur.archlinux.org/{}' URL_PKG_DOWNLOAD = 'https://aur.archlinux.org/{}'
@@ -61,7 +61,7 @@ class AURDataMapper:
@staticmethod @staticmethod
def check_version_update(version: str, latest_version: str) -> bool: def check_version_update(version: str, latest_version: str) -> bool:
if version and latest_version and version != latest_version: if version and latest_version and version != latest_version:
return compare_versions(latest_version, '>', version) return normalize_version(latest_version) > normalize_version(version)
return False return False

View File

@@ -12,7 +12,7 @@ from bauh.gems.arch.dependencies import DependenciesAnalyser
from bauh.gems.arch.exceptions import PackageNotFoundException from bauh.gems.arch.exceptions import PackageNotFoundException
from bauh.gems.arch.model import ArchPackage from bauh.gems.arch.model import ArchPackage
from bauh.gems.arch.pacman import RE_DEP_OPERATORS from bauh.gems.arch.pacman import RE_DEP_OPERATORS
from bauh.gems.arch.version import compare_versions from bauh.gems.arch.version import match_required_version
from bauh.view.util.translation import I18n from bauh.view.util.translation import I18n
@@ -725,14 +725,11 @@ class UpdatesSummarizer:
if versions: if versions:
op = ''.join(RE_DEP_OPERATORS.findall(dep)) op = ''.join(RE_DEP_OPERATORS.findall(dep))
if op == '=':
op = '=='
version_match = False version_match = False
for v in versions: for v in versions:
try: try:
if compare_versions(v, op, dep_split[1]): if match_required_version(current_version=v, operator=op, required_version=dep_split[1]):
version_match = True version_match = True
break break
except: except:

View File

@@ -1,35 +1,51 @@
from typing import Optional import re
from packaging.version import parse from packaging.version import LegacyVersion
RE_VERSION_WITH_RELEASE = re.compile(r'^(.+)-\d+$')
RE_VERSION_WITH_EPOCH = re.compile(r'^\d+:(.+)$')
def normalize_version(version: str) -> Optional[str]: def normalize_version(version: str) -> LegacyVersion:
if version: final_version = version.strip()
final_version = version.strip()
if final_version: if not RE_VERSION_WITH_EPOCH.match(final_version):
if ':' not in final_version: final_version = '0:{}'.format(final_version)
final_version = '0:{}'.format(final_version)
if '-' not in final_version: if not RE_VERSION_WITH_RELEASE.match(final_version):
final_version = '{}-1'.format(final_version) final_version = '{}-1'.format(final_version)
return final_version return LegacyVersion(final_version)
def compare_versions(version_a: str, operator: str, version_b: str) -> bool: def match_required_version(current_version: str, operator: str, required_version: str) -> bool:
if operator: final_required, final_current = required_version.strip(), current_version.strip()
a_obj, b_obj = parse(normalize_version(version_a)), parse(normalize_version(version_b))
if operator == '>': required_has_epoch, current_no_epoch = bool(RE_VERSION_WITH_EPOCH.match(final_required)), RE_VERSION_WITH_EPOCH.split(final_current)
return a_obj > b_obj current_has_epoch = len(current_no_epoch) > 1
elif operator == '>=':
return a_obj >= b_obj
elif operator == '<':
return a_obj < b_obj
elif operator == '<=':
return a_obj <= b_obj
elif operator == '==':
return a_obj == b_obj
raise Exception("compare: invalid string operator {}".format(operator)) if required_has_epoch and not current_has_epoch:
final_current = '0:{}'.format(final_current)
elif current_has_epoch and not required_has_epoch:
final_current = current_no_epoch[1]
required_has_release, current_no_release = bool(RE_VERSION_WITH_RELEASE.match(final_required)), RE_VERSION_WITH_RELEASE.split(final_current)
current_has_release = len(current_no_release) > 1
if required_has_release and not current_has_release:
final_current = '{}-1'.format(final_current)
elif current_has_release and not required_has_release:
final_current = current_no_release[1]
final_required, final_current = LegacyVersion(final_required), LegacyVersion(final_current)
if operator == '==' or operator == '=':
return final_current == final_required
elif operator == '>':
return final_current > final_required
elif operator == '>=':
return final_current >= final_required
elif operator == '<':
return final_current < final_required
elif operator == '<=':
return final_current <= final_required

View File

@@ -10,7 +10,7 @@ class ArchDataMapperTest(TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
warnings.filterwarnings('ignore') warnings.filterwarnings('ignore', category=DeprecationWarning)
def test_check_version_update(self): def test_check_version_update(self):
self.assertTrue(AURDataMapper.check_version_update('1.0.0-1', '1.0.0-2')) self.assertTrue(AURDataMapper.check_version_update('1.0.0-1', '1.0.0-2'))
@@ -181,3 +181,17 @@ class ArchDataMapperTest(TestCase):
# in this case, install_date will be considered instead of package's last_modified. # in this case, install_date will be considered instead of package's last_modified.
# as 'install_date' is higher, only the string versions will be compared # as 'install_date' is higher, only the string versions will be compared
self.assertTrue(mapper.check_update(pkg=pkg, last_modified=None)) self.assertTrue(mapper.check_update(pkg=pkg, last_modified=None))
def test_check_version_update__one_version_contain_mixed_letters_and_symbols(self):
self.assertTrue(AURDataMapper.check_version_update('2.2.6', '2.3.3op2'))
self.assertFalse(AURDataMapper.check_version_update('2.3', '2.2.a'))
self.assertTrue(AURDataMapper.check_version_update('2.2.a.123', '2.3'))
def test_check_update__only_installed_version_with_release_number(self):
self.assertTrue(AURDataMapper.check_version_update('2.2.6-1', '2.3'))
self.assertTrue(AURDataMapper.check_version_update('2.2', '2.2-2'))
self.assertFalse(AURDataMapper.check_version_update('2.2', '2.2-1'))
def test_check_update__versions_with_epoch(self):
self.assertTrue(AURDataMapper.check_version_update('2.3', '1:2.1'))
self.assertFalse(AURDataMapper.check_version_update('1:1.0', '2.1'))

View File

@@ -4,48 +4,55 @@ from unittest import TestCase
from bauh.gems.arch import version from bauh.gems.arch import version
class CompareVersionsTest(TestCase): class MatchRequiredVersionTest(TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
warnings.filterwarnings('ignore') warnings.filterwarnings('ignore', category=DeprecationWarning)
def test_both_versions_only_filled_with_numbers_and_same_number_of_divisions(self): def test_must_accept_single_or_both_equal_symbols_as_valid(self):
self.assertFalse(version.compare_versions('1', '>', '2')) self.assertTrue(version.match_required_version('1', '=', '1'))
self.assertTrue(version.compare_versions('1', '<', '2')) self.assertTrue(version.match_required_version('1', '==', '1'))
self.assertTrue(version.compare_versions('1', '<=', '2'))
self.assertTrue(version.compare_versions('2', '>=', '1'))
self.assertTrue(version.compare_versions('1', '==', '1'))
self.assertFalse(version.compare_versions('1.1', '>', '2.0'))
self.assertTrue(version.compare_versions('1.1', '<', '2.0'))
self.assertTrue(version.compare_versions('1.1', '<=', '2.0'))
self.assertTrue(version.compare_versions('2.1', '>', '2.0'))
self.assertTrue(version.compare_versions('2.1', '>=', '2.0'))
self.assertTrue(version.compare_versions('2.0', '==', '2.0'))
def test_versions_only_filled_with_numbers_and_different_number_of_divisons(self): def test_must_ignore_release_number_when_required_has_no_defined(self):
self.assertFalse(version.compare_versions('1.0', '>', '2')) self.assertTrue(version.match_required_version('1.3.0-2', '==', '1.3.0'))
self.assertTrue(version.compare_versions('1', '<', '2.0')) self.assertTrue(version.match_required_version('1.3.0-2', '<=', '1.3.0'))
self.assertTrue(version.compare_versions('1.1.1', '<=', '2')) self.assertTrue(version.match_required_version('1.3.0-2', '>=', '1.3.0'))
self.assertTrue(version.compare_versions('2', '>=', '1.0')) self.assertFalse(version.match_required_version('1.3.0-2', '<', '1.3.0'))
self.assertTrue(version.compare_versions('1.0', '==', '1')) self.assertFalse(version.match_required_version('1.3.0-2', '>', '1.3.0'))
self.assertTrue(version.compare_versions('1.1', '>', '1'))
self.assertFalse(version.compare_versions('1.1', '<', '1'))
def test_one_version_contain_mixed_letters_and_symbols(self): def test_must_ignore_epoch_number_when_required_has_no_defined(self):
self.assertTrue(version.compare_versions('2.3.3op2', '>=', '2.2.6')) self.assertTrue(version.match_required_version('1:1.3.0-1', '>', '1.1.0'))
self.assertTrue(version.compare_versions('2.2a', '<', '2.2b')) self.assertTrue(version.match_required_version('1:1.3.0-1', '>=', '1.1.0'))
self.assertTrue(version.compare_versions('2.2a', '<', '2.3a')) self.assertFalse(version.match_required_version('1:1.3.0-1', '<', '1.1.0'))
self.assertFalse(version.match_required_version('1:1.3.0-1', '<=', '1.1.0'))
self.assertFalse(version.match_required_version('1:1.3.0-1', '==', '1.1.0'))
def test_versions_with_epochs(self): def test_must_consider_default_epoch_for_current_version_when_required_has_epoch(self):
self.assertTrue(version.compare_versions('1:1', '==', '1:1.0')) self.assertFalse(version.match_required_version('1.1.0', '==', '1:1.1.0')) # 1.1.0 -> 0:1.1.0
self.assertTrue(version.compare_versions('1:1', '<', '1:1.1')) self.assertFalse(version.match_required_version('1.1.0', '>', '1:1.1.0')) # 1.1.0 -> 0:1.1.0
self.assertTrue(version.compare_versions('1:1', '<=', '1:1.1')) self.assertFalse(version.match_required_version('1.1.0', '>=', '1:1.1.0')) # 1.1.0 -> 0:1.1.0
self.assertTrue(version.compare_versions('2:0', '>', '1:1.1')) self.assertTrue(version.match_required_version('1.1.0', '<', '1:1.1.0')) # 1.1.0 -> 0:1.1.0
self.assertTrue(version.compare_versions('1:0', '>', '2.0-1')) self.assertTrue(version.match_required_version('1.1.0', '<=', '1:1.1.0')) # 1.1.0 -> 0:1.1.0
def test_versions_with_release_number(self): self.assertTrue(version.match_required_version('1.1.0', '==', '0:1.1.0')) # 1.1.0 -> 0:1.1.0
self.assertTrue(version.compare_versions('1.0-1', '<', '1.0-2'))
self.assertTrue(version.compare_versions('1.0-2', '>', '1.0-1')) def test_must_match_when_current_version_is_composed_of_alphanumerics_but_required_no(self):
self.assertTrue(version.compare_versions('1.0-1', '<', '1.0-10')) self.assertTrue(version.match_required_version('2.3.3op2', '>', '2.2.6'))
self.assertTrue(version.compare_versions('1.0-10', '>', '1.0-1')) self.assertTrue(version.match_required_version('2.3.3op2', '>=', '2.2.6'))
self.assertFalse(version.match_required_version('2.3.3op2', '==', '2.2.6'))
self.assertFalse(version.match_required_version('2.3.3op2', '<', '2.2.6'))
self.assertFalse(version.match_required_version('2.3.3op2', '<=', '2.2.6'))
# opposite comparisons
self.assertFalse(version.match_required_version('2.2.6', '>', '2.3.3op2'))
self.assertFalse(version.match_required_version('2.2.6', '>=', '2.3.3op2'))
self.assertFalse(version.match_required_version('2.2.6', '==', '2.3.3op2'))
self.assertTrue(version.match_required_version('2.2.6', '<', '2.3.3op2'))
self.assertTrue(version.match_required_version('2.2.6', '<=', '2.3.3op2'))
def test_must_match_when_both_versions_are_composed_of_alphanumerics(self):
self.assertTrue(version.match_required_version('2.3.3ab', '==', '2.3.3ab'))
self.assertTrue(version.match_required_version('2.3.3ab', '<', '2.3.3ac'))
self.assertTrue(version.match_required_version('2.3.3ad', '>', '2.3.3ac'))
self.assertTrue(version.match_required_version('2.3.3ad', '<', '2.3.3ad.1'))