diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/flags/assertflags.py b/flags/assertflags.py new file mode 100644 index 0000000..9262a58 --- /dev/null +++ b/flags/assertflags.py @@ -0,0 +1,48 @@ +""" +This script asserts that Python config vars with compiler options including +one or more -O flags have the last specified -O flag equal to the script's +first argument. + +We use it to check that the debug build (as well as extension modules) was +built with a desired optimization level (usually -Og or -O0). + +About -O flags: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html +"If you use multiple -O options, with or without level numbers, +the last such option is the one that is effective." +""" + +import sys +import sysconfig + +# We will check all flags if none were requested +KEYS_TO_CHECK = sys.argv[2:] or list(sysconfig.get_config_vars().keys()) +# For backwards compatibility, if no flags were provided, we assume flags without -O are to be skipped +# But when we provide explicit list of flags, we assert they get the options, +# so we can assert things like "CFLAGS has -O3" vs. "CFLAGS has no -O at all" +NO_FLAG_FAILS = bool(sys.argv[2:]) + +# The flags that currently don't have the -Og flag on the debug build +# and we consider it OK, because we don't know any better :) +SKIP = [ + 'CONFIGURE_CFLAGS', + 'CONFIGURE_CFLAGS_NODIST', + 'CONFIG_ARGS', + 'OPT', +] + +print('Expecting that {} is the last -O flag:\n'.format(sys.argv[1])) +ret = 0 + +for key in KEYS_TO_CHECK: + if key in SKIP: + continue + flags = sysconfig.get_config_vars()[key] + if isinstance(flags, str): + oflags = [f for f in flags.split(' ') if f.startswith('-O')] + if (oflags and oflags[-1] != sys.argv[1]) or (not oflags and NO_FLAG_FAILS): + print('Problem in {} -O flags: {}'.format(key, ' '.join(oflags) or '')) + ret = 1 + elif oflags: + print('{} are OK'.format(key)) + +sys.exit(ret) diff --git a/main.fmf b/main.fmf new file mode 100644 index 0000000..69f4373 --- /dev/null +++ b/main.fmf @@ -0,0 +1,4 @@ +contact: + - Petr Šplíchal + - Miro Hrončok +component: [python] diff --git a/marshalparser/test_marshalparser_compatibility.sh b/marshalparser/test_marshalparser_compatibility.sh new file mode 100755 index 0000000..2d32537 --- /dev/null +++ b/marshalparser/test_marshalparser_compatibility.sh @@ -0,0 +1,32 @@ +#!/bin/sh -eux + +# Marshalparser helps us to uniform byte-compiled Python files +# so it has to be able to also parse pyc files from +# the main python packages. +# Moreover, it has its own database of magic numbers so this test +# checks that the parser is compatible with the latest python before +# we ship it. + +# Test the latest version by default because that's the place +# where magic number incompatibility might happen with +# the largest probability +VERSION=${VERSION:-3.9} + +# How many randomly-selected pyc files to check? +SAMPLE=${SAMPLE:-50} + +# This allows us to test marshalparser optionally: on Fedora 33+ only +INSTALL_OR_SKIP=${INSTALL_OR_SKIP:-false} +if [ "$INSTALL_OR_SKIP" == "true" ] && [ ! -f "/usr/bin/marshalparser" ]; then + dnf -y install marshalparser || : + if [ ! -f "/usr/bin/marshalparser" ]; then + echo "marshalparser not installable, skipping this test" + exit 0 + fi +fi + +path="/usr/lib*/python$VERSION/" + +for file in `find $path -name "*.pyc" | shuf | head -n $SAMPLE`; do + marshalparser $file || exit 1 +done diff --git a/required-symbols/check.sh b/required-symbols/check.sh new file mode 100755 index 0000000..797f024 --- /dev/null +++ b/required-symbols/check.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# Compare required external symbols of compiled Python extension modules between +# the PR build (pre-installed by Testing Farm) and the latest stable version +# available in the distribution repositories. +# +# The PR build is already installed before this test runs (via the Testing Farm +# artifact mechanism). We save its .so files, downgrade to the stable repo version, +# collect symbols from both, and compare. +# +# Output: +# + [module] symbol — PR build adds a new required symbol (potential issue possibly requiring update of Requires) +# - [module] symbol — PR build drops a previously required symbol (generally OK) +# +# Exit codes: +# 0 — no changes, or no stable version available to compare against (skipped) +# 1 — symbol differences detected between PR build and stable release +set -eo pipefail + +PYVER=${VERSION} +SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" + +WORK_DIR="" +PKGS_MODIFIED=false + +cleanup() { + if [[ "$PKGS_MODIFIED" == true ]]; then + echo "" + echo "=== Reverting package changes ===" + dnf history undo last -y 2>&1 || echo "WARNING: dnf history undo failed" >&2 + fi + [[ -n "$WORK_DIR" ]] && rm -rf "$WORK_DIR" +} +trap cleanup EXIT + +# Directories containing compiled extension modules +SCAN_DIRS=() +for d in "/usr/lib64/python${PYVER}" "/usr/lib64/python${PYVER}t"; do + [ -d "$d" ] && SCAN_DIRS+=("$d") +done + +if [ ${#SCAN_DIRS[@]} -eq 0 ]; then + echo "ERROR: No Python extension directories found for python${PYVER}" >&2 + exit 1 +fi + +SCAN_ARGS=("${SCAN_DIRS[@]}") +for f in /usr/lib64/libpython${PYVER}*.so.*; do + [[ -f "$f" && ! -L "$f" ]] && SCAN_ARGS+=("$f") +done + +WORK_DIR=$(mktemp -d) + +echo "=== Collecting PR build symbols ===" +python${PYVER} "$SCRIPT_DIR/collect_symbols.py" "${SCAN_ARGS[@]}" > "$WORK_DIR/new.json" + +echo "" +echo "=== Available repos ===" +dnf repolist --all + +# Discover packages dynamically from the installed source RPM so we don't need +# to hardcode subpackage names — works regardless of which subpackages were built. +readarray -t PKGS < <( + rpm -qa --qf '%{SOURCERPM} %{NAME}\n' | + awk -v src="python${PYVER}" '$1 ~ "^" src "-[0-9]" { print $2 }' | + sort -u +) +echo "Packages to install from stable repos: ${PKGS[*]}" + +# Install the stable version from distribution repos, excluding the artifact repo +# that carries the PR build, so we always compare against what is publicly available. +# +# Strategy: +# 1. dnf reinstall — works when the PR did not bump NVR (same version in stable repos) +# 2. dnf distro-sync — fallback when the PR bumped NVR; installs the latest stable NVR +echo "" +echo "=== Installing stable version from repos ===" +if ! dnf reinstall -y --disablerepo=test-artifacts "${PKGS[@]}" 2>&1; then + echo "INFO: reinstall failed (NVR not in stable repos), falling back to distro-sync" >&2 + if ! dnf distro-sync -y --disablerepo=test-artifacts "${PKGS[@]}" 2>&1; then + echo "INFO: distro-sync failed; no stable version available, skipping comparison." >&2 + exit 0 + fi +fi +PKGS_MODIFIED=true + +echo "" +echo "=== Collecting stable version symbols ===" +python${PYVER} "$SCRIPT_DIR/collect_symbols.py" "${SCAN_ARGS[@]}" > "$WORK_DIR/old.json" + +echo "" +echo "=== Comparing PR build against stable ===" +python${PYVER} "$SCRIPT_DIR/compare_symbols.py" "$WORK_DIR/old.json" "$WORK_DIR/new.json" diff --git a/required-symbols/collect_symbols.py b/required-symbols/collect_symbols.py new file mode 100755 index 0000000..983c223 --- /dev/null +++ b/required-symbols/collect_symbols.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +""" +Collect undefined non-boring external symbols from compiled Python extension modules +and libpython shared libraries. + +Usage: collect_symbols.py [...] + +Each path may be a directory (scanned recursively for *.cpython-*.so) or a file +(scanned directly; intended for libpython*.so.* passed explicitly from the caller). +Symbols are unioned across all variants of each module (regular/debug/freethreading), +and the result is printed as JSON to stdout. +""" + +import json +import re +import subprocess +import sys +from pathlib import Path + +BORING = re.compile( + r"@GLIBC_" # glibc versioned symbols (stable by definition) + r"|@GCC_" # GCC built-ins + r"|^_?Py[A-Za-z_]" # Python C API (resolved from libpython at runtime) + r"|^__" # C runtime internals + r"|^_ITM_" # Intel transactional memory +) + + +def module_name(so: Path) -> str: + # _ssl.cpython-314td-x86_64-linux-gnu.so → _ssl + if m := re.match(r"(.+?)\.cpython-", so.name): + return m.group(1) + # libpython3.14.so.1.0 → libpython3.14 + return re.sub(r"\.so.*$", "", so.name) + + +def external_symbols(so: Path) -> list[str]: + result = subprocess.run(["nm", "-D", str(so)], capture_output=True, text=True) + return sorted( + parts[-1] + for line in result.stdout.splitlines() + if len(parts := line.split()) >= 2 + and parts[-2] == "U" + and not BORING.search(parts[-1]) + ) + + +modules: dict[str, set[str]] = {} +for path_arg in sys.argv[1:]: + p = Path(path_arg) + if p.is_file(): + print(f"Scanning: {p.name}", file=sys.stderr) + if syms := external_symbols(p): + modules.setdefault(module_name(p), set()).update(syms) + else: + print(f"Scanning: {path_arg}", file=sys.stderr) + for so in sorted(p.rglob("*.cpython-*.so")): + if so.is_symlink(): + continue + if syms := external_symbols(so): + modules.setdefault(module_name(so), set()).update(syms) + +total_symbols = sum(len(v) for v in modules.values()) +print(f"Found {len(modules)} modules, {total_symbols} tracked symbols", file=sys.stderr) + +json.dump({k: sorted(v) for k, v in sorted(modules.items())}, sys.stdout, indent=2) +print() diff --git a/required-symbols/compare_symbols.py b/required-symbols/compare_symbols.py new file mode 100755 index 0000000..2692993 --- /dev/null +++ b/required-symbols/compare_symbols.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +""" +Compare two symbol JSON files produced by collect_symbols.py. + +Usage: compare_symbols.py + +Output: + + [module] symbol — new build adds a required symbol the old build did not have + - [module] symbol — new build drops a required symbol the old build had + +Exit codes: + 0 — no differences + 1 — differences found +""" + +import json +import sys +from pathlib import Path + +old = json.loads(Path(sys.argv[1]).read_text()) +new = json.loads(Path(sys.argv[2]).read_text()) + +all_mods = sorted(set(old) | set(new)) +changes = [] +for mod in all_mods: + added = sorted(set(new.get(mod, [])) - set(old.get(mod, []))) + removed = sorted(set(old.get(mod, [])) - set(new.get(mod, []))) + if added or removed: + changes.append((mod, added, removed)) + +if not changes: + print("OK: no symbol changes between stable and PR build") + sys.exit(0) + +print("Symbol changes detected (+ new requirement in PR build, - dropped by PR build):") +for mod, added, removed in changes: + for sym in added: + print(f" + [{mod}] {sym}") + for sym in removed: + print(f" - [{mod}] {sym}") +sys.exit(1) diff --git a/selftest/findleaks.fmf b/selftest/findleaks.fmf new file mode 100644 index 0000000..4210b54 --- /dev/null +++ b/selftest/findleaks.fmf @@ -0,0 +1 @@ +test: ./findleaks.sh diff --git a/selftest/findleaks.sh b/selftest/findleaks.sh new file mode 100755 index 0000000..dde2ba8 --- /dev/null +++ b/selftest/findleaks.sh @@ -0,0 +1,21 @@ +#!/bin/sh -eux + +# set python version +VERSION=${VERSION:-3.7} +PYTHON=${PYTHON:-python${VERSION}dm} + +# what to skip +# test_socket swaps and kills the machine https://bugs.python.org/issue34587 +X=${X:-"-x test_socket"} + +# Fedora sets explicit minimum/maximum TLS versions. +# Python's test suite assumes that the minimum/maximum version is set to +# a magic marker. We workaround the test problem by setting: +export OPENSSL_CONF=/non-existing-file +# https://bugzilla.redhat.com/show_bug.cgi?id=1618753 +# https://bugzilla.redhat.com/show_bug.cgi?id=1778357 +# https://bugs.python.org/issue35045 +# https://bugs.python.org/issue38815 + +$PYTHON -m test.pythoninfo +$PYTHON -m test -wW --findleaks $X diff --git a/selftest/main.fmf b/selftest/main.fmf new file mode 100644 index 0000000..a5f418e --- /dev/null +++ b/selftest/main.fmf @@ -0,0 +1,4 @@ +require: +- python3-test +- python3-rpm-macros +- python3-tkinter diff --git a/selftest/parallel.fmf b/selftest/parallel.fmf new file mode 100644 index 0000000..9794a27 --- /dev/null +++ b/selftest/parallel.fmf @@ -0,0 +1,4 @@ +test: ./parallel.sh +duration: 30m +recommend: +- /usr/bin/dtrace diff --git a/selftest/parallel.sh b/selftest/parallel.sh new file mode 100755 index 0000000..588255d --- /dev/null +++ b/selftest/parallel.sh @@ -0,0 +1,41 @@ +#!/bin/sh -eux + +# set default version to %{python3_version} if available +VERSION=${VERSION:-$(rpm --eval '%{?python3_version}')} || true +# ...or 3.8 if that macro is not available or rpm fails for any reason +VERSION=${VERSION:-3.8} + +PYTHON=${PYTHON:-python$VERSION} + +# what to skip +# test_socket swaps and kills the machine https://bugs.python.org/issue34587 +X=${X:-"-x test_socket"} + +TIMEOUT=${TIMEOUT:-1800} + +# parallel jobs, 0 lets Python decide what's best +JOBS=${JOBS:-0} + +# Fedora sets explicit minimum/maximum TLS versions. +# Python's test suite assumes that the minimum/maximum version is set to +# a magic marker. We workaround the test problem by setting: +export OPENSSL_CONF=/non-existing-file +# https://bugzilla.redhat.com/show_bug.cgi?id=1618753 +# https://bugzilla.redhat.com/show_bug.cgi?id=1778357 +# https://bugs.python.org/issue35045 +# https://bugs.python.org/issue38815 + +# setuptools 60+ uses its own copy of distutils by default +# this setting must be overriden with the environment variable for +# Python tests to use the standard library's distutils +# We only do this if stdlib distutils actually exists +$PYTHON -c 'import distutils' 2>/dev/null && export SETUPTOOLS_USE_DISTUTILS=stdlib || true + +$PYTHON -m test.pythoninfo +$PYTHON -m test -wW -j$JOBS --timeout=$TIMEOUT $X && e=0 || e=$? +# https://tmt.readthedocs.io/en/stable/spec/tests.html#framework says only exit code 1 is failure, rest is error +# https://github.com/python/cpython/blob/v3.14.0/Lib/test/libregrtest/results.py#L18 says a bad test exits with 2 +if [ $e -eq 2 ]; then + exit 1 +fi +exit $e diff --git a/smoke/venv.fmf b/smoke/venv.fmf new file mode 100644 index 0000000..805ca8b --- /dev/null +++ b/smoke/venv.fmf @@ -0,0 +1,54 @@ +summary: Basic smoke test for virtualenv +description: | + Check basic functionality of Python virtual environments (venv + or virtualenv based). The test supports different python + versions & implementations including pypy and jython. + + The following steps are performed: + + * Create virtual environment + * Activate virtual environment + * Install pip, pytest and Cython + * Create and test a simple module + * Check we can do extension modules + * Ensure we can test with the tox tool + + Use the following environment variables to adjust what + should be tested (default values listed first): + + * VERSION ... python version (3.7, 3.6, 2.7, 2.6) + * PYTHON ... implementation (python${VERSION}, pypy, pypy3, jython) + * METHOD ... virtual environment creation method (venv, virtualenv) + * TOX ... enable or disable the tox test (true, false) + +path: /smoke +test: ./venv.sh + +tier: 1 +tag: [venv] + +require+: +- gcc +- gcc-c++ + +environment: + VERSION: 3.7 + METHOD: venv + TOX: true + TOX_REQUIRES: "" +duration: 10m +component+: + - python2 + - python26 + - python3 + - python34 + - python35 + - python36 + - pypy + - pypy3 + - jython + - python-pip + - python-wheel + - python-setuptools + - python-virtualenv + - python-tox diff --git a/smoke/venv.sh b/smoke/venv.sh index 9585acc..4088279 100755 --- a/smoke/venv.sh +++ b/smoke/venv.sh @@ -1,24 +1,56 @@ #!/bin/sh -eux -# set python version -VERSION=${VERSION:-3.7} +# set default version to %{python3_version} if available +VERSION=${VERSION:-$(rpm --eval '%{?python3_version}')} || true +# ...or 3.8 if that macro is not available or rpm fails for any reason +VERSION=${VERSION:-3.8} + PYTHON=${PYTHON:-python$VERSION} METHOD=${METHOD:-venv} TOX=${TOX:-true} +TOX_REQUIRES=${TOX_REQUIRES:-} +CYTHON=${CYTHON:-true} +INSTALL_OR_SKIP=${INSTALL_OR_SKIP:-false} -# clean from possible older runs -rm -rf venv .tox __pycache__ .pytest* test_*.py *.pyx *.c *.so || : +# Work from a custom directory +workdir="$(mktemp --directory)" +cd "$workdir" + +if [ "$INSTALL_OR_SKIP" == "true" ]; then + [ ! -f "/usr/bin/$PYTHON" ] && dnf -y install "/usr/bin/$PYTHON" || : + [ ! -f "/usr/bin/${PYTHON}-config" ] && dnf -y install "/usr/bin/${PYTHON}-config" || : + dnf -y install "$PYTHON-devel" || : + if [ ! -f "/usr/bin/$PYTHON" ]; then + echo "/usr/bin/$PYTHON not installable, skipping this test" + exit 0 + fi +fi # check the we can create the venv if [ "$METHOD" == "venv" ]; then $PYTHON -m venv venv elif [ "$METHOD" == "virtualenv" ]; then virtualenv --python=$PYTHON venv +elif [ "$METHOD" == "virtualenv-download" ]; then + virtualenv --download --python=$PYTHON venv +elif [ "$METHOD" == "virtualenv-no-download" ]; then + virtualenv --no-download --python=$PYTHON venv +elif [ "$METHOD" == "virtualenv-seeder-pip" ]; then + virtualenv --seeder pip --python=$PYTHON venv +elif [ "$METHOD" == "none" ]; then + echo 'Skipping plain virtual environment tests' >&2 else echo 'Unsupported $METHOD' $METHOD >&2 exit 1 fi +# create tests +cat > test_foo.py << EOF +def test_foo(): + assert True +EOF + +if [ "$METHOD" != "none" ]; then # and activate it # unset variables on 3.5, it's known @@ -27,29 +59,15 @@ source venv/bin/activate set -u # run python in it -python -c 'import sys; print(sys.version)' | head -n1 | grep $VERSION +python -c 'import sys; print(sys.version)' | head -n1 | grep ${VERSION/t} # install packages with pip -if [ "$VERSION" == "2.6" ]; then - pip install pytest - pip install Cython --install-option="--no-cython-compile" -else - python -m pip install pytest - if [ "$PYTHON" != "jython" ]; then - python -m pip install Cython - fi -fi +python -m pip install pytest # run tests -cat > test_foo.py << EOF -def test_foo(): - assert True -EOF - python -m pytest -v test_foo.py -# check that we can do extension modules -if [ "$PYTHON" != "jython" ]; then +if [ "$CYTHON" == "true" ]; then cat > module.pyx << EOF cdef int add(int a, int b): return a + b @@ -60,6 +78,12 @@ def two(): return add(a, b) EOF +cat > pyproject.toml << EOF +[build-system] +requires = ["setuptools", "cython"] +build-backend = "setuptools.build_meta" +EOF + cat > setup.py << EOF from setuptools import setup from Cython.Build import cythonize @@ -67,7 +91,7 @@ from Cython.Build import cythonize setup(ext_modules = cythonize('module.pyx')) EOF -python setup.py build_ext --inplace +python -m pip install . python -c 'import module; print(module.two())' | grep '^2$' fi @@ -77,18 +101,50 @@ set +u deactivate set -u +fi # METHOD != none + +# ensurepip test (when testing virtualenv, this was not covered) +# with the main Python, this will say "Requirement already satisfied", but that's OK +$PYTHON -m ensurepip --root ensurepiptestroot + # use it with tox [[ "$TOX" != "true" ]] && exit 0 -cat > tox.ini << EOF -[tox] +if [[ $PYTHON == python* ]]; then + export TOXENV=py${VERSION/./} +elif [[ $PYTHON == pypy* ]]; then + export TOXENV=pypy${VERSION/./} +else + export TOXENV=$PYTHON +fi + +echo '[tox]' > tox.ini +if [ -n "$TOX_REQUIRES" ]; then + echo 'requires = '"$TOX_REQUIRES" >> tox.ini +fi +cat >> tox.ini << EOF +skipsdist = True +[testenv] +commands = python -c 'import sys; print(sys.version)' +EOF + +# Tests that the Python version used in tox is really the Python version we want +tox | tee toxlog +grep "^${VERSION/t}\.[0-9]" toxlog + +echo '[tox]' > tox.ini +if [ -n "$TOX_REQUIRES" ]; then + echo 'requires = '"$TOX_REQUIRES" >> tox.ini +fi +cat >> tox.ini << EOF skipsdist = True [testenv] deps = pytest commands = python -m pytest -v test_foo.py EOF -if [[ $PYTHON == python* ]]; then - tox -e py${VERSION/./} -else - tox -e $PYTHON -fi +# A more complex example with pytest +tox + +# Clean up if everything else went well +cd - +rm -rf "$workdir" diff --git a/tests.yml b/tests.yml new file mode 100644 index 0000000..fae8c2a --- /dev/null +++ b/tests.yml @@ -0,0 +1,104 @@ +--- +- hosts: localhost + tags: + - classic + tasks: + - dnf: + name: "*" + state: latest + - ansible.builtin.user: + name: testuser + +- hosts: localhost + roles: + - role: standard-test-basic + tags: + - classic + tests: + - smoke_default: + dir: smoke + run: runuser testuser -c './venv.sh' + - smoke27: + dir: smoke + run: VERSION=2.7 INSTALL_OR_SKIP=true METHOD=virtualenv TOX_REQUIRES="virtualenv<20.22.0" ./venv.sh + - smoke27_seeder_pip: + dir: smoke + run: VERSION=2.7 INSTALL_OR_SKIP=true METHOD=virtualenv-seeder-pip TOX=false ./venv.sh + - smoke36: + dir: smoke + run: runuser testuser -c 'VERSION=3.6 METHOD=venv TOX_REQUIRES="virtualenv<20.22.0" ./venv.sh' + - smoke37: + dir: smoke + run: VERSION=3.7 METHOD=venv INSTALL_OR_SKIP=true ./venv.sh + - smoke38: + dir: smoke + run: VERSION=3.8 METHOD=venv INSTALL_OR_SKIP=true ./venv.sh + - smoke39: + dir: smoke + run: runuser testuser -c 'VERSION=3.9 METHOD=venv ./venv.sh' + - smoke311: + dir: smoke + run: runuser testuser -c 'VERSION=3.11 METHOD=venv CYTHON=false ./venv.sh' + - smoke312: + dir: smoke + run: runuser testuser -c 'VERSION=3.12 METHOD=venv ./venv.sh' + - smoke314t_optional: + dir: smoke + run: VERSION=3.14t METHOD=venv INSTALL_OR_SKIP=true ./venv.sh + - smoke39_seeder_pip: + dir: smoke + run: runuser testuser -c 'VERSION=3.9 METHOD=virtualenv-seeder-pip ./venv.sh' + - smoke34_optional: + dir: smoke + run: VERSION=3.4 METHOD=venv INSTALL_OR_SKIP=true ./venv.sh + - smoke310_optional: + dir: smoke + run: VERSION=3.10 METHOD=venv TOX=false INSTALL_OR_SKIP=true ./venv.sh + - smoke311_virtualenv_download: + dir: smoke + run: VERSION=3.10 METHOD=virtualenv-download TOX=false ./venv.sh + - smokepypy: + dir: smoke + run: runuser testuser -c 'VERSION=2.7 METHOD=none PYTHON=pypy TOX_REQUIRES="virtualenv<20.22.0" ./venv.sh' + - smokepypy310: + dir: smoke + run: runuser testuser -c 'VERSION=3.10 METHOD=venv PYTHON=pypy3.10 ./venv.sh' + - selftest39: + dir: selftest + run: runuser testuser -c 'VERSION=3.9 X="-x test_socket -x test_asyncgen -x test_asyncio -x test_compile -x test_concurrent_futures -x test_itertools -x test_multiprocessing_fork -x test_multiprocessing_forkserver -x test_shutil -x test_time -x test_multiprocessing_spawn -x test_threading -x test_wsgiref" ./parallel.sh' + - debugtest310: + dir: selftest + run: runuser testuser -c 'VERSION=3.10 PYTHON="python3.10d" X="test_ssl" ./parallel.sh' + - debugflags: + dir: flags + run: runuser testuser -c 'python3-debug ./assertflags.py -O0' + - debugflags_some: + dir: flags + run: runuser testuser -c 'python3-debug ./assertflags.py -O0 PY_STDMODULE_CFLAGS' + - optflags_o3: + dir: flags + run: runuser testuser -c 'python3 ./assertflags.py -O3 CFLAGS' + - marshalparser_compatibility: + dir: marshalparser + run: runuser testuser -c 'SAMPLE=10 test_marshalparser_compatibility.sh' + required_packages: + - util-linux # for runuser + - dnf + - gcc + - gcc-c++ + - virtualenv + - python3.6 + - python3.9 + - python3.11 + - python3.12 + - pypy + - pypy3.10-devel + - python3-devel + - pypy-devel + - python3-tox + - python3-tkinter + - python3-test + - python3-debug + - python3.10-debug + - python3-rpm-macros + - marshalparser