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()