From e562aea8a418ec10ec046c5e6ecccf6bbd711c97 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Tue, 16 Sep 2014 10:57:49 -0700 Subject: [PATCH] _solve_..slot_conflicts: fix bug #510270 This fixes an IndexError in _solve_non_slot_operator_slot_conflicts which occurs when none of the conflict packages matched a particular atom. This 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 all of the associated conflict nodes into the graph so that they are protected from removal by the conflict solver. X-Gentoo-Bug: 510270 X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=510270 --- 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 diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index cc87d9f..b31f90c 100644 --- a/pym/_emerge/depgraph.py +++ b/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 diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py index 77a5b5c..b1974d7 100644 --- a/pym/portage/tests/resolver/ResolverPlayground.py +++ b/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) diff --git a/pym/portage/tests/resolver/test_autounmask_use_breakage.py b/pym/portage/tests/resolver/test_autounmask_use_breakage.py new file mode 100644 index 0000000..3654aa6 --- /dev/null +++ b/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() -- 1.8.5.5