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)