Allow to search mandatory packages in all forest

Update requirements with filter option:

        "mandatory": {
            "enum": ["exact", "newest"]
        }

1) [RPM] We need to search the newest mandatory packages in all
   forest but not from the exact tree, also repo priority should
   be take into account

2) Add priority to gen_repository in tests, since this is
   mandatory parameter

3) [Tests] Add package10 with different version to check mandatory
   values:
    - None
    - "exact"
    - "newest"
  match for mandatory packages

Change-Id: I36a1463b495075bf6c44a46b557c3ac524de0220
Closes-bug: #1585103
This commit is contained in:
Sergey Kulanov 2016-06-08 16:33:07 +03:00
parent 5c9d32f234
commit f922090dc6
6 changed files with 81 additions and 36 deletions

View File

@ -21,12 +21,14 @@ import logging
import six
from packetary import objects
from packetary import schemas
from packetary.api.context import Context
from packetary.api.options import RepositoryCopyOptions
from packetary.controllers import RepositoryController
from packetary.library.functions import compose
from packetary import objects
from packetary import schemas
from packetary.objects.package_relation import PackageRelation
from packetary.api.loaders import get_packages_traverse
from packetary.api.loaders import load_package_relations
@ -37,6 +39,12 @@ from packetary.api.validators import declare_schema
logger = logging.getLogger(__package__)
_MANDATORY = {
"exact": "=",
"newest": ">=",
}
class RepositoryApi(object):
"""Provides high-level API to operate with repositories."""
@ -149,16 +157,24 @@ class RepositoryApi(object):
requirements.get('repositories'), package_relations.append
)
for repo in repositories:
tree = forest.add_tree(repo.priority)
self.controller.load_packages(
repo,
compose(
forest.add_tree(repo.priority).add,
tree.add,
packages_traverse
)
)
return forest.get_packages(
package_relations, requirements.get('mandatory', True)
)
mandatory = requirements.get('mandatory')
if mandatory:
for package in tree.mandatory_packages:
package_relations.append(
PackageRelation.from_args(
(package.name,
_MANDATORY[requirements['mandatory']],
package.version)))
return forest.get_packages(package_relations)
packages = set()
self._load_packages(repositories, packages.add)

View File

@ -44,12 +44,10 @@ class PackagesForest(object):
tree = self.trees[priority] = PackagesTree()
return tree
def get_packages(self, requirements, include_mandatory=False):
def get_packages(self, requirements):
"""Get the packages according requirements.
:param requirements: the list of requirements
:param include_mandatory: if true, the mandatory packages will be
included to result
:return list of packages to copy
"""
@ -61,12 +59,6 @@ class PackagesForest(object):
unresolved = set()
stack = [(None, requirements)]
if include_mandatory:
for tree in six.itervalues(self.trees):
for mandatory in tree.mandatory_packages:
resolved.add(mandatory)
stack.append((mandatory, mandatory.requires))
while stack:
pkg, requirements = stack.pop()
for required in requirements:

View File

@ -68,7 +68,7 @@ REQUIREMENTS_SCHEMA = {
}
},
"mandatory": {
"type": "boolean"
},
"enum": ["exact", "newest"]
}
}
}

View File

@ -20,10 +20,12 @@ from packetary import objects
def gen_repository(name="test", url="file:///test",
architecture="x86_64", origin="Test", **kwargs):
architecture="x86_64", priority=99,
origin="Test", **kwargs):
"""Helper to create Repository object with default attributes."""
url = kwargs.pop("uri", url)
return objects.Repository(name, url, architecture, origin, **kwargs)
return objects.Repository(name, url, architecture, priority, origin,
**kwargs)
def gen_relation(name="test", version=None, alternative=None):

View File

@ -96,22 +96,11 @@ class TestPackagesForest(base.TestCase):
p22, forest.find(generator.gen_relation("package2", [">=", 2]))
)
def test_get_packages_with_mandatory(self):
def test_get_packages(self):
forest = PackagesForest()
self._generate_packages(forest)
packages = forest.get_packages(
[generator.gen_relation("package3")], True
)
self.assertItemsEqual(
["package1", "package2", "package3", "package4", "package5"],
(x.name for x in packages)
)
def test_get_packages_without_mandatory(self):
forest = PackagesForest()
self._generate_packages(forest)
packages = forest.get_packages(
[generator.gen_relation("package3")], False
[generator.gen_relation("package3")]
)
self.assertItemsEqual(
["package2", "package3", "package5"],

View File

@ -79,8 +79,12 @@ class TestRepositoryApi(base.TestCase):
name='{0}_5'.format(r.name), repository=r,
requires=[generator.gen_relation("unresolved")]
),
generator.gen_package(
name='package10', repository=r, mandatory=True,
requires=None, version=counter + 10
)
]
for r in self.repos
for counter, r in enumerate(self.repos)
]
self.controller.load_packages.side_effect = self.packages
@ -139,7 +143,7 @@ class TestRepositoryApi(base.TestCase):
self._generate_repositories(1)
self._generate_packages()
packages = self.api.get_packages(self.repos_data)
self.assertEqual(5, len(self.packages[0]))
self.assertEqual(6, len(self.packages[0]))
self.assertItemsEqual(self.packages[0], packages)
jsonschema_mock.validate.assert_called_once_with(
self.repos_data, self.api._get_repositories_data_schema()
@ -148,10 +152,30 @@ class TestRepositoryApi(base.TestCase):
def test_get_packages_by_requirements(self, jsonschema_mock):
self._generate_repositories(2)
self._generate_packages()
requirements = {
'packages': [{"name": "repo0_1"}],
'repositories': [{"name": "repo1"}]
}
packages = self.api.get_packages(self.repos_data, requirements)
expected_packages = [self.packages[0][0]] + self.packages[1]
self.assertItemsEqual(
[x.name for x in expected_packages],
[x.name for x in packages]
)
repos_schema = self.api._get_repositories_data_schema()
jsonschema_mock.validate.assert_has_calls([
mock.call(self.repos_data, repos_schema),
mock.call(requirements, schemas.REQUIREMENTS_SCHEMA)
], any_order=True)
def test_get_packages_by_requirements_newest_mandatory(self,
jsonschema_mock):
self._generate_repositories(2)
self._generate_packages()
requirements = {
'packages': [{"name": "repo0_1"}],
'repositories': [{"name": "repo1"}],
'mandatory': True
'mandatory': "newest"
}
packages = self.api.get_packages(self.repos_data, requirements)
expected_packages = self.packages[0][:3] + self.packages[1]
@ -165,6 +189,29 @@ class TestRepositoryApi(base.TestCase):
mock.call(requirements, schemas.REQUIREMENTS_SCHEMA)
], any_order=True)
def test_get_packages_by_requirements_exact_mandatory(self,
jsonschema_mock):
self._generate_repositories(2)
self._generate_packages()
requirements = {
'packages': [{"name": "repo0_1"}],
'repositories': [{"name": "repo1"}],
'mandatory': "exact"
}
packages = self.api.get_packages(self.repos_data, requirements)
expected_packages = self.packages[0][:3] + \
[self.packages[0][-1]] + \
self.packages[1]
self.assertItemsEqual(
[x.name for x in expected_packages],
[x.name for x in packages]
)
repos_schema = self.api._get_repositories_data_schema()
jsonschema_mock.validate.assert_has_calls([
mock.call(self.repos_data, repos_schema),
mock.call(requirements, schemas.REQUIREMENTS_SCHEMA)
], any_order=True)
def test_clone_repositories_as_is(self, jsonschema_mock):
self._generate_repositories(1)
self._generate_packages()
@ -193,7 +240,6 @@ class TestRepositoryApi(base.TestCase):
requirements = {
'packages': [{"name": "repo0_1"}],
'repositories': [{"name": "repo1"}],
'mandatory': False
}
self.controller.assign_packages.return_value = [0, 1, 1] * 3
stats = self.api.clone_repositories(