commit:     f41f766f3e1d0c60c34e4cb6a9e6b06c67d18fe1
Author:     Sam James <sam <AT> gentoo <DOT> org>
AuthorDate: Wed Jan 14 10:19:35 2026 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Wed Jan 14 10:20:20 2026 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=f41f766f

Scheduler: add test case for pkg_pretend bug

Add a testcase for the pkg_pretend --getbinpkg issue introduced in
14a21e6e4a6c5ef9ad9f84bae6b65fff5c00fdda and fixed by
a3f0843a3256ffe3f1248b903702d5b58c7ac892.

Signed-off-by: Sam James <sam <AT> gentoo.org>

 lib/portage/tests/emerge/test_binpkg_fetch.py | 204 ++++++++++++++++++++++++++
 1 file changed, 204 insertions(+)

diff --git a/lib/portage/tests/emerge/test_binpkg_fetch.py 
b/lib/portage/tests/emerge/test_binpkg_fetch.py
index 4995511156..8c15e02e79 100644
--- a/lib/portage/tests/emerge/test_binpkg_fetch.py
+++ b/lib/portage/tests/emerge/test_binpkg_fetch.py
@@ -5,6 +5,7 @@ import shutil
 import subprocess
 import sys
 import tempfile
+import textwrap
 
 import portage
 from portage import os
@@ -204,3 +205,206 @@ class BinpkgFetchtestCase(TestCase):
             playground.debug = False
             playground.cleanup()
             tmppkgdir.cleanup()
+
+    def testFetchBinpkgWithPkgPretend(self):
+        """
+        Make sure error handling for pkg_pretend w/ --getbinpkg works
+        correctly (fixed by a3f0843a3256ffe3f1248b903702d5b58c7ac892).
+        """
+        debug = False
+
+        pkg_pretend = textwrap.dedent(
+            """
+        S="${WORKDIR}"
+
+        pkg_pretend() {
+            einfo "Hello world!"
+        }
+        """
+        )
+
+        ebuilds = {
+            "dev-libs/A-1::local": {
+                "EAPI": "7",
+                "SLOT": "0",
+                "MISC_CONTENT": pkg_pretend,
+            },
+            "dev-libs/B-1::local": {
+                "EAPI": "7",
+                "SLOT": "0",
+                "MISC_CONTENT": pkg_pretend,
+            },
+        }
+
+        playground = ResolverPlayground(ebuilds=ebuilds, debug=debug)
+        settings = playground.settings
+        eprefix = settings["EPREFIX"]
+        eroot = settings["EROOT"]
+        trees = playground.trees
+        bindb = trees[eroot]["bintree"].dbapi
+        var_cache_edb = os.path.join(eprefix, "var", "cache", "edb")
+        user_config_dir = os.path.join(eprefix, USER_CONFIG_PATH)
+
+        portage_python = portage._python_interpreter
+        emerge_cmd = (
+            portage_python,
+            "-b",
+            "-Wd",
+            os.path.join(str(self.bindir), "emerge"),
+        )
+
+        tmppkgdir = tempfile.TemporaryDirectory()
+        tmppkgdir_suffix = os.path.join(tmppkgdir.name, "binpkg")
+
+        test_commands = (
+            # Create two trivial binpkgs first.
+            CommandStep(
+                returncode=os.EX_OK,
+                command=emerge_cmd
+                + ("--oneshot", "--verbose", "--buildpkg", "dev-libs/A", 
"dev-libs/B"),
+            ),
+            # Copy to a new PKGDIR which we'll use as PORTAGE_BINHOST
+            # then delete the old PKGDIR.
+            FunctionStep(
+                function=lambda _: shutil.copytree(
+                    bindb.bintree.pkgdir, tmppkgdir_suffix
+                )
+                or True,
+            ),
+            FunctionStep(
+                function=lambda _: os.unlink(
+                    os.path.join(
+                        bindb.bintree.pkgdir, "dev-libs", "A", "A-1-1.gpkg.tar"
+                    )
+                )
+            ),
+            FunctionStep(
+                function=lambda _: os.unlink(
+                    os.path.join(
+                        bindb.bintree.pkgdir, "dev-libs", "B", "B-1-1.gpkg.tar"
+                    )
+                )
+            ),
+            # Both dev-libs/A and dev-libs/B have a (passing) pkg_pretend to 
force
+            # taking the path that was broken before.
+            CommandStep(
+                returncode=os.EX_OK,
+                command=emerge_cmd
+                + (
+                    "--oneshot",
+                    "--verbose",
+                    "--jobs=2",
+                    "--getbinpkgonly",
+                    "dev-libs/A",
+                    "dev-libs/B",
+                ),
+            ),
+        )
+
+        fake_bin = os.path.join(eprefix, "bin")
+        portage_tmpdir = os.path.join(eprefix, "var", "tmp", "portage")
+
+        path = settings.get("PATH")
+        if path is not None and not path.strip():
+            path = None
+        if path is None:
+            path = ""
+        else:
+            path = ":" + path
+        path = fake_bin + path
+
+        pythonpath = os.environ.get("PYTHONPATH")
+        if pythonpath is not None and not pythonpath.strip():
+            pythonpath = None
+        if pythonpath is not None and pythonpath.split(":")[0] == 
PORTAGE_PYM_PATH:
+            pass
+        else:
+            if pythonpath is None:
+                pythonpath = ""
+            else:
+                pythonpath = ":" + pythonpath
+            pythonpath = PORTAGE_PYM_PATH + pythonpath
+
+        env = {
+            "PORTAGE_OVERRIDE_EPREFIX": eprefix,
+            "PATH": path,
+            "PORTAGE_PYTHON": portage_python,
+            "PORTAGE_REPOSITORIES": settings.repositories.config_string(),
+            "PYTHONDONTWRITEBYTECODE": 
os.environ.get("PYTHONDONTWRITEBYTECODE", ""),
+            "PYTHONPATH": pythonpath,
+            "PORTAGE_INST_GID": str(os.getgid()),
+            "PORTAGE_INST_UID": str(os.getuid()),
+            "FEATURES": "-parallel-fetch -pkgdir-index-trusted",
+        }
+
+        dirs = [
+            playground.distdir,
+            fake_bin,
+            portage_tmpdir,
+            user_config_dir,
+            var_cache_edb,
+        ]
+
+        true_symlinks = ["chown", "chgrp"]
+
+        needed_binaries = {
+            "true": (find_binary("true"), True),
+        }
+
+        try:
+            for d in dirs:
+                ensure_dirs(d)
+            for x in true_symlinks:
+                os.symlink(needed_binaries["true"][0], os.path.join(fake_bin, 
x))
+
+            with open(os.path.join(var_cache_edb, "counter"), "wb") as f:
+                f.write(b"100")
+
+            if debug:
+                # The subprocess inherits both stdout and stderr, for
+                # debugging purposes.
+                stdout = None
+            else:
+                # The subprocess inherits stderr so that any warnings
+                # triggered by python -Wd will be visible.
+                stdout = subprocess.PIPE
+
+            for i, step in enumerate(test_commands):
+                if isinstance(step, FunctionStep):
+                    try:
+                        step.function(i)
+                    except Exception as e:
+                        if isinstance(e, AssertionError) and f"step {i}" in 
str(e):
+                            raise
+                        raise AssertionError(
+                            f"step {i} raised {e.__class__.__name__}"
+                        ) from e
+                    continue
+
+                env["PORTAGE_BINHOST"] = f"file:///{tmppkgdir_suffix}"
+                proc = subprocess.Popen(
+                    step.command,
+                    env=dict(env.items(), **(step.env or {})),
+                    cwd=step.cwd,
+                    stdout=stdout,
+                )
+
+                if debug:
+                    proc.wait()
+                else:
+                    output = proc.stdout.readlines()
+                    proc.wait()
+                    proc.stdout.close()
+                    if proc.returncode != step.returncode:
+                        for line in output:
+                            sys.stderr.write(portage._unicode_decode(line))
+
+                self.assertEqual(
+                    step.returncode,
+                    proc.returncode,
+                    f"{step.command} (step {i}) failed with exit code 
{proc.returncode}",
+                )
+        finally:
+            playground.debug = False
+            playground.cleanup()
+            tmppkgdir.cleanup()

Reply via email to