@@ -, +, @@ --- pym/_emerge/depgraph.py | 14 +++++ pym/portage/tests/resolver/ResolverPlayground.py | 9 ++++ .../tests/resolver/test_autounmask_use_breakage.py | 63 ++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 pym/portage/tests/resolver/test_autounmask_use_breakage.py --- a/pym/_emerge/depgraph.py +++ a/pym/_emerge/depgraph.py @@ -1059,6 +1059,7 @@ class depgraph(object): def __str__(self): return "(%s)" % ",".join(str(pkg) for pkg in self) + non_matching_forced = set() for conflict in conflicts: if debug: writemsg_level(" conflict:\n", level=logging.DEBUG, noiselevel=-1) @@ -1105,6 +1106,18 @@ class depgraph(object): continue elif len(matched) == 1: conflict_graph.add(matched[0], parent) + elif len(matched) == 0: + # The typically means that autounmask broke a + # USE-dep, but it could also be due to the slot + # not matching due to multislot (bug #220341). + # Either way, don't try to solve this conflict. + # Instead, force them all into the graph so that + # they are protected from removal. + non_matching_forced.update(conflict) + if debug: + for pkg in conflict: + writemsg_level(" non-match: %s\n" % pkg, + level=logging.DEBUG, noiselevel=-1) else: # More than one packages matched, but not all. conflict_graph.add(or_tuple(matched), parent) @@ -1125,6 +1138,7 @@ class depgraph(object): # Now select required packages. Collect them in the # 'forced' set. forced = set([non_conflict_node]) + forced.update(non_matching_forced) unexplored = set([non_conflict_node]) # or_tuples get special handling. We first explore # all packages in the hope of having forced one of --- a/pym/portage/tests/resolver/ResolverPlayground.py +++ a/pym/portage/tests/resolver/ResolverPlayground.py @@ -549,6 +549,7 @@ class ResolverPlaygroundTestCase(object): self.all_permutations = kwargs.pop("all_permutations", False) self.ignore_mergelist_order = kwargs.pop("ignore_mergelist_order", False) self.ambiguous_merge_order = kwargs.pop("ambiguous_merge_order", False) + self.ambiguous_slot_collision_solutions = kwargs.pop("ambiguous_slot_collision_solutions", False) self.check_repo_names = kwargs.pop("check_repo_names", False) self.merge_order_assertions = kwargs.pop("merge_order_assertions", False) @@ -664,6 +665,14 @@ class ResolverPlaygroundTestCase(object): str((node1, node2))) + \ ", got: " + str(got)) + elif key == "slot_collision_solutions" and \ + self.ambiguous_slot_collision_solutions: + # Tests that use all_permutations can have multiple + # outcomes here. + for x in expected: + if x == got: + expected = x + break elif key in ("unstable_keywords", "needed_p_mask_changes", "unsatisfied_deps") and expected is not None: expected = set(expected) --- a/pym/portage/tests/resolver/test_autounmask_use_breakage.py +++ a/pym/portage/tests/resolver/test_autounmask_use_breakage.py @@ -0,0 +1,63 @@ +# Copyright 2014 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import (ResolverPlayground, + ResolverPlaygroundTestCase) + +class AutounmaskUseBreakageTestCase(TestCase): + + def testAutounmaskUseBreakage(self): + + ebuilds = { + + "app-misc/A-0" : { + "EAPI": "5", + "RDEPEND": "app-misc/D[-foo]", + }, + + "app-misc/B-0" : { + "EAPI": "5", + "RDEPEND": "app-misc/D[foo]" + }, + + "app-misc/C-0" : { + "EAPI": "5", + "RDEPEND": ">=app-misc/D-1" + }, + + "app-misc/D-0" : { + "EAPI": "5", + "IUSE": "foo" + }, + + "app-misc/D-1" : { + "EAPI": "5", + "IUSE": "bar" + }, + + } + + test_cases = ( + + # Bug 510270 + # _solve_non_slot_operator_slot_conflicts throws + # IndexError: tuple index out of range + # due to autounmask USE breakage. + ResolverPlaygroundTestCase( + ["app-misc/C", "app-misc/B", "app-misc/A"], + all_permutations = True, + success = False, + ambiguous_slot_collision_solutions = True, + slot_collision_solutions = [None, []] + ), + + ) + + playground = ResolverPlayground(ebuilds=ebuilds, debug=False) + try: + for test_case in test_cases: + playground.run_TestCase(test_case) + self.assertEqual(test_case.test_success, True, test_case.fail_msg) + finally: + playground.cleanup() --