Merge "Allow to search mandatory packages in all forest"

This commit is contained in:
Jenkins 2016-07-05 21:23:39 +00:00 committed by Gerrit Code Review
commit 1508e35df2
6 changed files with 81 additions and 36 deletions

View File

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

View File

@ -44,12 +44,10 @@ class PackagesForest(object):
tree = self.trees[priority] = PackagesTree() tree = self.trees[priority] = PackagesTree()
return tree return tree
def get_packages(self, requirements, include_mandatory=False): def get_packages(self, requirements):
"""Get the packages according requirements. """Get the packages according requirements.
:param requirements: the list of 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 :return list of packages to copy
""" """
@ -61,12 +59,6 @@ class PackagesForest(object):
unresolved = set() unresolved = set()
stack = [(None, requirements)] 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: while stack:
pkg, requirements = stack.pop() pkg, requirements = stack.pop()
for required in requirements: for required in requirements:

View File

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

View File

@ -20,10 +20,12 @@ from packetary import objects
def gen_repository(name="test", url="file:///test", 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.""" """Helper to create Repository object with default attributes."""
url = kwargs.pop("uri", url) 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): 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])) p22, forest.find(generator.gen_relation("package2", [">=", 2]))
) )
def test_get_packages_with_mandatory(self): def test_get_packages(self):
forest = PackagesForest() forest = PackagesForest()
self._generate_packages(forest) self._generate_packages(forest)
packages = forest.get_packages( packages = forest.get_packages(
[generator.gen_relation("package3")], True [generator.gen_relation("package3")]
)
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
) )
self.assertItemsEqual( self.assertItemsEqual(
["package2", "package3", "package5"], ["package2", "package3", "package5"],

View File

@ -79,8 +79,12 @@ class TestRepositoryApi(base.TestCase):
name='{0}_5'.format(r.name), repository=r, name='{0}_5'.format(r.name), repository=r,
requires=[generator.gen_relation("unresolved")] 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 self.controller.load_packages.side_effect = self.packages
@ -139,7 +143,7 @@ class TestRepositoryApi(base.TestCase):
self._generate_repositories(1) self._generate_repositories(1)
self._generate_packages() self._generate_packages()
packages = self.api.get_packages(self.repos_data) 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) self.assertItemsEqual(self.packages[0], packages)
jsonschema_mock.validate.assert_called_once_with( jsonschema_mock.validate.assert_called_once_with(
self.repos_data, self.api._get_repositories_data_schema() 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): def test_get_packages_by_requirements(self, jsonschema_mock):
self._generate_repositories(2) self._generate_repositories(2)
self._generate_packages() 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 = { requirements = {
'packages': [{"name": "repo0_1"}], 'packages': [{"name": "repo0_1"}],
'repositories': [{"name": "repo1"}], 'repositories': [{"name": "repo1"}],
'mandatory': True 'mandatory': "newest"
} }
packages = self.api.get_packages(self.repos_data, requirements) packages = self.api.get_packages(self.repos_data, requirements)
expected_packages = self.packages[0][:3] + self.packages[1] expected_packages = self.packages[0][:3] + self.packages[1]
@ -165,6 +189,29 @@ class TestRepositoryApi(base.TestCase):
mock.call(requirements, schemas.REQUIREMENTS_SCHEMA) mock.call(requirements, schemas.REQUIREMENTS_SCHEMA)
], any_order=True) ], 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): def test_clone_repositories_as_is(self, jsonschema_mock):
self._generate_repositories(1) self._generate_repositories(1)
self._generate_packages() self._generate_packages()
@ -193,7 +240,6 @@ class TestRepositoryApi(base.TestCase):
requirements = { requirements = {
'packages': [{"name": "repo0_1"}], 'packages': [{"name": "repo0_1"}],
'repositories': [{"name": "repo1"}], 'repositories': [{"name": "repo1"}],
'mandatory': False
} }
self.controller.assign_packages.return_value = [0, 1, 1] * 3 self.controller.assign_packages.return_value = [0, 1, 1] * 3
stats = self.api.clone_repositories( stats = self.api.clone_repositories(