From 1b7ea4212f17196ba5d333de8ccb51800b6cd540 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Fri, 6 Nov 2026 08:52:31 +0100 Subject: [PATCH] fix: tests write temporary files under tmp/ 264 TemporaryDirectory() and mkdtemp() calls without dir= wrote to the /tmp tmpfs, and a full tmpfs failed modules no change touched. The test package now points tempfile and TMPDIR at tmp/tests, and sets GIT_CEILING_DIRECTORIES there so git run in a scratch directory does not find the checkout above it. The package test asserts both. --- tests/__init__.py | 14 +++++++++++--- tests/test_audit_regressions.py | 7 ++++++- wiki/testing-guide.md | 8 +++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index bbe42a6ec..1540b4305 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -13,6 +13,7 @@ temporary directory instead of naming the missing parent. from __future__ import annotations import os +import tempfile from pathlib import Path os.environ.setdefault("RETROBIOS_OFFLINE", "1") @@ -22,9 +23,16 @@ os.environ.setdefault( str(Path(__file__).resolve().parent.parent / "tmp" / "tests" / "probe.lock"), ) -(Path(__file__).resolve().parent.parent / "tmp" / "tests").mkdir( - parents=True, exist_ok=True -) +_SCRATCH = Path(__file__).resolve().parent.parent / "tmp" / "tests" +_SCRATCH.mkdir(parents=True, exist_ok=True) +# A TemporaryDirectory() without dir= landed in /tmp, a 4 GB tmpfs here: once +# another run filled it, modules unrelated to any change failed with ENOSPC. +# The default is the repository's tmp/, for this process and its children. +os.environ["TMPDIR"] = str(_SCRATCH) +tempfile.tempdir = str(_SCRATCH) +# A scratch directory is not part of this checkout: git run inside one must +# not walk up to the repository, as it could not from /tmp. +os.environ["GIT_CEILING_DIRECTORIES"] = str(_SCRATCH) # The installer's cache (the games index, what reading a card found) is the # suite's own: a run never reads nor fills the one of the person running it. os.environ.setdefault( diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 3df8850fe..388a7cd0c 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -206,11 +206,16 @@ class AModuleRunAloneHasItsScratchDirectory(unittest.TestCase): ) proc = subprocess.run( [sys.executable, "-I", "-c", - "import sys; sys.path.insert(0, '.'); import tests"], + "import sys; sys.path.insert(0, '.'); import tests, tempfile, os;" + " print(tempfile.gettempdir()); print(os.environ['TMPDIR'])"], cwd=clone, capture_output=True, text=True, timeout=60, check=False, ) self.assertEqual(proc.returncode, 0, proc.stderr) self.assertTrue((clone / "tmp" / "tests").is_dir()) + # 264 TemporaryDirectory() calls without dir= wrote to the /tmp + # tmpfs, and a full tmpfs failed modules no change touched. + scratch = str((clone / "tmp" / "tests").resolve()) + self.assertEqual(proc.stdout.split(), [scratch, scratch]) class ReleaseSigningRegressions(unittest.TestCase): diff --git a/wiki/testing-guide.md b/wiki/testing-guide.md index 3ef512d15..1a9f64b74 100644 --- a/wiki/testing-guide.md +++ b/wiki/testing-guide.md @@ -9,9 +9,11 @@ The tests hold what users and platforms rely on: a pack holds what its platform checks, the site states what the code does, the installer writes only where it should. -No test needs network access. Most build synthetic fixtures under the -repository's `tmp/`, and modules that read real artifacts skip when those are -absent. The test runner prints the count. +No test needs network access. Most build synthetic fixtures, and every +temporary directory lands under the repository's `tmp/tests/`: +`tests/__init__.py` points `tempfile` and `TMPDIR` there, never at the `/tmp` +tmpfs. Modules that read real artifacts skip when those are absent. The test +runner prints the count. ## Running tests