proj/portage:master commit in: lib/portage/tests/ebuild/

"Matt Turner" <[email protected]>
Newsgroups gmane.linux.gentoo.cvs
Message-ID <1787000837.f05da60403fc3394954eb5cfd29e3e36d1fea504.mattst88@gentoo>
commit:     f05da60403fc3394954eb5cfd29e3e36d1fea504
Author:     Matt Turner <mattst88 <AT> gentoo <DOT> org>
AuthorDate: Sun Aug 16 21:39:44 2026 +0000
Commit:     Matt Turner <mattst88 <AT> gentoo <DOT> org>
CommitDate: Mon Aug 17 21:07:17 2026 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=f05da604

tests: cover self update with a relocated install

Emulate a portage update which installs to a different location: copy
the installed tree, run _prepare_self_update(), delete the tree, and
then import modules which were not imported beforehand, both in the
process itself and in a child. Repeat for the fork and forkserver
start methods.

Bug: https://bugs.gentoo.org/976616
Signed-off-by: Matt Turner <mattst88 <AT> gentoo.org>

 lib/portage/tests/ebuild/meson.build               |  1 +
 .../tests/ebuild/test_prepare_self_update.py       | 94 ++++++++++++++++++++++
 2 files changed, 95 insertions(+)

diff --git a/lib/portage/tests/ebuild/meson.build b/lib/portage/tests/ebuild/meson.build
index 6ef546f51..bca1c56d4 100644
--- a/lib/portage/tests/ebuild/meson.build
+++ b/lib/portage/tests/ebuild/meson.build
@@ -6,6 +6,7 @@ py.install_sources(
         'test_doebuild_spawn.py',
         'test_fetch.py',
         'test_ipc_daemon.py',
+        'test_prepare_self_update.py',
         'test_spawn.py',
         'test_use_expand_incremental.py',
         '__init__.py',

diff --git a/lib/portage/tests/ebuild/test_prepare_self_update.py b/lib/portage/tests/ebuild/test_prepare_self_update.py
new file mode 100644
index 000000000..9cb1ed753
--- /dev/null
+++ b/lib/portage/tests/ebuild/test_prepare_self_update.py
@@ -0,0 +1,94 @@
+# Copyright 2026 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+import os
+import shutil
+import subprocess
+import tempfile
+
+import portage
+from portage.const import PORTAGE_BIN_PATH, PORTAGE_PYM_PATH
+from portage.tests import TestCase
+
+# Runs in a subprocess with an installed copy of portage which is
+# deleted after _prepare_self_update() has run, in order to emulate a
+# self update which installs to a different location, as happens when
+# PYTHON_TARGETS changes (bug 976616).
+_SCRIPT = """
+import multiprocessing
+import os
+import shutil
+import sys
+
+def child():
+    import portage.dbapi.vartree
+
+
+if __name__ == "__main__":
+    sys.path.insert(0, sys.argv[1])
+
+    import portage
+    from portage.package.ebuild.doebuild import _prepare_self_update
+
+    # The modules imported below must not be imported before the update.
+    assert "portage.util.env_update" not in sys.modules
+    assert "portage.dbapi.vartree" not in sys.modules
+
+    _prepare_self_update({"PORTAGE_TMPDIR": sys.argv[2]})
+
+    shutil.rmtree(os.path.dirname(sys.argv[1]))
+
+    import portage.util.env_update
+
+    ctx = multiprocessing.get_context(sys.argv[3])
+    proc = ctx.Process(target=child)
+    proc.start()
+    proc.join()
+    sys.exit(proc.exitcode)
+"""
+
+
+class PrepareSelfUpdateTestCase(TestCase):
+    def testPrepareSelfUpdate(self):
+        tmpdir = tempfile.mkdtemp()
+        try:
+            script = os.path.join(tmpdir, "self_update.py")
+            with open(script, "w") as f:
+                f.write(_SCRIPT)
+
+            # PYTHONPATH would provide a second copy of the modules
+            # which are expected to become unimportable.
+            env = os.environ.copy()
+            env.pop("PYTHONPATH", None)
+
+            for start_method in ("fork", "forkserver"):
+                # The installed copy is deleted by the subprocess, so
+                # create a fresh one for each start method.
+                base_path = os.path.join(tmpdir, f"base-{start_method}")
+                pym_path = os.path.join(base_path, os.path.basename(PORTAGE_PYM_PATH))
+                os.mkdir(base_path)
+                shutil.copytree(PORTAGE_PYM_PATH, pym_path, symlinks=True)
+                shutil.copytree(
+                    PORTAGE_BIN_PATH,
+                    os.path.join(base_path, os.path.basename(PORTAGE_BIN_PATH)),
+                    symlinks=True,
+                )
+
+                proc = subprocess.run(
+                    [
+                        portage._python_interpreter,
+                        script,
+                        pym_path,
+                        tmpdir,
+                        start_method,
+                    ],
+                    env=env,
+                    stderr=subprocess.PIPE,
+                )
+                self.assertEqual(
+                    proc.returncode,
+                    os.EX_OK,
+                    msg=proc.stderr.decode(errors="replace"),
+                )
+        finally:
+            shutil.rmtree(tmpdir, ignore_errors=True)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.