diff --git a/scripts/smoke-tests/baremetal-arm-smoke-test b/scripts/smoke-tests/baremetal-arm-smoke-test index b18c9ba99..f34c346c5 100755 --- a/scripts/smoke-tests/baremetal-arm-smoke-test +++ b/scripts/smoke-tests/baremetal-arm-smoke-test @@ -41,17 +41,26 @@ # not run, and that is reported as WARN. Failures exit non-zero. import argparse +import functools import os import re import shlex import shutil -import subprocess import sys import tempfile +sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) +sys.dont_write_bytecode = True +import smoketest # noqa: E402 +from smoketest import (last_line, overlay_prefixes, report, # noqa: E402 + summary) + # How --prefix survives the re-exec into the mount namespace. PREFIX_VARIABLE = "BAREMETAL_ARM_SMOKE_TEST_PREFIX" +# What says why a link or a QEMU run failed, for last_line. +ERROR = r"error|cannot find|undefined symbol|unrecognised|fault" + RUNTIMES = "/usr/lib/clang-runtimes" HELLO = r""" @@ -138,80 +147,8 @@ PROGRAMS = [ MPS2_AN505), ] -status = {"fail": 0, "warn": 0} - - -def report(state, message): - if state != "ok": - status[state] += 1 - print("%-5s %s" % (state.upper(), message)) - - -def run(command, **kwargs): - """Run command, capturing its output, never raising for its exit status.""" - try: - return subprocess.run(command, capture_output=True, text=True, - timeout=120, **kwargs) - except (OSError, subprocess.TimeoutExpired) as error: - return subprocess.CompletedProcess(command, 127, "", str(error)) - - -def last_line(process): - """The line of the output that says what went wrong.""" - lines = (process.stderr or process.stdout).strip().splitlines() - for line in lines: - # The linker says why before clang says that it failed. - if re.search(r"error|cannot find|undefined symbol|unrecognised|fault", - line) and \ - "linker command failed" not in line: - return line.strip() - return lines[-1] if lines else "exit status %d" % process.returncode - - -# ---------------------------------------------------------------- --prefix - -def unpack(items, workdir): - """Return the directories to overlay, unpacking package archives.""" - lowers = [] - for number, item in enumerate(items): - if os.path.isdir(item): - path = os.path.abspath(item.rstrip("/")) - elif os.path.isfile(item): - path = os.path.join(workdir, str(number)) - os.mkdir(path) - print(" unpacking %s" % item, flush=True) - process = subprocess.run(["tar", "-C", path, "-xf", item], - capture_output=True, text=True) - if process.returncode: - sys.exit("cannot unpack %s: %s" % (item, process.stderr)) - else: - sys.exit("no such directory or package: %s" % item) - if ":" in path or "," in path: - sys.exit("cannot overlay %s: overlayfs separates layers by ':'" - " and options by ','" % path) - if not os.path.isdir(path + "/usr"): - sys.exit("%s has no usr/" % item) - lowers.append(path) - return lowers - - -def run_in_overlay(lowers): - """Run this script again with lowers mounted over /usr.""" - layers = ":".join([lower + "/usr" for lower in lowers] + ["/usr"]) - setup = "set -e\nmount -t overlay overlay -o lowerdir=%s /usr\nexec \"$@\"" \ - % shlex.quote(layers) - environment = dict(os.environ) - environment[PREFIX_VARIABLE] = ":".join(lowers) - command = ["unshare", "--user", "--map-root-user", "--mount", - "--propagation", "slave", - "sh", "-c", setup, "sh", sys.executable, - os.path.abspath(__file__)] + sys.argv[1:] - try: - return subprocess.run(command, env=environment).returncode - except OSError as error: - sys.exit("cannot re-run in a mount namespace (%s); install the" - " packages and run without --prefix" % error) - +# Shorter than smoketest's default: nothing here compiles for long. +run = functools.partial(smoketest.run, timeout=120) # ----------------------------------------------------------------- checks @@ -278,7 +215,8 @@ def check_program(name, flags, machine, memory, qemu, machines, directory): os.path.join(RUNTIMES, variant, "lib/crt0-semihost.o"), "-o", output, "hello.c", "-lsemihost"], cwd=directory) if process.returncode: - report("fail", "%s: link failed: %s" % (name, last_line(process))) + report("fail", "%s: link failed: %s" + % (name, last_line(process, ERROR))) return if machine not in machines: report("warn", "%s: linked, but not run: %s has no %s board" @@ -293,7 +231,7 @@ def check_program(name, flags, machine, memory, qemu, machines, directory): if process.returncode or EXPECT not in printed: report("fail", "%s on %s: exit status %d: %s" % (name, machine, process.returncode, - last_line(process) if process.returncode else + last_line(process, ERROR) if process.returncode else printed.strip())) else: report("ok", "%s runs on %s with %s" % (name, machine, variant)) @@ -307,7 +245,8 @@ def check_default_link(directory): ["-o", "default.elf", "hello.c", "-lsemihost"] process = run(command, cwd=directory) if process.returncode: - report("fail", "%s: link failed: %s" % (name, last_line(process))) + report("fail", "%s: link failed: %s" + % (name, last_line(process, ERROR))) return variant = os.path.join(RUNTIMES, VARIANTS[3], "lib") # clang spells the paths /usr/bin/../lib/clang-runtimes/... @@ -337,15 +276,9 @@ def main(): " (default: from PATH)") arguments = parser.parse_args() - if arguments.prefix and PREFIX_VARIABLE not in os.environ: - workdir = tempfile.mkdtemp(prefix="baremetal-arm-smoke-") - try: - return run_in_overlay(unpack(arguments.prefix, workdir)) - finally: - shutil.rmtree(workdir, ignore_errors=True) - if PREFIX_VARIABLE in os.environ: - print(" testing, over /usr: %s" - % os.environ[PREFIX_VARIABLE].replace(":", " ")) + returncode = overlay_prefixes(arguments.prefix, PREFIX_VARIABLE) + if returncode is not None: + return returncode machines = qemu_machines(arguments.qemu) if arguments.qemu: @@ -367,10 +300,7 @@ def main(): finally: shutil.rmtree(directory, ignore_errors=True) - print("%s (%d failed, %d warned)" - % ("FAIL" if status["fail"] else "PASS", status["fail"], - status["warn"])) - return 1 if status["fail"] else 0 + return summary() if __name__ == "__main__": diff --git a/scripts/smoke-tests/freetype-smoke-test b/scripts/smoke-tests/freetype-smoke-test index b7158a0cf..f55e1fa0a 100755 --- a/scripts/smoke-tests/freetype-smoke-test +++ b/scripts/smoke-tests/freetype-smoke-test @@ -47,6 +47,10 @@ import subprocess import sys import tempfile +sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) +sys.dont_write_bytecode = True +from smoketest import report, summary # noqa: E402 + # configure links these when it finds them, and 2.10.4 linked all of them. NEEDED = ["libbz2.so.1.0", "libpng16.so.16", "libz.so.1", "libharfbuzz.so.0", "libbrotlidec.so."] @@ -425,13 +429,6 @@ int main(int argc, char **argv) """ root = "" -status = {"fail": 0, "warn": 0} - - -def report(state, message): - if state in status: - status[state] += 1 - print("%-5s %s" % (state.upper(), message)) def environment(tree=None): @@ -1041,16 +1038,14 @@ def main(): comparisons = check_rendering(binary, directory, fonts, arguments) check_fcscan(fonts) - print("%s (%d failed, %d warned), files in %s" - % ("FAIL" if status["fail"] else "PASS", - status["fail"], status["warn"], directory)) + returncode = summary(directory) if comparisons and os.environ.get("DISPLAY") and not arguments.no_view: try: view(comparisons) # ImportError without tkinter, tkinter.TclError without a display. except Exception as error: print("cannot show the rendering: %s" % error) - return 1 if status["fail"] else 0 + return returncode if __name__ == "__main__": diff --git a/scripts/smoke-tests/llvm-smoke-test b/scripts/smoke-tests/llvm-smoke-test index 23141297c..d88958bc7 100755 --- a/scripts/smoke-tests/llvm-smoke-test +++ b/scripts/smoke-tests/llvm-smoke-test @@ -40,15 +40,21 @@ import argparse import os import re -import shlex import shutil -import subprocess import sys import tempfile +sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) +sys.dont_write_bytecode = True +from smoketest import (last_line, overlay_prefixes, report, run, # noqa: E402 + strict_overcommit, summary) + # How --prefix survives the re-exec into the mount namespace. PREFIX_VARIABLE = "LLVM_SMOKE_TEST_PREFIX" +# What says why a compile failed, for last_line. +ERROR = r"error|cannot find|undefined reference" + SOURCES = { "hello.c": r""" #include @@ -109,80 +115,6 @@ kernel void k(global float *out) { """, } -status = {"fail": 0, "warn": 0} - - -def report(state, message): - if state != "ok": - status[state] += 1 - print("%-5s %s" % (state.upper(), message)) - - -def run(command, **kwargs): - """Run command, capturing its output, never raising for its exit status.""" - try: - return subprocess.run(command, capture_output=True, text=True, - timeout=600, **kwargs) - except (OSError, subprocess.TimeoutExpired) as error: - return subprocess.CompletedProcess(command, 127, "", str(error)) - - -def last_line(process): - """The line of the output that says what went wrong.""" - lines = (process.stderr or process.stdout).strip().splitlines() - for line in lines: - # The linker says why before clang says that it failed. - if re.search(r"error|cannot find|undefined reference", line) and \ - "linker command failed" not in line: - return line.strip() - return lines[-1] if lines else "exit status %d" % process.returncode - - -# ---------------------------------------------------------------- --prefix - -def unpack(items, workdir): - """Return the directories to overlay, unpacking package archives.""" - lowers = [] - for number, item in enumerate(items): - if os.path.isdir(item): - path = os.path.abspath(item.rstrip("/")) - elif os.path.isfile(item): - path = os.path.join(workdir, str(number)) - os.mkdir(path) - print(" unpacking %s" % item, flush=True) - process = subprocess.run(["tar", "-C", path, "-xf", item], - capture_output=True, text=True) - if process.returncode: - sys.exit("cannot unpack %s: %s" % (item, process.stderr)) - else: - sys.exit("no such directory or package: %s" % item) - if ":" in path or "," in path: - sys.exit("cannot overlay %s: overlayfs separates layers by ':'" - " and options by ','" % path) - if not os.path.isdir(path + "/usr"): - sys.exit("%s has no usr/" % item) - lowers.append(path) - return lowers - - -def run_in_overlay(lowers): - """Run this script again with lowers mounted over /usr.""" - layers = ":".join([lower + "/usr" for lower in lowers] + ["/usr"]) - setup = "set -e\nmount -t overlay overlay -o lowerdir=%s /usr\nexec \"$@\"" \ - % shlex.quote(layers) - environment = dict(os.environ) - environment[PREFIX_VARIABLE] = ":".join(lowers) - command = ["unshare", "--user", "--map-root-user", "--mount", - "--propagation", "slave", - "sh", "-c", setup, "sh", sys.executable, - os.path.abspath(__file__)] + sys.argv[1:] - try: - return subprocess.run(command, env=environment).returncode - except OSError as error: - sys.exit("cannot re-run in a mount namespace (%s); install the" - " packages and run without --prefix" % error) - - # ----------------------------------------------------------------- checks def major(version): @@ -240,7 +172,7 @@ def build(name, command, directory): process = run(command, cwd=directory) if process.returncode: report("fail", "%s: %s failed: %s" - % (name, " ".join(command[:1]), last_line(process))) + % (name, " ".join(command[:1]), last_line(process, ERROR))) return False return True @@ -272,14 +204,6 @@ def check_lld(version, directory): report("ok", "%s, .comment names LLD %s" % (name, version)) -def strict_overcommit(): - try: - with open("/proc/sys/vm/overcommit_memory") as sysctl: - return sysctl.read().strip() == "2" - except OSError: - return False - - def check_sanitizer(name, flags, source, expect, directory): output = name.lower() if not build(name, ["clang", "-g"] + flags + ["-o", output, source], @@ -294,7 +218,7 @@ def check_sanitizer(name, flags, source, expect, directory): report("ok", "%s reports %s" % (name, expect)) else: report("fail", "%s: expected %r, got exit status %d: %s" - % (name, expect, process.returncode, last_line(process))) + % (name, expect, process.returncode, last_line(process, ERROR))) def check_profile(directory): @@ -313,7 +237,7 @@ def check_profile(directory): env=dict(os.environ, **environment)) if process.returncode: report("fail", "%s: %s failed: %s" - % (name, command[0], last_line(process))) + % (name, command[0], last_line(process, ERROR))) return if "TOTAL" in process.stdout: report("ok", "%s: compiler-rt profile, llvm-profdata, llvm-cov" % name) @@ -358,15 +282,9 @@ def main(): " installed; may be repeated") arguments = parser.parse_args() - if arguments.prefix and PREFIX_VARIABLE not in os.environ: - workdir = tempfile.mkdtemp(prefix="llvm-smoke-") - try: - return run_in_overlay(unpack(arguments.prefix, workdir)) - finally: - shutil.rmtree(workdir, ignore_errors=True) - if PREFIX_VARIABLE in os.environ: - print(" testing, over /usr: %s" - % os.environ[PREFIX_VARIABLE].replace(":", " ")) + returncode = overlay_prefixes(arguments.prefix, PREFIX_VARIABLE) + if returncode is not None: + return returncode version = check_versions() if not version: @@ -398,10 +316,7 @@ def main(): finally: shutil.rmtree(directory, ignore_errors=True) - print("%s (%d failed, %d warned)" - % ("FAIL" if status["fail"] else "PASS", status["fail"], - status["warn"])) - return 1 if status["fail"] else 0 + return summary() if __name__ == "__main__": diff --git a/scripts/smoke-tests/mesa-smoke-test b/scripts/smoke-tests/mesa-smoke-test index dbe645413..47cbf06a6 100755 --- a/scripts/smoke-tests/mesa-smoke-test +++ b/scripts/smoke-tests/mesa-smoke-test @@ -43,12 +43,14 @@ import glob import json import math import os -import shlex import shutil import subprocess import sys -import tempfile -import time + +sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) +sys.dont_write_bytecode = True +from smoketest import (overlay_prefixes, report, start_xvfb, # noqa: E402 + summary) # How --prefix survives the re-exec into the mount namespace. PREFIX_VARIABLE = "MESA_SMOKE_TEST_PREFIX" @@ -63,64 +65,6 @@ VULKAN_ICD_DIRS = ("/usr/share/vulkan/icd.d", "/etc/vulkan/icd.d") LIBCLC_MISSING = "Libclc failed to load" LIBCLC_UPSTREAM = "Patched Mesa libclc not detected" -status = {"fail": 0, "warn": 0} - - -def report(state, message): - if state != "ok": - status[state] += 1 - print("%-5s %s" % (state.upper(), message)) - - -# ---------------------------------------------------------------- --prefix - -def unpack(items, workdir): - """Return the directories to overlay, unpacking package archives.""" - lowers = [] - for number, item in enumerate(items): - if os.path.isdir(item): - path = os.path.abspath(item.rstrip("/")) - elif os.path.isfile(item): - path = os.path.join(workdir, str(number)) - os.mkdir(path) - print(" unpacking %s" % item, flush=True) - process = subprocess.run(["tar", "-C", path, "-xf", item], - capture_output=True, text=True) - if process.returncode: - sys.exit("cannot unpack %s: %s" % (item, process.stderr)) - else: - sys.exit("no such directory or package: %s" % item) - if ":" in path or "," in path: - sys.exit("cannot overlay %s: overlayfs separates layers by ':'" - " and options by ','" % path) - lowers.append(path) - return lowers - - -def run_in_overlay(lowers): - """Run this script again with lowers mounted over /usr and /etc.""" - setup = ["set -e"] - for directory in ("usr", "etc"): - layers = [lower + "/" + directory for lower in lowers - if os.path.isdir(lower + "/" + directory)] - if layers: - setup.append("mount -t overlay overlay -o lowerdir=%s /%s" - % (shlex.quote(":".join(layers + ["/" + directory])), - directory)) - setup.append('exec "$@"') - environment = dict(os.environ) - environment[PREFIX_VARIABLE] = ":".join(lowers) - command = ["unshare", "--user", "--map-root-user", "--mount", - "--propagation", "slave", - "sh", "-c", "\n".join(setup), "sh", sys.executable, - os.path.abspath(__file__)] + sys.argv[1:] - try: - return subprocess.run(command, env=environment).returncode - except OSError as error: - sys.exit("cannot re-run in a mount namespace (%s); install the" - " packages and run without --prefix" % error) - - # --------------------------------------------------------------- children # # Every check that loads a driver runs in a child process of this script, so @@ -653,34 +597,12 @@ def check_gl(): % (label, name)) -def start_xvfb(): - """Return (display, process) for a private X server, or (None, None).""" - if not shutil.which("Xvfb") or not shutil.which("xdpyinfo"): - return None, None - for number in range(77, 97): - if os.path.exists("/tmp/.X%d-lock" % number): - continue - display = ":%d" % number - process = subprocess.Popen( - ["Xvfb", display, "-nolisten", "tcp", "-screen", "0", "640x480x24"], - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - for _ in range(50): - time.sleep(.2) - if process.poll() is not None: - break - if subprocess.run(["xdpyinfo", "-display", display], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL).returncode == 0: - return display, process - process.kill() - return None, None - - def check_glx(): if not shutil.which("glxinfo"): report("warn", "GLX: no glxinfo") return - display, xvfb = start_xvfb() + display, xvfb = start_xvfb(["-nolisten", "tcp", "-screen", "0", + "640x480x24"]) if not display: report("warn", "GLX: cannot start Xvfb") return @@ -792,15 +714,10 @@ def main(): print(json.dumps(CHILDREN[arguments.child]())) return 0 - if arguments.prefix and PREFIX_VARIABLE not in os.environ: - workdir = tempfile.mkdtemp(prefix="mesa-smoke-") - try: - return run_in_overlay(unpack(arguments.prefix, workdir)) - finally: - shutil.rmtree(workdir, ignore_errors=True) - if PREFIX_VARIABLE in os.environ: - print(" testing, over /usr and /etc: %s" - % os.environ[PREFIX_VARIABLE].replace(":", " ")) + returncode = overlay_prefixes(arguments.prefix, PREFIX_VARIABLE, + directories=("usr", "etc"), required=None) + if returncode is not None: + return returncode check_libraries() check_libclc() @@ -809,10 +726,7 @@ def main(): check_vulkan() check_opencl(arguments.rusticl_enable) - print("%s (%d failed, %d warned)" - % ("FAIL" if status["fail"] else "PASS", status["fail"], - status["warn"])) - return 1 if status["fail"] else 0 + return summary() if __name__ == "__main__": diff --git a/scripts/smoke-tests/smoketest.py b/scripts/smoke-tests/smoketest.py new file mode 100644 index 000000000..7426ffb0a --- /dev/null +++ b/scripts/smoke-tests/smoketest.py @@ -0,0 +1,171 @@ +# What the smoke tests in this directory share: reporting, running commands, +# --prefix in a mount namespace, and a private Xvfb. +# +# The scripts have no .py extension, so they cannot be imported from each +# other; each puts this directory on sys.path and imports this module instead. + +import os +import re +import shlex +import shutil +import subprocess +import sys +import tempfile +import time + +# The script that imported this module, which the namespace runs again. +SCRIPT = os.path.abspath(sys.modules["__main__"].__file__) + +status = {"fail": 0, "warn": 0} + + +def report(state, message): + """Print a result; fail and warn are counted, ok and info are not.""" + if state in status: + status[state] += 1 + print("%-5s %s" % (state.upper(), message)) + + +def summary(directory=None): + """Print the verdict, naming directory if the files are kept; return the + exit status.""" + print("%s (%d failed, %d warned)%s" + % ("FAIL" if status["fail"] else "PASS", status["fail"], + status["warn"], ", files in %s" % directory if directory else "")) + return 1 if status["fail"] else 0 + + +def run(command, timeout=600, **kwargs): + """Run command, capturing its output, never raising for its exit status.""" + try: + return subprocess.run(command, capture_output=True, text=True, + timeout=timeout, **kwargs) + except (OSError, subprocess.TimeoutExpired) as error: + return subprocess.CompletedProcess(command, 127, "", str(error)) + + +def last_line(process, pattern): + """The line of the output that says what went wrong: the first one + matching pattern, or else the last one.""" + lines = (process.stderr or process.stdout).strip().splitlines() + for line in lines: + # The linker says why before clang says that it failed. + if re.search(pattern, line) and "linker command failed" not in line: + return line.strip() + return lines[-1] if lines else "exit status %d" % process.returncode + + +def strict_overcommit(): + """Whether vm.overcommit_memory=2 accounts every reserved byte.""" + try: + with open("/proc/sys/vm/overcommit_memory") as sysctl: + return sysctl.read().strip() == "2" + except OSError: + return False + + +def start_xvfb(arguments): + """Return (display, process) for a private X server started with + arguments, or (None, None).""" + if not shutil.which("Xvfb") or not shutil.which("xdpyinfo"): + return None, None + for number in range(77, 97): + if os.path.exists("/tmp/.X%d-lock" % number): + continue + display = ":%d" % number + process = subprocess.Popen(["Xvfb", display] + arguments, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + for _ in range(50): + time.sleep(.2) + if process.poll() is not None: + break + if subprocess.run(["xdpyinfo", "-display", display], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL).returncode == 0: + return display, process + process.kill() + return None, None + + +# ---------------------------------------------------------------- --prefix + +def namespace_command(setup): + """The command running the script again, with its arguments, after the + shell commands setup, in a user and mount namespace of its own. + + Mount propagation is slave, so that automounts from the host, such as /pkg, + reach the namespace, but nothing mounted inside leaks out.""" + return ["unshare", "--user", "--map-root-user", "--mount", + "--propagation", "slave", + "sh", "-c", setup, "sh", sys.executable, SCRIPT] + sys.argv[1:] + + +def unpack(items, workdir, required="usr"): + """Return the directories to overlay, unpacking package archives into + workdir. Each has to have the directory required, unless that is None.""" + lowers = [] + for number, item in enumerate(items): + if os.path.isdir(item): + path = os.path.abspath(item.rstrip("/")) + elif os.path.isfile(item): + path = os.path.join(workdir, str(number)) + os.mkdir(path) + print(" unpacking %s" % item, flush=True) + process = subprocess.run(["tar", "-C", path, "-xf", item], + capture_output=True, text=True) + if process.returncode: + sys.exit("cannot unpack %s: %s" % (item, process.stderr)) + else: + sys.exit("no such directory or package: %s" % item) + if ":" in path or "," in path: + sys.exit("cannot overlay %s: overlayfs separates layers by ':'" + " and options by ','" % path) + if required and not os.path.isdir(path + "/" + required): + sys.exit("%s has no %s/" % (item, required)) + lowers.append(path) + return lowers + + +def run_in_overlay(lowers, variable, directories): + """Run the script again with lowers mounted over directories, and their + paths in the environment variable.""" + setup = ["set -e"] + for directory in directories: + layers = [lower + "/" + directory for lower in lowers + if os.path.isdir(lower + "/" + directory)] + if layers: + setup.append("mount -t overlay overlay -o lowerdir=%s /%s" + % (shlex.quote(":".join(layers + ["/" + directory])), + directory)) + setup.append('exec "$@"') + environment = dict(os.environ) + environment[variable] = ":".join(lowers) + try: + return subprocess.run(namespace_command("\n".join(setup)), + env=environment).returncode + except OSError as error: + sys.exit("cannot re-run in a mount namespace (%s); install the" + " packages and run without --prefix" % error) + + +def overlay_prefixes(items, variable, directories=("usr",), required="usr"): + """Implement --prefix: run the script again with the image directories or + .bee.tar.bz2 packages items, unpacked below $TMPDIR, mounted read-only over + directories, and return its exit status. variable tells the run inside + that it is; there, and without items, return None to carry on testing.""" + if variable in os.environ: + print(" testing, over %s: %s" + % (" and ".join("/" + directory for directory in directories), + os.environ[variable].replace(":", " "))) + return None + if not items: + return None + # llvm-smoke-test unpacks into llvm-smoke-XXXXXXXX. + workdir = tempfile.mkdtemp( + prefix=re.sub(r"(-test)?$", "-", os.path.basename(SCRIPT), count=1)) + try: + return run_in_overlay(unpack(items, workdir, required), variable, + directories) + finally: + shutil.rmtree(workdir, ignore_errors=True) diff --git a/scripts/smoke-tests/webkitgtk-smoke-test b/scripts/smoke-tests/webkitgtk-smoke-test index c885650b1..e999ed2bd 100755 --- a/scripts/smoke-tests/webkitgtk-smoke-test +++ b/scripts/smoke-tests/webkitgtk-smoke-test @@ -35,6 +35,12 @@ import sys import tempfile import time +sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) +sys.dont_write_bytecode = True +import smoketest # noqa: E402 +from smoketest import (namespace_command, report, # noqa: E402 + strict_overcommit, summary) + # The two flavours built from the same sources: webkitgtk.be0 builds the GTK 4 # one, which upstream calls API version 6.0, and webkit2gtk.be0 the GTK 3 one, # API version 4.1. Everything is named after the API version, so both can be @@ -139,15 +145,6 @@ OPTIONAL = ["webgl"] COMMIT_PER_PROCESS_KB = 5.5 * 1024 * 1024 SESSION_PROCESSES = 4 -status = {"fail": 0, "warn": 0} - - -def report(state, message): - if state != "ok": - status[state] += 1 - print("%-5s %s" % (state.upper(), message)) - - def libexecdir(): """Where the package installs MiniBrowser, jsc and the child processes.""" for candidate in ("/usr/libexec/", "/usr/lib/"): @@ -238,12 +235,8 @@ def check_overcommit(): Nothing here is a packaging bug, hence WARN: the machine is too small for WebKit's reservations, whatever its `MemTotal' says. """ - try: - with open("/proc/sys/vm/overcommit_memory") as sysctl: - if int(sysctl.read().strip()) != 2: - return True # MAP_NORESERVE is honoured; nothing to check. - except (OSError, ValueError): - return True + if not strict_overcommit(): + return True # MAP_NORESERVE is honoured; nothing to check. meminfo = {} try: @@ -274,29 +267,18 @@ def check_overcommit(): def start_xvfb(): - """Return (display, process) for a private X server, or (None, None).""" + """Return (display, process) for $DISPLAY, without a process, or else for + a private X server, which $DISPLAY then names.""" if os.environ.get("DISPLAY"): return os.environ["DISPLAY"], None - if not shutil.which("Xvfb"): - sys.exit("no $DISPLAY and no Xvfb -- run this on a machine with X") - for number in range(77, 97): - if os.path.exists("/tmp/.X%d-lock" % number): - continue - display = ":%d" % number - process = subprocess.Popen( - ["Xvfb", display, "-screen", "0", "1280x1024x24"], - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - for _ in range(50): - time.sleep(.2) - if process.poll() is not None: - break - if subprocess.run(["xdpyinfo", "-display", display], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL).returncode == 0: - os.environ["DISPLAY"] = display - return display, process - process.kill() - sys.exit("could not start Xvfb") + if not shutil.which("Xvfb") or not shutil.which("xdpyinfo"): + sys.exit("no $DISPLAY and no Xvfb or xdpyinfo -- run this on a machine" + " with X") + display, process = smoketest.start_xvfb(["-screen", "0", "1280x1024x24"]) + if not display: + sys.exit("could not start Xvfb") + os.environ["DISPLAY"] = display + return display, process def load(uri, snapshot=None, script="JSON.stringify(window.smoke)"): @@ -477,10 +459,7 @@ mount -t overlay overlay \ ln -s %s /usr/libexec/%s exec "$@" """ % (shlex.quote(libexec), api["subdir"]) - command = ["unshare", "--user", "--map-root-user", "--mount", - "--propagation", "slave", - "sh", "-c", setup, "sh", sys.executable, - os.path.abspath(__file__)] + sys.argv[1:] + command = namespace_command(setup) try: os.execvpe(command[0], command, environment) except OSError as error: @@ -550,10 +529,7 @@ def main(): xvfb.terminate() xvfb.wait() - print("%s (%d failed, %d warned), files in %s" - % ("FAIL" if status["fail"] else "PASS", - status["fail"], status["warn"], directory)) - return 1 if status["fail"] else 0 + return summary(directory) if __name__ == "__main__":