Test suite: Fix small numbers of Python tests being run in incorrect environment
Some checks are pending
CI / Linux (python=3.14 cc=clang sanitize=1) (push) Waiting to run
CI / Linux (python=3.12 cc=gcc sanitize=0) (push) Waiting to run
CI / Linux (python=3.13 cc=gcc sanitize=1) (push) Waiting to run
CI / Linux package (push) Waiting to run
CI / Bundle test (macos-latest) (push) Waiting to run
CI / Bundle test (ubuntu-latest) (push) Waiting to run
CI / macOS Brew (push) Waiting to run
CI / Test ./dev.sh and benchmark (push) Waiting to run
CodeQL / CodeQL-Build (actions, ubuntu-latest) (push) Waiting to run
CodeQL / CodeQL-Build (c, macos-latest) (push) Waiting to run
CodeQL / CodeQL-Build (c, ubuntu-latest) (push) Waiting to run
CodeQL / CodeQL-Build (go, ubuntu-latest) (push) Waiting to run
CodeQL / CodeQL-Build (python, ubuntu-latest) (push) Waiting to run
Depscan / Scan dependencies for vulnerabilities (push) Waiting to run

Also fix env reporting. Fixes #10478
This commit is contained in:
Kovid Goyal 2026-09-15 16:50:21 +05:30
parent e1bdb2c66f
commit bab67650e2
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
3 changed files with 49 additions and 17 deletions

View file

@ -4,3 +4,8 @@
import os
is_ci = os.environ.get('CI') == 'true'
def in_isolated_test_env() -> bool:
"""Whether HOME and the XDG dirs point at the throwaway directory created by env_for_python_tests()."""
return 'KT_ORIGINAL_HOME' in os.environ

View file

@ -41,9 +41,19 @@ from kitty.utils import (
shlex_split_with_positions,
)
from . import in_isolated_test_env
from .base import BaseTest, filled_cursor, filled_history_buf, filled_line_buf
def rmtree_in_test_home(path):
# Guard against destroying the invoking user's data if the test suite is ever
# run without the isolated $HOME set up by env_for_python_tests()
if not in_isolated_test_env():
raise AssertionError(f'Refusing to delete {path} as the test suite is not running with an isolated HOME')
if os.path.exists(path):
shutil.rmtree(path)
def create_lbuf(*lines):
maxw = max(map(len, lines))
ans = LineBuf(len(lines), maxw)
@ -692,8 +702,7 @@ class TestDataTypes(BaseTest):
saved = {x: os.environ.get(x) for x in 'KITTY_CONFIG_DIRECTORY XDG_CONFIG_DIRS XDG_CONFIG_HOME'.split()}
try:
dot_config = os.path.expanduser('~/.config')
if os.path.exists(dot_config):
shutil.rmtree(dot_config)
rmtree_in_test_home(dot_config)
with tempfile.TemporaryDirectory() as tdir:
with open(tdir + '/macos-launch-services-cmdline', 'w') as f:
print('kitty --title from-file', file=f)
@ -736,8 +745,7 @@ class TestDataTypes(BaseTest):
os.environ[k] = v
self.assertEqual(x[-1], get_config_dir(), str(x))
finally:
if os.path.exists(dot_config):
shutil.rmtree(dot_config)
rmtree_in_test_home(dot_config)
for k in saved:
os.environ.pop(k, None)
if saved[k] is not None:

View file

@ -225,21 +225,29 @@ def env_vars(**kw: str) -> Iterator[None]:
os.environ[k] = v
@contextmanager
def env_for_python_tests(report_env: bool = False) -> Iterator[None]:
gohome = os.path.expanduser('~/go')
slangc = os.environ.get('SLANGC') or shutil.which('slangc') or 'slangc'
def path_for_python_tests() -> str:
"""The PATH used by tests: the built launcher dir followed by all entries not under the real home dir."""
current_home = os.path.expanduser('~') + os.sep
paths = os.environ.get('PATH', '/usr/local/sbin:/usr/local/bin:/usr/bin').split(os.pathsep)
path = os.pathsep.join(x for x in paths if not x.startswith(current_home))
launcher_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kitty', 'launcher')
path = f'{launcher_dir}{os.pathsep}{path}'
if report_env:
print('Running under CI:', is_ci)
print('Using PATH in test environment:', path)
from kitty.fast_data_types import has_avx2, has_avx512, has_sse4_2
return f'{launcher_dir}{os.pathsep}{path}'
print(f'Intrinsics: {has_avx512=} {has_avx2=} {has_sse4_2=}')
def report_test_env() -> None:
"""Print details of the environment tests will run in. Called once in the master process as workers have their stdout captured."""
print('Running under CI:', is_ci)
print('Using PATH in test environment:', path_for_python_tests())
from kitty.fast_data_types import has_avx2, has_avx512, has_sse4_2
print(f'Intrinsics: {has_avx512=} {has_avx2=} {has_sse4_2=}')
@contextmanager
def env_for_python_tests() -> Iterator[None]:
gohome = os.path.expanduser('~/go')
slangc = os.environ.get('SLANGC') or shutil.which('slangc') or 'slangc'
path = path_for_python_tests()
with (
TemporaryDirectory() as tdir,
env_vars(
@ -759,8 +767,11 @@ def run_tests(report_env: bool = False) -> None:
if args.name and not tests_list and not has_go:
raise SystemExit('No test named %s found' % ' '.join(args.name))
# Start Python workers before modifying the main-process env; each worker
# calls env_for_python_tests independently for full HOME/XDG isolation.
if report_env:
report_test_env()
# Start Python workers; each worker calls env_for_python_tests independently
# for full HOME/XDG isolation, as does the serial path below.
# On macOS fork()+threading is unsafe, so use subprocess workers there.
use_parallel = len(tests_list) > PARALLEL_THRESHOLD
worker_pids: list[int] = []
@ -795,7 +806,15 @@ def run_tests(report_env: bool = False) -> None:
if use_parallel:
python_ok, go_ok = collect_worker_results(worker_pids, worker_procs, read_fds, len(tests_list), go_proc=go_proc)
elif tests_list:
python_ok = run_cli(all_tests, args.verbosity)
# we need fonts installed in the user home directory as well, so initialize
# fontconfig before nuking $HOME and friends
from kitty.fonts.common import all_fonts_map
all_fonts_map(True)
# Tests must never run against the real $HOME, some of them delete
# files/directories in it. See run_test_worker() for the parallel equivalent.
with env_for_python_tests():
python_ok = run_cli(all_tests, args.verbosity)
if go_proc is not None:
_, go_ok = collect_worker_results([], [], [], 0, go_proc=go_proc)
else: