diff options
author | Michał Górny <mgorny@gentoo.org> | 2018-07-17 21:50:45 +0200 |
---|---|---|
committer | Zac Medico <zmedico@gentoo.org> | 2018-07-18 16:19:11 -0700 |
commit | bc0fa8d3795ed7e40aaa00f579bb2977897bce25 (patch) | |
tree | 2a62c721ee8dec47ddb564254e1cbd967577d1f0 /lib/portage/tests/emerge | |
parent | EventLoop: raise TypeError for unexpected call_* keyword args (diff) | |
download | portage-bc0fa8d3795ed7e40aaa00f579bb2977897bce25.tar.gz portage-bc0fa8d3795ed7e40aaa00f579bb2977897bce25.tar.bz2 portage-bc0fa8d3795ed7e40aaa00f579bb2977897bce25.zip |
Rename pym→lib, for better distutils-r1 interoperability
Closes: https://github.com/gentoo/portage/pull/343
Diffstat (limited to 'lib/portage/tests/emerge')
-rw-r--r-- | lib/portage/tests/emerge/__init__.py | 2 | ||||
-rw-r--r-- | lib/portage/tests/emerge/__test__.py | 0 | ||||
-rw-r--r-- | lib/portage/tests/emerge/test_config_protect.py | 293 | ||||
-rw-r--r-- | lib/portage/tests/emerge/test_emerge_blocker_file_collision.py | 168 | ||||
-rw-r--r-- | lib/portage/tests/emerge/test_emerge_slot_abi.py | 179 | ||||
-rw-r--r-- | lib/portage/tests/emerge/test_global_updates.py | 41 | ||||
-rw-r--r-- | lib/portage/tests/emerge/test_simple.py | 505 |
7 files changed, 1188 insertions, 0 deletions
diff --git a/lib/portage/tests/emerge/__init__.py b/lib/portage/tests/emerge/__init__.py new file mode 100644 index 000000000..532918b6a --- /dev/null +++ b/lib/portage/tests/emerge/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2011 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 diff --git a/lib/portage/tests/emerge/__test__.py b/lib/portage/tests/emerge/__test__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/lib/portage/tests/emerge/__test__.py diff --git a/lib/portage/tests/emerge/test_config_protect.py b/lib/portage/tests/emerge/test_config_protect.py new file mode 100644 index 000000000..06ab059ee --- /dev/null +++ b/lib/portage/tests/emerge/test_config_protect.py @@ -0,0 +1,293 @@ +# Copyright 2014-2015 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from __future__ import unicode_literals + +import io +from functools import partial +import shutil +import stat +import subprocess +import sys +import time + +import portage +from portage import os +from portage import _encodings, _unicode_decode +from portage.const import BASH_BINARY, PORTAGE_PYM_PATH +from portage.process import find_binary +from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import ResolverPlayground +from portage.util import (ensure_dirs, find_updated_config_files, + shlex_split) + +class ConfigProtectTestCase(TestCase): + + def testConfigProtect(self): + """ + Demonstrates many different scenarios. For example: + + * regular file replaces regular file + * regular file replaces symlink + * regular file replaces directory + * symlink replaces symlink + * symlink replaces regular file + * symlink replaces directory + * directory replaces regular file + * directory replaces symlink + """ + + debug = False + + content_A_1 = """ +S="${WORKDIR}" + +src_install() { + insinto /etc/A + keepdir /etc/A/dir_a + keepdir /etc/A/symlink_replaces_dir + keepdir /etc/A/regular_replaces_dir + echo regular_a_1 > "${T}"/regular_a + doins "${T}"/regular_a + echo regular_b_1 > "${T}"/regular_b + doins "${T}"/regular_b + dosym regular_a /etc/A/regular_replaces_symlink + dosym regular_b /etc/A/symlink_replaces_symlink + echo regular_replaces_regular_1 > \ + "${T}"/regular_replaces_regular + doins "${T}"/regular_replaces_regular + echo symlink_replaces_regular > \ + "${T}"/symlink_replaces_regular + doins "${T}"/symlink_replaces_regular +} + +""" + + content_A_2 = """ +S="${WORKDIR}" + +src_install() { + insinto /etc/A + keepdir /etc/A/dir_a + dosym dir_a /etc/A/symlink_replaces_dir + echo regular_replaces_dir > "${T}"/regular_replaces_dir + doins "${T}"/regular_replaces_dir + echo regular_a_2 > "${T}"/regular_a + doins "${T}"/regular_a + echo regular_b_2 > "${T}"/regular_b + doins "${T}"/regular_b + echo regular_replaces_symlink > \ + "${T}"/regular_replaces_symlink + doins "${T}"/regular_replaces_symlink + dosym regular_b /etc/A/symlink_replaces_symlink + echo regular_replaces_regular_2 > \ + "${T}"/regular_replaces_regular + doins "${T}"/regular_replaces_regular + dosym regular_a /etc/A/symlink_replaces_regular +} + +""" + + ebuilds = { + "dev-libs/A-1": { + "EAPI" : "5", + "IUSE" : "+flag", + "KEYWORDS": "x86", + "LICENSE": "GPL-2", + "MISC_CONTENT": content_A_1, + }, + "dev-libs/A-2": { + "EAPI" : "5", + "IUSE" : "+flag", + "KEYWORDS": "x86", + "LICENSE": "GPL-2", + "MISC_CONTENT": content_A_2, + }, + } + + playground = ResolverPlayground( + ebuilds=ebuilds, debug=debug) + settings = playground.settings + eprefix = settings["EPREFIX"] + eroot = settings["EROOT"] + var_cache_edb = os.path.join(eprefix, "var", "cache", "edb") + + portage_python = portage._python_interpreter + dispatch_conf_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.sbindir, "dispatch-conf")) + emerge_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.bindir, "emerge")) + etc_update_cmd = (BASH_BINARY, + os.path.join(self.sbindir, "etc-update")) + etc_update_auto = etc_update_cmd + ("--automode", "-5",) + + config_protect = "/etc" + + def modify_files(dir_path): + for name in os.listdir(dir_path): + path = os.path.join(dir_path, name) + st = os.lstat(path) + if stat.S_ISREG(st.st_mode): + with io.open(path, mode='a', + encoding=_encodings["stdio"]) as f: + f.write("modified at %d\n" % time.time()) + elif stat.S_ISLNK(st.st_mode): + old_dest = os.readlink(path) + os.unlink(path) + os.symlink(old_dest + + " modified at %d" % time.time(), path) + + def updated_config_files(count): + self.assertEqual(count, + sum(len(x[1]) for x in find_updated_config_files(eroot, + shlex_split(config_protect)))) + + test_commands = ( + etc_update_cmd, + dispatch_conf_cmd, + emerge_cmd + ("-1", "=dev-libs/A-1"), + partial(updated_config_files, 0), + emerge_cmd + ("-1", "=dev-libs/A-2"), + partial(updated_config_files, 2), + etc_update_auto, + partial(updated_config_files, 0), + emerge_cmd + ("-1", "=dev-libs/A-2"), + partial(updated_config_files, 0), + # Test bug #523684, where a file renamed or removed by the + # admin forces replacement files to be merged with config + # protection. + partial(shutil.rmtree, + os.path.join(eprefix, "etc", "A")), + emerge_cmd + ("-1", "=dev-libs/A-2"), + partial(updated_config_files, 8), + etc_update_auto, + partial(updated_config_files, 0), + # Modify some config files, and verify that it triggers + # config protection. + partial(modify_files, + os.path.join(eroot, "etc", "A")), + emerge_cmd + ("-1", "=dev-libs/A-2"), + partial(updated_config_files, 6), + etc_update_auto, + partial(updated_config_files, 0), + # Modify some config files, downgrade to A-1, and verify + # that config protection works properly when the file + # types are changing. + partial(modify_files, + os.path.join(eroot, "etc", "A")), + emerge_cmd + ("-1", "--noconfmem", "=dev-libs/A-1"), + partial(updated_config_files, 6), + etc_update_auto, + partial(updated_config_files, 0), + ) + + distdir = playground.distdir + fake_bin = os.path.join(eprefix, "bin") + portage_tmpdir = os.path.join(eprefix, "var", "tmp", "portage") + + path = os.environ.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, + "CLEAN_DELAY" : "0", + "CONFIG_PROTECT": config_protect, + "DISTDIR" : distdir, + "EMERGE_DEFAULT_OPTS": "-v", + "EMERGE_WARNING_DELAY" : "0", + "INFODIR" : "", + "INFOPATH" : "", + "PATH" : path, + "PORTAGE_INST_GID" : str(portage.data.portage_gid), + "PORTAGE_INST_UID" : str(portage.data.portage_uid), + "PORTAGE_PYTHON" : portage_python, + "PORTAGE_REPOSITORIES" : settings.repositories.config_string(), + "PORTAGE_TMPDIR" : portage_tmpdir, + "PYTHONDONTWRITEBYTECODE" : os.environ.get("PYTHONDONTWRITEBYTECODE", ""), + "PYTHONPATH" : pythonpath, + "__PORTAGE_TEST_PATH_OVERRIDE" : fake_bin, + } + + if "__PORTAGE_TEST_HARDLINK_LOCKS" in os.environ: + env["__PORTAGE_TEST_HARDLINK_LOCKS"] = \ + os.environ["__PORTAGE_TEST_HARDLINK_LOCKS"] + + dirs = [distdir, fake_bin, portage_tmpdir, + var_cache_edb] + etc_symlinks = ("dispatch-conf.conf", "etc-update.conf") + # Override things that may be unavailable, or may have portability + # issues when running tests in exotic environments. + # prepstrip - bug #447810 (bash read builtin EINTR problem) + true_symlinks = ["prepstrip", "scanelf"] + true_binary = find_binary("true") + self.assertEqual(true_binary is None, False, + "true command not found") + try: + for d in dirs: + ensure_dirs(d) + for x in true_symlinks: + os.symlink(true_binary, os.path.join(fake_bin, x)) + for x in etc_symlinks: + os.symlink(os.path.join(self.cnf_etc_path, x), + os.path.join(eprefix, "etc", 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 args in test_commands: + + if hasattr(args, '__call__'): + args() + continue + + if isinstance(args[0], dict): + local_env = env.copy() + local_env.update(args[0]) + args = args[1:] + else: + local_env = env + + proc = subprocess.Popen(args, + env=local_env, stdout=stdout) + + if debug: + proc.wait() + else: + output = proc.stdout.readlines() + proc.wait() + proc.stdout.close() + if proc.returncode != os.EX_OK: + for line in output: + sys.stderr.write(_unicode_decode(line)) + + self.assertEqual(os.EX_OK, proc.returncode, + "emerge failed with args %s" % (args,)) + finally: + playground.cleanup() diff --git a/lib/portage/tests/emerge/test_emerge_blocker_file_collision.py b/lib/portage/tests/emerge/test_emerge_blocker_file_collision.py new file mode 100644 index 000000000..10d09d843 --- /dev/null +++ b/lib/portage/tests/emerge/test_emerge_blocker_file_collision.py @@ -0,0 +1,168 @@ +# Copyright 2016 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import subprocess +import sys + +import portage +from portage import os +from portage import _unicode_decode +from portage.const import PORTAGE_PYM_PATH, USER_CONFIG_PATH +from portage.process import find_binary +from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import ResolverPlayground +from portage.util import ensure_dirs + +class BlockerFileCollisionEmergeTestCase(TestCase): + + def testBlockerFileCollision(self): + + debug = False + + install_something = """ +S="${WORKDIR}" + +src_install() { + einfo "installing something..." + insinto /usr/lib + echo "${PN}" > "${T}/file-collision" + doins "${T}/file-collision" +} +""" + + ebuilds = { + "dev-libs/A-1" : { + "EAPI": "6", + "MISC_CONTENT": install_something, + "RDEPEND": "!dev-libs/B", + }, + "dev-libs/B-1" : { + "EAPI": "6", + "MISC_CONTENT": install_something, + "RDEPEND": "!dev-libs/A", + }, + } + + playground = ResolverPlayground(ebuilds=ebuilds, debug=debug) + settings = playground.settings + eprefix = settings["EPREFIX"] + eroot = settings["EROOT"] + 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(self.bindir, "emerge")) + + file_collision = os.path.join(eroot, 'usr/lib/file-collision') + + test_commands = ( + emerge_cmd + ("--oneshot", "dev-libs/A",), + (lambda: portage.util.grablines(file_collision) == ["A\n"],), + emerge_cmd + ("--oneshot", "dev-libs/B",), + (lambda: portage.util.grablines(file_collision) == ["B\n"],), + emerge_cmd + ("--oneshot", "dev-libs/A",), + (lambda: portage.util.grablines(file_collision) == ["A\n"],), + ({"FEATURES":"parallel-install"},) + emerge_cmd + ("--oneshot", "dev-libs/B",), + (lambda: portage.util.grablines(file_collision) == ["B\n"],), + ({"FEATURES":"parallel-install"},) + emerge_cmd + ("-Cq", "dev-libs/B",), + (lambda: not os.path.exists(file_collision),), + ) + + fake_bin = os.path.join(eprefix, "bin") + portage_tmpdir = os.path.join(eprefix, "var", "tmp", "portage") + profile_path = settings.profile_path + + path = os.environ.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, + } + + if "__PORTAGE_TEST_HARDLINK_LOCKS" in os.environ: + env["__PORTAGE_TEST_HARDLINK_LOCKS"] = \ + os.environ["__PORTAGE_TEST_HARDLINK_LOCKS"] + + dirs = [playground.distdir, fake_bin, portage_tmpdir, + user_config_dir, var_cache_edb] + true_symlinks = ["chown", "chgrp"] + true_binary = find_binary("true") + self.assertEqual(true_binary is None, False, + "true command not found") + try: + for d in dirs: + ensure_dirs(d) + for x in true_symlinks: + os.symlink(true_binary, os.path.join(fake_bin, x)) + with open(os.path.join(var_cache_edb, "counter"), 'wb') as f: + f.write(b"100") + # non-empty system set keeps --depclean quiet + with open(os.path.join(profile_path, "packages"), 'w') as f: + f.write("*dev-libs/token-system-pkg") + + 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, args in enumerate(test_commands): + + if hasattr(args[0], '__call__'): + self.assertTrue(args[0](), + "callable at index %s failed" % (i,)) + continue + + if isinstance(args[0], dict): + local_env = env.copy() + local_env.update(args[0]) + args = args[1:] + else: + local_env = env + + proc = subprocess.Popen(args, + env=local_env, stdout=stdout) + + if debug: + proc.wait() + else: + output = proc.stdout.readlines() + proc.wait() + proc.stdout.close() + if proc.returncode != os.EX_OK: + for line in output: + sys.stderr.write(_unicode_decode(line)) + + self.assertEqual(os.EX_OK, proc.returncode, + "emerge failed with args %s" % (args,)) + finally: + playground.debug = False + playground.cleanup() diff --git a/lib/portage/tests/emerge/test_emerge_slot_abi.py b/lib/portage/tests/emerge/test_emerge_slot_abi.py new file mode 100644 index 000000000..200699396 --- /dev/null +++ b/lib/portage/tests/emerge/test_emerge_slot_abi.py @@ -0,0 +1,179 @@ +# Copyright 2012-2015 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import subprocess +import sys + +import portage +from portage import os +from portage import _unicode_decode +from portage.const import (BASH_BINARY, PORTAGE_PYM_PATH, USER_CONFIG_PATH) +from portage.process import find_binary +from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import ResolverPlayground +from portage.util import ensure_dirs + +class SlotAbiEmergeTestCase(TestCase): + + def testSlotAbiEmerge(self): + + debug = False + + ebuilds = { + "dev-libs/glib-1.2.10" : { + "SLOT": "1" + }, + "dev-libs/glib-2.30.2" : { + "EAPI": "4-slot-abi", + "SLOT": "2/2.30" + }, + "dev-libs/glib-2.32.3" : { + "EAPI": "4-slot-abi", + "SLOT": "2/2.32" + }, + "dev-libs/dbus-glib-0.98" : { + "EAPI": "4-slot-abi", + "DEPEND": "dev-libs/glib:2=", + "RDEPEND": "dev-libs/glib:2=" + }, + } + installed = { + "dev-libs/glib-1.2.10" : { + "EAPI": "4-slot-abi", + "SLOT": "1" + }, + "dev-libs/glib-2.30.2" : { + "EAPI": "4-slot-abi", + "SLOT": "2/2.30" + }, + "dev-libs/dbus-glib-0.98" : { + "EAPI": "4-slot-abi", + "DEPEND": "dev-libs/glib:2/2.30=", + "RDEPEND": "dev-libs/glib:2/2.30=" + }, + } + + world = ["dev-libs/glib:1", "dev-libs/dbus-glib"] + + playground = ResolverPlayground(ebuilds=ebuilds, + installed=installed, world=world, debug=debug) + settings = playground.settings + eprefix = settings["EPREFIX"] + eroot = settings["EROOT"] + trees = playground.trees + portdb = trees[eroot]["porttree"].dbapi + vardb = trees[eroot]["vartree"].dbapi + var_cache_edb = os.path.join(eprefix, "var", "cache", "edb") + user_config_dir = os.path.join(eprefix, USER_CONFIG_PATH) + package_mask_path = os.path.join(user_config_dir, "package.mask") + + portage_python = portage._python_interpreter + ebuild_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.bindir, "ebuild")) + emerge_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.bindir, "emerge")) + + test_ebuild = portdb.findname("dev-libs/dbus-glib-0.98") + self.assertFalse(test_ebuild is None) + + test_commands = ( + emerge_cmd + ("--oneshot", "dev-libs/glib",), + (lambda: "dev-libs/glib:2/2.32=" in vardb.aux_get("dev-libs/dbus-glib-0.98", ["RDEPEND"])[0],), + (BASH_BINARY, "-c", "echo %s >> %s" % + tuple(map(portage._shell_quote, + (">=dev-libs/glib-2.32", package_mask_path,)))), + emerge_cmd + ("--oneshot", "dev-libs/glib",), + (lambda: "dev-libs/glib:2/2.30=" in vardb.aux_get("dev-libs/dbus-glib-0.98", ["RDEPEND"])[0],), + ) + + distdir = playground.distdir + pkgdir = playground.pkgdir + fake_bin = os.path.join(eprefix, "bin") + portage_tmpdir = os.path.join(eprefix, "var", "tmp", "portage") + profile_path = settings.profile_path + + path = os.environ.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, + } + + if "__PORTAGE_TEST_HARDLINK_LOCKS" in os.environ: + env["__PORTAGE_TEST_HARDLINK_LOCKS"] = \ + os.environ["__PORTAGE_TEST_HARDLINK_LOCKS"] + + dirs = [distdir, fake_bin, portage_tmpdir, + user_config_dir, var_cache_edb] + true_symlinks = ["chown", "chgrp"] + true_binary = find_binary("true") + self.assertEqual(true_binary is None, False, + "true command not found") + try: + for d in dirs: + ensure_dirs(d) + for x in true_symlinks: + os.symlink(true_binary, os.path.join(fake_bin, x)) + with open(os.path.join(var_cache_edb, "counter"), 'wb') as f: + f.write(b"100") + # non-empty system set keeps --depclean quiet + with open(os.path.join(profile_path, "packages"), 'w') as f: + f.write("*dev-libs/token-system-pkg") + + 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, args in enumerate(test_commands): + + if hasattr(args[0], '__call__'): + self.assertTrue(args[0](), + "callable at index %s failed" % (i,)) + continue + + proc = subprocess.Popen(args, + env=env, stdout=stdout) + + if debug: + proc.wait() + else: + output = proc.stdout.readlines() + proc.wait() + proc.stdout.close() + if proc.returncode != os.EX_OK: + for line in output: + sys.stderr.write(_unicode_decode(line)) + + self.assertEqual(os.EX_OK, proc.returncode, + "emerge failed with args %s" % (args,)) + finally: + playground.cleanup() diff --git a/lib/portage/tests/emerge/test_global_updates.py b/lib/portage/tests/emerge/test_global_updates.py new file mode 100644 index 000000000..eb5431059 --- /dev/null +++ b/lib/portage/tests/emerge/test_global_updates.py @@ -0,0 +1,41 @@ +# Copyright 2011-2012 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from portage.tests import TestCase +from portage.update import parse_updates +from portage.dep import Atom + +class ParseUpdatesTestCase(TestCase): + + def testParseUpdates(self): + test_cases = ( + ( + """ +slotmove invalid_atom 0 3 +slotmove !=invalid/blocker-3* 0 3 +slotmove =valid/atom-3* 0 3 invalid_extra_token +slotmove =valid/atom-3* 0 3 +slotmove =valid/atom-3* 0 3/3.1 +slotmove =valid/atom-3* 0/0 3 +move valid/atom1 valid/atom2 invalid_extra_token +move valid/atom1 invalid_atom2 +move invalid_atom1 valid/atom2 +move !invalid/blocker1 valid/atom2 +move valid/atom1 !invalid/blocker2 +move =invalid/operator-1* valid/atom2 +move valid/atom1 =invalid/operator-2* +move valid/atom1 valid/atom2 +""", + [ + ['slotmove', Atom('=valid/atom-3*'), '0', '3'], + ['move', Atom('valid/atom1'), Atom('valid/atom2')], + ], + 12, + ), + + ) + + for input_content, expected_output, expected_error_count in test_cases: + output_data, errors = parse_updates(input_content) + self.assertEqual(output_data, expected_output) + self.assertEqual(len(errors), expected_error_count) diff --git a/lib/portage/tests/emerge/test_simple.py b/lib/portage/tests/emerge/test_simple.py new file mode 100644 index 000000000..b1402ddd5 --- /dev/null +++ b/lib/portage/tests/emerge/test_simple.py @@ -0,0 +1,505 @@ +# Copyright 2011-2018 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import subprocess +import sys + +import portage +from portage import os +from portage import _unicode_decode +from portage.const import (BASH_BINARY, PORTAGE_BASE_PATH, + PORTAGE_PYM_PATH, USER_CONFIG_PATH) +from portage.process import find_binary +from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import ResolverPlayground +from portage.util import (ensure_dirs, find_updated_config_files, + shlex_split) + +class SimpleEmergeTestCase(TestCase): + + def _have_python_xml(self): + try: + __import__("xml.etree.ElementTree") + __import__("xml.parsers.expat").parsers.expat.ExpatError + except (AttributeError, ImportError): + return False + return True + + def testSimple(self): + + debug = False + + install_something = """ +S="${WORKDIR}" + +pkg_pretend() { + einfo "called pkg_pretend for $CATEGORY/$PF" +} + +src_install() { + einfo "installing something..." + insinto /usr/lib/${P} + echo "blah blah blah" > "${T}"/regular-file + doins "${T}"/regular-file + dosym regular-file /usr/lib/${P}/symlink || die + + # Test CONFIG_PROTECT + insinto /etc + newins "${T}"/regular-file ${PN}-${SLOT%/*} + + # Test code for bug #381629, using a copyright symbol encoded with latin-1. + # We use $(printf "\\xa9") rather than $'\\xa9', since printf apparently + # works in any case, while $'\\xa9' transforms to \\xef\\xbf\\xbd under + # some conditions. TODO: Find out why it transforms to \\xef\\xbf\\xbd when + # running tests for Python 3.2 (even though it's bash that is ultimately + # responsible for performing the transformation). + local latin_1_dir=/usr/lib/${P}/latin-1-$(printf "\\xa9")-directory + insinto "${latin_1_dir}" + echo "blah blah blah" > "${T}"/latin-1-$(printf "\\xa9")-regular-file || die + doins "${T}"/latin-1-$(printf "\\xa9")-regular-file + dosym latin-1-$(printf "\\xa9")-regular-file ${latin_1_dir}/latin-1-$(printf "\\xa9")-symlink || die + + call_has_and_best_version +} + +pkg_config() { + einfo "called pkg_config for $CATEGORY/$PF" +} + +pkg_info() { + einfo "called pkg_info for $CATEGORY/$PF" +} + +pkg_preinst() { + if ! ___eapi_best_version_and_has_version_support_-b_-d_-r; then + # The BROOT variable is unset during pkg_* phases for EAPI 7, + # therefore best/has_version -b is expected to fail if we attempt + # to call it for EAPI 7 here. + call_has_and_best_version + fi +} + +call_has_and_best_version() { + local root_arg + if ___eapi_best_version_and_has_version_support_-b_-d_-r; then + root_arg="-b" + else + root_arg="--host-root" + fi + einfo "called ${EBUILD_PHASE_FUNC} for $CATEGORY/$PF" + einfo "EPREFIX=${EPREFIX}" + einfo "PORTAGE_OVERRIDE_EPREFIX=${PORTAGE_OVERRIDE_EPREFIX}" + einfo "ROOT=${ROOT}" + einfo "EROOT=${EROOT}" + einfo "SYSROOT=${SYSROOT}" + einfo "ESYSROOT=${ESYSROOT}" + einfo "BROOT=${BROOT}" + # Test that has_version and best_version work correctly with + # prefix (involves internal ROOT -> EROOT calculation in order + # to support ROOT override via the environment with EAPIs 3 + # and later which support prefix). + if has_version $CATEGORY/$PN:$SLOT ; then + einfo "has_version detects an installed instance of $CATEGORY/$PN:$SLOT" + einfo "best_version reports that the installed instance is $(best_version $CATEGORY/$PN:$SLOT)" + else + einfo "has_version does not detect an installed instance of $CATEGORY/$PN:$SLOT" + fi + if [[ ${EPREFIX} != ${PORTAGE_OVERRIDE_EPREFIX} ]] ; then + if has_version ${root_arg} $CATEGORY/$PN:$SLOT ; then + einfo "has_version ${root_arg} detects an installed instance of $CATEGORY/$PN:$SLOT" + einfo "best_version ${root_arg} reports that the installed instance is $(best_version ${root_arg} $CATEGORY/$PN:$SLOT)" + else + einfo "has_version ${root_arg} does not detect an installed instance of $CATEGORY/$PN:$SLOT" + fi + fi +} + +""" + + ebuilds = { + "dev-libs/A-1": { + "EAPI" : "5", + "IUSE" : "+flag", + "KEYWORDS": "x86", + "LICENSE": "GPL-2", + "MISC_CONTENT": install_something, + "RDEPEND": "flag? ( dev-libs/B[flag] )", + }, + "dev-libs/B-1": { + "EAPI" : "5", + "IUSE" : "+flag", + "KEYWORDS": "x86", + "LICENSE": "GPL-2", + "MISC_CONTENT": install_something, + }, + "dev-libs/C-1": { + "EAPI" : "7", + "KEYWORDS": "~x86", + "RDEPEND": "dev-libs/D[flag]", + "MISC_CONTENT": install_something, + }, + "dev-libs/D-1": { + "EAPI" : "7", + "KEYWORDS": "~x86", + "IUSE" : "flag", + "MISC_CONTENT": install_something, + }, + "virtual/foo-0": { + "EAPI" : "5", + "KEYWORDS": "x86", + "LICENSE": "GPL-2", + }, + } + + installed = { + "dev-libs/A-1": { + "EAPI" : "5", + "IUSE" : "+flag", + "KEYWORDS": "x86", + "LICENSE": "GPL-2", + "RDEPEND": "flag? ( dev-libs/B[flag] )", + "USE": "flag", + }, + "dev-libs/B-1": { + "EAPI" : "5", + "IUSE" : "+flag", + "KEYWORDS": "x86", + "LICENSE": "GPL-2", + "USE": "flag", + }, + "dev-libs/depclean-me-1": { + "EAPI" : "5", + "IUSE" : "", + "KEYWORDS": "x86", + "LICENSE": "GPL-2", + "USE": "", + }, + "app-misc/depclean-me-1": { + "EAPI" : "5", + "IUSE" : "", + "KEYWORDS": "x86", + "LICENSE": "GPL-2", + "RDEPEND": "dev-libs/depclean-me", + "USE": "", + }, + } + + metadata_xml_files = ( + ( + "dev-libs/A", + { + "flags" : "<flag name='flag'>Description of how USE='flag' affects this package</flag>", + }, + ), + ( + "dev-libs/B", + { + "flags" : "<flag name='flag'>Description of how USE='flag' affects this package</flag>", + }, + ), + ) + + playground = ResolverPlayground( + ebuilds=ebuilds, installed=installed, debug=debug) + settings = playground.settings + eprefix = settings["EPREFIX"] + eroot = settings["EROOT"] + trees = playground.trees + portdb = trees[eroot]["porttree"].dbapi + test_repo_location = settings.repositories["test_repo"].location + var_cache_edb = os.path.join(eprefix, "var", "cache", "edb") + cachedir = os.path.join(var_cache_edb, "dep") + cachedir_pregen = os.path.join(test_repo_location, "metadata", "md5-cache") + + portage_python = portage._python_interpreter + dispatch_conf_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.sbindir, "dispatch-conf")) + ebuild_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.bindir, "ebuild")) + egencache_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.bindir, "egencache"), + "--repo", "test_repo", + "--repositories-configuration", settings.repositories.config_string()) + emerge_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.bindir, "emerge")) + emaint_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.sbindir, "emaint")) + env_update_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.sbindir, "env-update")) + etc_update_cmd = (BASH_BINARY, + os.path.join(self.sbindir, "etc-update")) + fixpackages_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.sbindir, "fixpackages")) + portageq_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.bindir, "portageq")) + quickpkg_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.bindir, "quickpkg")) + regenworld_cmd = (portage_python, "-b", "-Wd", + os.path.join(self.sbindir, "regenworld")) + + rm_binary = find_binary("rm") + self.assertEqual(rm_binary is None, False, + "rm command not found") + rm_cmd = (rm_binary,) + + egencache_extra_args = [] + if self._have_python_xml(): + egencache_extra_args.append("--update-use-local-desc") + + test_ebuild = portdb.findname("dev-libs/A-1") + self.assertFalse(test_ebuild is None) + + cross_prefix = os.path.join(eprefix, "cross_prefix") + cross_root = os.path.join(eprefix, "cross_root") + cross_eroot = os.path.join(cross_root, eprefix.lstrip(os.sep)) + + test_commands = ( + env_update_cmd, + portageq_cmd + ("envvar", "-v", "CONFIG_PROTECT", "EROOT", + "PORTAGE_CONFIGROOT", "PORTAGE_TMPDIR", "USERLAND"), + etc_update_cmd, + dispatch_conf_cmd, + emerge_cmd + ("--version",), + emerge_cmd + ("--info",), + emerge_cmd + ("--info", "--verbose"), + emerge_cmd + ("--list-sets",), + emerge_cmd + ("--check-news",), + rm_cmd + ("-rf", cachedir), + rm_cmd + ("-rf", cachedir_pregen), + emerge_cmd + ("--regen",), + rm_cmd + ("-rf", cachedir), + ({"FEATURES" : "metadata-transfer"},) + \ + emerge_cmd + ("--regen",), + rm_cmd + ("-rf", cachedir), + ({"FEATURES" : "metadata-transfer"},) + \ + emerge_cmd + ("--regen",), + rm_cmd + ("-rf", cachedir), + egencache_cmd + ("--update",) + tuple(egencache_extra_args), + ({"FEATURES" : "metadata-transfer"},) + \ + emerge_cmd + ("--metadata",), + rm_cmd + ("-rf", cachedir), + ({"FEATURES" : "metadata-transfer"},) + \ + emerge_cmd + ("--metadata",), + emerge_cmd + ("--metadata",), + rm_cmd + ("-rf", cachedir), + emerge_cmd + ("--oneshot", "virtual/foo"), + lambda: self.assertFalse(os.path.exists( + os.path.join(pkgdir, "virtual", "foo-0.tbz2"))), + ({"FEATURES" : "unmerge-backup"},) + \ + emerge_cmd + ("--unmerge", "virtual/foo"), + lambda: self.assertTrue(os.path.exists( + os.path.join(pkgdir, "virtual", "foo-0.tbz2"))), + emerge_cmd + ("--pretend", "dev-libs/A"), + ebuild_cmd + (test_ebuild, "manifest", "clean", "package", "merge"), + emerge_cmd + ("--pretend", "--tree", "--complete-graph", "dev-libs/A"), + emerge_cmd + ("-p", "dev-libs/B"), + emerge_cmd + ("-p", "--newrepo", "dev-libs/B"), + emerge_cmd + ("-B", "dev-libs/B",), + emerge_cmd + ("--oneshot", "--usepkg", "dev-libs/B",), + + # trigger clean prior to pkg_pretend as in bug #390711 + ebuild_cmd + (test_ebuild, "unpack"), + emerge_cmd + ("--oneshot", "dev-libs/A",), + + emerge_cmd + ("--noreplace", "dev-libs/A",), + emerge_cmd + ("--config", "dev-libs/A",), + emerge_cmd + ("--info", "dev-libs/A", "dev-libs/B"), + emerge_cmd + ("--pretend", "--depclean", "--verbose", "dev-libs/B"), + emerge_cmd + ("--pretend", "--depclean",), + emerge_cmd + ("--depclean",), + quickpkg_cmd + ("--include-config", "y", "dev-libs/A",), + # Test bug #523684, where a file renamed or removed by the + # admin forces replacement files to be merged with config + # protection. + lambda: self.assertEqual(0, + len(list(find_updated_config_files(eroot, + shlex_split(settings["CONFIG_PROTECT"]))))), + lambda: os.unlink(os.path.join(eprefix, "etc", "A-0")), + emerge_cmd + ("--usepkgonly", "dev-libs/A"), + lambda: self.assertEqual(1, + len(list(find_updated_config_files(eroot, + shlex_split(settings["CONFIG_PROTECT"]))))), + emaint_cmd + ("--check", "all"), + emaint_cmd + ("--fix", "all"), + fixpackages_cmd, + regenworld_cmd, + portageq_cmd + ("match", eroot, "dev-libs/A"), + portageq_cmd + ("best_visible", eroot, "dev-libs/A"), + portageq_cmd + ("best_visible", eroot, "binary", "dev-libs/A"), + portageq_cmd + ("contents", eroot, "dev-libs/A-1"), + portageq_cmd + ("metadata", eroot, "ebuild", "dev-libs/A-1", "EAPI", "IUSE", "RDEPEND"), + portageq_cmd + ("metadata", eroot, "binary", "dev-libs/A-1", "EAPI", "USE", "RDEPEND"), + portageq_cmd + ("metadata", eroot, "installed", "dev-libs/A-1", "EAPI", "USE", "RDEPEND"), + portageq_cmd + ("owners", eroot, eroot + "usr"), + emerge_cmd + ("-p", eroot + "usr"), + emerge_cmd + ("-p", "--unmerge", "-q", eroot + "usr"), + emerge_cmd + ("--unmerge", "--quiet", "dev-libs/A"), + emerge_cmd + ("-C", "--quiet", "dev-libs/B"), + + # If EMERGE_DEFAULT_OPTS contains --autounmask=n, then --autounmask + # must be specified with --autounmask-continue. + ({"EMERGE_DEFAULT_OPTS" : "--autounmask=n"},) + \ + emerge_cmd + ("--autounmask", "--autounmask-continue", "dev-libs/C",), + # Verify that the above --autounmask-continue command caused + # USE=flag to be applied correctly to dev-libs/D. + portageq_cmd + ("match", eroot, "dev-libs/D[flag]"), + + # Test cross-prefix usage, including chpathtool for binpkgs. + # EAPI 7 + ({"EPREFIX" : cross_prefix},) + \ + emerge_cmd + ("dev-libs/C",), + ({"EPREFIX" : cross_prefix},) + \ + portageq_cmd + ("has_version", cross_prefix, "dev-libs/C"), + ({"EPREFIX" : cross_prefix},) + \ + portageq_cmd + ("has_version", cross_prefix, "dev-libs/D"), + ({"ROOT": cross_root},) + emerge_cmd + ("dev-libs/D",), + portageq_cmd + ("has_version", cross_eroot, "dev-libs/D"), + # EAPI 5 + ({"EPREFIX" : cross_prefix},) + \ + emerge_cmd + ("--usepkgonly", "dev-libs/A"), + ({"EPREFIX" : cross_prefix},) + \ + portageq_cmd + ("has_version", cross_prefix, "dev-libs/A"), + ({"EPREFIX" : cross_prefix},) + \ + portageq_cmd + ("has_version", cross_prefix, "dev-libs/B"), + ({"EPREFIX" : cross_prefix},) + \ + emerge_cmd + ("-C", "--quiet", "dev-libs/B"), + ({"EPREFIX" : cross_prefix},) + \ + emerge_cmd + ("-C", "--quiet", "dev-libs/A"), + ({"EPREFIX" : cross_prefix},) + \ + emerge_cmd + ("dev-libs/A",), + ({"EPREFIX" : cross_prefix},) + \ + portageq_cmd + ("has_version", cross_prefix, "dev-libs/A"), + ({"EPREFIX" : cross_prefix},) + \ + portageq_cmd + ("has_version", cross_prefix, "dev-libs/B"), + + # Test ROOT support + ({"ROOT": cross_root},) + emerge_cmd + ("dev-libs/B",), + portageq_cmd + ("has_version", cross_eroot, "dev-libs/B"), + ) + + distdir = playground.distdir + pkgdir = playground.pkgdir + fake_bin = os.path.join(eprefix, "bin") + portage_tmpdir = os.path.join(eprefix, "var", "tmp", "portage") + profile_path = settings.profile_path + user_config_dir = os.path.join(os.sep, eprefix, USER_CONFIG_PATH) + + path = os.environ.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, + "CLEAN_DELAY" : "0", + "DISTDIR" : distdir, + "EMERGE_WARNING_DELAY" : "0", + "INFODIR" : "", + "INFOPATH" : "", + "PATH" : path, + "PKGDIR" : pkgdir, + "PORTAGE_INST_GID" : str(portage.data.portage_gid), + "PORTAGE_INST_UID" : str(portage.data.portage_uid), + "PORTAGE_PYTHON" : portage_python, + "PORTAGE_REPOSITORIES" : settings.repositories.config_string(), + "PORTAGE_TMPDIR" : portage_tmpdir, + "PORT_LOGDIR" : portage_tmpdir, + "PYTHONDONTWRITEBYTECODE" : os.environ.get("PYTHONDONTWRITEBYTECODE", ""), + "PYTHONPATH" : pythonpath, + "__PORTAGE_TEST_PATH_OVERRIDE" : fake_bin, + } + + if "__PORTAGE_TEST_HARDLINK_LOCKS" in os.environ: + env["__PORTAGE_TEST_HARDLINK_LOCKS"] = \ + os.environ["__PORTAGE_TEST_HARDLINK_LOCKS"] + + updates_dir = os.path.join(test_repo_location, "profiles", "updates") + dirs = [cachedir, cachedir_pregen, cross_eroot, cross_prefix, + distdir, fake_bin, portage_tmpdir, updates_dir, + user_config_dir, var_cache_edb] + etc_symlinks = ("dispatch-conf.conf", "etc-update.conf") + # Override things that may be unavailable, or may have portability + # issues when running tests in exotic environments. + # prepstrip - bug #447810 (bash read builtin EINTR problem) + true_symlinks = ["find", "prepstrip", "sed", "scanelf"] + true_binary = find_binary("true") + self.assertEqual(true_binary is None, False, + "true command not found") + try: + for d in dirs: + ensure_dirs(d) + for x in true_symlinks: + os.symlink(true_binary, os.path.join(fake_bin, x)) + for x in etc_symlinks: + os.symlink(os.path.join(self.cnf_etc_path, x), + os.path.join(eprefix, "etc", x)) + with open(os.path.join(var_cache_edb, "counter"), 'wb') as f: + f.write(b"100") + # non-empty system set keeps --depclean quiet + with open(os.path.join(profile_path, "packages"), 'w') as f: + f.write("*dev-libs/token-system-pkg") + for cp, xml_data in metadata_xml_files: + with open(os.path.join(test_repo_location, cp, "metadata.xml"), 'w') as f: + f.write(playground.metadata_xml_template % xml_data) + with open(os.path.join(updates_dir, "1Q-2010"), 'w') as f: + f.write(""" +slotmove =app-doc/pms-3 2 3 +move dev-util/git dev-vcs/git +""") + + 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 args in test_commands: + + if hasattr(args, '__call__'): + args() + continue + + if isinstance(args[0], dict): + local_env = env.copy() + local_env.update(args[0]) + args = args[1:] + else: + local_env = env + + proc = subprocess.Popen(args, + env=local_env, stdout=stdout) + + if debug: + proc.wait() + else: + output = proc.stdout.readlines() + proc.wait() + proc.stdout.close() + if proc.returncode != os.EX_OK: + for line in output: + sys.stderr.write(_unicode_decode(line)) + + self.assertEqual(os.EX_OK, proc.returncode, + "emerge failed with args %s" % (args,)) + finally: + playground.cleanup() |