Source code for rez.tests.test_solver

# SPDX-License-Identifier: Apache-2.0
# Copyright Contributors to the Rez Project


"""
test dependency resolving algorithm
"""
from __future__ import print_function

from rez.vendor.version.requirement import Requirement
from rez.solver import Solver, Cycle, SolverStatus
from rez.config import config
import unittest
from rez.tests.util import TestBase
import itertools


solver_verbosity = 1


[docs]class TestSolver(TestBase):
[docs] @classmethod def setUpClass(cls): packages_path = cls.data_path("solver", "packages") cls.packages_path = [packages_path] cls.settings = dict( packages_path=cls.packages_path, package_filter=None)
def _create_solvers(self, reqs): s1 = Solver(reqs, self.packages_path, optimised=True, verbosity=solver_verbosity) s2 = Solver(reqs, self.packages_path, optimised=False, verbosity=solver_verbosity) s_perms = [] perms = itertools.permutations(reqs) for reqs_ in perms: s = Solver(reqs_, self.packages_path, optimised=True, verbosity=solver_verbosity) s_perms.append(s) return (s1, s2, s_perms) def _solve(self, packages, expected_resolve): print() reqs = [Requirement(x) for x in packages] s1, s2, s_perms = self._create_solvers(reqs) s1.solve() self.assertEqual(s1.status, SolverStatus.solved) # ephemeral order doesn't matter, hence the sort resolve = ( [str(x) for x in s1.resolved_packages] + sorted(str(x) for x in s1.resolved_ephemerals) ) print() print("request: %s" % ' '.join(packages)) print("expecting: %s" % ' '.join(expected_resolve)) print("result: %s" % ' '.join(str(x) for x in resolve)) self.assertEqual(resolve, expected_resolve) print("checking that unoptimised solve matches optimised...") s2.solve() self.assertEqual(s2.status, SolverStatus.solved) resolve2 = ( [str(x) for x in s2.resolved_packages] + sorted(str(x) for x in s2.resolved_ephemerals) ) self.assertEqual(resolve2, resolve) print("checking that permutations also succeed...") for s in s_perms: s.solve() self.assertEqual(s.status, SolverStatus.solved) return s1 def _fail(self, *packages): print() reqs = [Requirement(x) for x in packages] s1, s2, s_perms = self._create_solvers(reqs) s1.solve() print() print("request: %s" % ' '.join(packages)) print("expecting failure") self.assertEqual(s1.status, SolverStatus.failed) print("result: %s" % str(s1.failure_reason())) print("checking that unoptimised solve fail matches optimised...") s2.solve() self.assertEqual(s2.status, SolverStatus.failed) self.assertEqual(s1.failure_reason(), s2.failure_reason()) print("checking that permutations also fail...") for s in s_perms: s.solve() self.assertEqual(s.status, SolverStatus.failed) return s1
[docs] def test_01(self): """Extremely basic solves involving a single package.""" self._solve([], []) self._solve(["nada"], ["nada[]"]) self._solve(["!nada"], []) self._solve(["~nada"], []) self._solve([".foo-1"], [".foo-1"]) self._solve(["!.bah"], []) self._solve(["!.bah-2.5+"], []) self._solve(["python"], ["python-2.7.0[]"]) self._solve(["~python-2+"], []) self._solve(["~python"], []) self._solve(["!python-2.5"], []) self._solve(["!python"], [])
[docs] def test_02(self): """Basic solves involving a single package.""" self._solve(["nada", "~nada"], ["nada[]"]) self._solve(["nopy"], ["nopy-2.1[]"]) self._solve(["python-2.6"], ["python-2.6.8[]"]) self._solve(["python-2.6", "!python-2.6.8"], ["python-2.6.0[]"]) self._solve(["python-2.6", "python-2.6.5+"], ["python-2.6.8[]"]) self._solve(["python", "python-0+<2.6"], ["python-2.5.2[]"]) self._solve(["python", "python-0+<2.6.8"], ["python-2.6.0[]"]) self._solve(["python", "~python-2.7+"], ["python-2.7.0[]"]) self._solve(["!python-2.6+", "python"], ["python-2.5.2[]"])
[docs] def test_03(self): """Failures in the initial request.""" self._fail("nada", "!nada") self._fail("python-2.6", "~python-2.7") self._fail("pyfoo", "nada", "!nada") self._fail(".foo-1", ".foo-2") self._fail(".foo-2.5", "!.foo-2")
[docs] def test_04(self): """Basic failures.""" self._fail("pybah", "!python") self._fail("pyfoo-3.1", "python-2.7+") self._fail("pyodd<2", "python-2.7") self._fail("nopy", "python-2.5.2")
[docs] def test_05(self): """More complex failures.""" self._fail("bahish", "pybah<5") self._fail("pybah-4", "pyfoo-3.0")
[docs] def test_06(self): """Basic solves involving multiple packages.""" self._solve(["nada", "nopy"], ["nada[]", "nopy-2.1[]"]) self._solve(["pyfoo"], ["python-2.6.8[]", "pyfoo-3.1.0[]"]) self._solve(["pyfoo-3.0"], ["python-2.5.2[]", "pyfoo-3.0.0[]", ".eek-3+"]) self._solve(["pyfoo-3.0", ".eek-4.5"], ["python-2.5.2[]", "pyfoo-3.0.0[]", ".eek-4.5"]) self._solve(["pybah"], ["python-2.5.2[]", "pybah-5[]"]) self._solve(["nopy", "python"], ["nopy-2.1[]", "python-2.7.0[]"]) self._solve(["pybah", "!python-2.5"], ["python-2.6.8[]", "pybah-4[]", ".eek-1"]) self._solve(["pybah", "!python-2.5", "python<2.6.8"], ["python-2.6.0[]", "pybah-4[]", ".eek-1"]) self._solve(["python", "pybah"], ["python-2.6.8[]", "pybah-4[]", ".eek-1"])
[docs] def test_07(self): """More complex solves.""" self._solve(["python", "pyodd"], ["python-2.6.8[]", "pybah-4[]", "pyodd-2[]", ".eek-1"]) self._solve(["pybah", "pyodd"], ["python-2.5.2[]", "pybah-5[]", "pyodd-2[]"]) self._solve(["pysplit", "python-2.5"], ["pysplit-5[]", "python-2.5.2[]"]) self._solve(["~python<2.6", "pysplit"], ["pysplit-5[]"]) self._solve(["python", "bahish", "pybah"], ["python-2.5.2[]", "pybah-5[]", "bahish-2[]"]) self._solve([".foo-2.5+", ".foo-2"], [".foo-2.5+<2_"])
[docs] def test_08(self): """Cyclic failures.""" def _test(*pkgs): s = self._fail(*pkgs) self.assertTrue(isinstance(s.failure_reason(), Cycle)) _test("pymum-1") _test("pydad-1") _test("pyson-1") _test("pymum-3") _test("pydad-3") s = self._fail("pymum-2") self.assertFalse(isinstance(s.failure_reason(), Cycle))
# variant tests
[docs] def test_09_version_priority_mode(self): config.override("variant_select_mode", "version_priority") self._solve(["pyvariants", "python"], ["python-2.7.0[]", "pyvariants-2[0]"]) self._solve(["pyvariants", "python", "nada"], ["python-2.7.0[]", "pyvariants-2[0]", "nada[]"])
[docs] def test_10_intersection_priority_mode(self): config.override("variant_select_mode", "intersection_priority") self._solve(["pyvariants", "python"], ["python-2.7.0[]", "pyvariants-2[0]"]) self._solve(["pyvariants", "python", "nada"], ["python-2.6.8[]", "nada[]", "pyvariants-2[1]"])
[docs] def test_11_variant_splitting(self): self._solve(["test_variant_split_start"], ["test_variant_split_end-1.0[1]", "test_variant_split_mid2-2.0[0]", "test_variant_split_start-1.0[1]"])
if __name__ == '__main__': unittest.main()