commit:     4c7ce0e20b36903a4e7acd7aaa6aac8779917fc4
Author:     Sam James <sam <AT> gentoo <DOT> org>
AuthorDate: Fri Jan  2 01:35:59 2026 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Sun Jan  4 02:21:47 2026 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=4c7ce0e2

tests: add testcase for failed Qt upgrade (XFAIL'd)

Dropping dev-python/pyside's dev-qt/qtconnectivity dependency here is
enough to fix it.

Bug: https://bugs.gentoo.org/968228
Signed-off-by: Sam James <sam <AT> gentoo.org>
Signed-off-by: Zac Medico <zmedico <AT> gentoo.org>

 lib/portage/tests/resolver/meson.build           |   1 +
 lib/portage/tests/resolver/test_missed_update.py | 248 +++++++++++++++++++++++
 2 files changed, 249 insertions(+)

diff --git a/lib/portage/tests/resolver/meson.build 
b/lib/portage/tests/resolver/meson.build
index bab474cf0d..7ae73630be 100644
--- a/lib/portage/tests/resolver/meson.build
+++ b/lib/portage/tests/resolver/meson.build
@@ -85,6 +85,7 @@ py.install_sources(
         'test_solve_non_slot_operator_slot_conflicts.py',
         'test_targetroot.py',
         'test_tar_merge_order.py',
+        'test_missed_update.py',
         'test_unmerge_order.py',
         'test_unnecessary_slot_upgrade.py',
         'test_update.py',

diff --git a/lib/portage/tests/resolver/test_missed_update.py 
b/lib/portage/tests/resolver/test_missed_update.py
new file mode 100644
index 0000000000..6813a91727
--- /dev/null
+++ b/lib/portage/tests/resolver/test_missed_update.py
@@ -0,0 +1,248 @@
+# Copyright 2026 Gentoo Authors
+# 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 MissedQtUpdateTestCase(TestCase):
+    def testMissedQtUpdate(self):
+        """
+        Testcase where Portage was unable to upgrade from
+        Qt 6.9.3 -> Qt 6.10.1 without an explicit redundant
+        argument on the command line (bug #968228).
+
+        This was fixed by the "earlier slot operator backtracking"
+        patch related to bug #964705.
+        """
+        ebuilds = {
+            "dev-qt/qtbase-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "IUSE": "+nls",
+                "RDEPEND": """
+                    !<dev-qt/qtconnectivity-6.9.3:6
+                    !<dev-qt/qtsvg-6.9.3:6
+                    !<dev-qt/qttools-6.9.3:6
+                """,
+                "PDEPEND": "nls? ( ~dev-qt/qttranslations-6.9.3:6 )",
+            },
+            "dev-qt/qtbase-6.10.1": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "IUSE": "+nls",
+                "RDEPEND": """
+                    !<dev-qt/qtconnectivity-6.10.1:6
+                    !<dev-qt/qtsvg-6.10.1:6
+                    !<dev-qt/qttools-6.10.1:6
+                """,
+                "PDEPEND": "nls? ( ~dev-qt/qttranslations-6.10.1:6 )",
+            },
+            "dev-qt/qtconnectivity-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.9.3:6",
+            },
+            "dev-qt/qtconnectivity-6.10.1": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.10.1:6",
+            },
+            "dev-qt/qttranslations-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.9.3:6",
+                "BDEPEND": "~dev-qt/qttools-6.9.3:6",
+            },
+            "dev-qt/qttranslations-6.10.1": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.10.1:6",
+                "BDEPEND": "~dev-qt/qttools-6.10.1:6",
+            },
+            "dev-qt/qtsvg-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.9.3:6",
+                "RDEPEND": """
+                    ~dev-qt/qtbase-6.9.3:6
+                """,
+            },
+            "dev-qt/qtsvg-6.10.1": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.10.1:6",
+                "RDEPEND": """
+                    ~dev-qt/qtbase-6.10.1:6
+                """,
+            },
+            "dev-qt/qttools-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.9.3:6",
+                "RDEPEND": "~dev-qt/qtbase-6.9.3:6",
+            },
+            "dev-qt/qttools-6.10.1": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.10.1:6",
+                "RDEPEND": """
+                    ~dev-qt/qtbase-6.10.1:6
+                """,
+            },
+            "dev-python/pyside-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6/6.9.3",
+                "DEPEND": """
+                    =dev-qt/qtbase-6.9.3*:6
+                    =dev-qt/qttools-6.9.3*:6
+                    =dev-qt/qtconnectivity-6.9.3*:6
+                """,
+                "RDEPEND": """
+                    =dev-qt/qtbase-6.9.3*:6
+                    =dev-qt/qttools-6.9.3*:6
+                    =dev-qt/qtconnectivity-6.9.3*:6
+                """,
+            },
+            "dev-python/pyside-6.10.1": {
+                "EAPI": "8",
+                "SLOT": "6/6.10.1",
+                "DEPEND": """
+                    =dev-qt/qtbase-6.10.1*:6
+                    =dev-qt/qtconnectivity-6.10.1*:6
+                """,
+                "RDEPEND": """
+                    =dev-qt/qtbase-6.10.1*:6
+                    =dev-qt/qtconnectivity-6.10.1*:6
+                """,
+            },
+            "media-gfx/freecad-1.0.1-r2": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": """
+                    dev-qt/qtbase:6
+                    dev-qt/qtsvg:6
+                    dev-python/pyside:6=
+                """,
+                "RDEPEND": """
+                    dev-qt/qtbase:6
+                    dev-qt/qtsvg:6
+                    dev-python/pyside:6=
+                """,
+            },
+        }
+        installed = {
+            "dev-qt/qtbase-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "IUSE": "+nls",
+                "USE": "nls",
+                "PDEPEND": "nls? ( ~dev-qt/qttranslations-6.9.3:6 )",
+            },
+            "dev-qt/qtconnectivity-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.9.3:6",
+            },
+            "dev-qt/qttranslations-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.9.3:6",
+                "BDEPEND": "~dev-qt/qttools-6.9.3:6",
+            },
+            "dev-qt/qtsvg-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.9.3:6",
+                "RDEPEND": "~dev-qt/qtbase-6.9.3:6",
+            },
+            "dev-qt/qttools-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": "~dev-qt/qtbase-6.9.3:6",
+                "RDEPEND": "~dev-qt/qtbase-6.9.3:6",
+            },
+            "dev-python/pyside-6.9.3": {
+                "EAPI": "8",
+                "SLOT": "6/6.9.3",
+                "DEPEND": """
+                    =dev-qt/qtbase-6.9.3*:6
+                    =dev-qt/qtconnectivity-6.9.3*:6
+                """,
+                "RDEPEND": """
+                    =dev-qt/qtbase-6.9.3*:6
+                    =dev-qt/qtconnectivity-6.9.3*:6
+                """,
+            },
+            "media-gfx/freecad-1.0.1-r2": {
+                "EAPI": "8",
+                "SLOT": "6",
+                "DEPEND": """
+                    dev-qt/qtbase:6
+                    dev-qt/qtsvg:6
+                    dev-python/pyside:6/6.9.3=
+                """,
+                "RDEPEND": """
+                    dev-qt/qtbase:6
+                    dev-qt/qtsvg:6
+                    dev-python/pyside:6/6.9.3=
+                """,
+            },
+        }
+
+        world = ("media-gfx/freecad",)
+
+        test_cases = (
+            # The extra pyside atom is sufficient to nudge Portage
+            # towards a solution but shouldn't be necessary.
+            # ResolverPlaygroundTestCase(
+            #     ["@world", "=dev-python/pyside-6.10.1"],
+            #     success=True,
+            #     options={"--update": True, "--deep": True},
+            #     mergelist=[
+            #         "dev-qt/qtbase-6.10.1",
+            #         "dev-qt/qttools-6.10.1",
+            #         "!<dev-qt/qttools-6.10.1:6",
+            #         "dev-qt/qttranslations-6.10.1",
+            #         "dev-qt/qtconnectivity-6.10.1",
+            #         "!<dev-qt/qtconnectivity-6.10.1:6",
+            #         "dev-qt/qtsvg-6.10.1",
+            #         "!<dev-qt/qtsvg-6.10.1:6",
+            #         "dev-python/pyside-6.10.1",
+            #         "media-gfx/freecad-1.0.1-r2",
+            #     ],
+            # ),
+            # It should resolve identically (or at least with a solution)
+            # without explicit dev-python/pyside, as it's a dependency of
+            # media-gfx/freecad.
+            ResolverPlaygroundTestCase(
+                ["@world"],
+                success=True,
+                options={"--update": True, "--deep": True},
+                mergelist=[
+                    "dev-qt/qtbase-6.10.1",
+                    "dev-qt/qttools-6.10.1",
+                    "!<dev-qt/qttools-6.10.1:6",
+                    "dev-qt/qttranslations-6.10.1",
+                    "dev-qt/qtconnectivity-6.10.1",
+                    "!<dev-qt/qtconnectivity-6.10.1:6",
+                    "dev-qt/qtsvg-6.10.1",
+                    "!<dev-qt/qtsvg-6.10.1:6",
+                    "dev-python/pyside-6.10.1",
+                    "media-gfx/freecad-1.0.1-r2",
+                ],
+            ),
+        )
+
+        playground = ResolverPlayground(
+            ebuilds=ebuilds, installed=installed, world=world
+        )
+        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()

Reply via email to