From 1d8f47cc711cd2dbee6aef6c14299b693f341b90 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 22 May 2026 14:41:17 +1000 Subject: [PATCH] testsuite: generate predictable fixture files instead of reading /etc, /bin, / The Python rewrite of the suite carried over the shell habit of populating the test tree by capturing "ls -l /etc" / "ls -l /bin" (falling back to "ls /"): hands_setup() built etc-ltr-list / bin-lt-list that way, and longdir_test.py did the same for its leaf files. That ties the fixtures to the host filesystem layout -- those directories are absent or unreadable on Android/Termux and other minimal environments, where "ls /" fails outright -- and the captured content was never reproducible from run to run. Add a deterministic make_text_file() helper to rsyncfns.py and use it for hands_setup()'s two fixture files and longdir's leaf files. The names etc-ltr-list / bin-lt-list are unchanged (chmod, chmod-temp-dir and alt-dest reference them by name); only the content source changes, so the fixtures are now self-contained and identical on every platform. This also drops longdir_test.py's date(1) and ls(1) subprocess calls. Co-Authored-By: Claude Opus 4.7 (1M context) --- testsuite/longdir_test.py | 17 +++++------------ testsuite/rsyncfns.py | 40 ++++++++++++++++++++++++++++----------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/testsuite/longdir_test.py b/testsuite/longdir_test.py index ead1fa4d..4676c3f1 100644 --- a/testsuite/longdir_test.py +++ b/testsuite/longdir_test.py @@ -6,10 +6,8 @@ # 175-char directory names, drop a couple of files in the leaf, and # verify that --delete -avH still produces an identical destination. -import os -import subprocess - -from rsyncfns import FROMDIR, TODIR, checkit, hands_setup, test_skipped +from rsyncfns import (FROMDIR, TODIR, checkit, hands_setup, make_text_file, + test_skipped) hands_setup() @@ -29,13 +27,8 @@ try: except OSError: test_skipped("unable to create files in long directory") -# Drop some recognisably-varied content into the two leaf files. -(longdir / '1').write_text(subprocess.check_output(['date'], text=True)) - -listdir = '/etc' if os.access('/etc', os.R_OK) else '/' -out = subprocess.run(['ls', '-la', listdir], capture_output=True, text=True) -# ls exits 1 if it can't stat some entries (e.g. permission-denied files in -# /etc); the shell test silently accepts that. We do the same. -(longdir / '2').write_text(out.stdout) +# Drop predictable, self-contained content into the two leaf files. +make_text_file(longdir / '1', 50) +make_text_file(longdir / '2', 100) checkit(['--delete', '-avH', f'{FROMDIR}/', str(TODIR)], FROMDIR, TODIR) diff --git a/testsuite/rsyncfns.py b/testsuite/rsyncfns.py index 5b42654e..371c2c25 100644 --- a/testsuite/rsyncfns.py +++ b/testsuite/rsyncfns.py @@ -410,6 +410,26 @@ def make_data_file(path, size: int) -> 'None': f.write(bytes(out)) +def make_text_file(path, lines: int = 100) -> 'None': + """Write a predictable, self-contained text file of `lines` lines. + + This replaces the old habit of capturing `ls -l /etc` / `ls -l /bin` + (falling back to `ls /`) into the test tree. Those tied the fixtures + to the host filesystem layout: the directories are absent or + unreadable on Android/Termux and other minimal environments, where + `ls /` fails outright, and the captured content was never + reproducible. The output here is deterministic and depends on nothing + outside the suite, so every platform builds the identical fixture. + """ + content = ''.join( + "line %06d the quick brown fox jumps over the lazy dog %d %d\n" + % (i, (i * 31) % 97, (i * 131) % 89) + for i in range(1, lines + 1) + ) + with open(str(path), 'w') as f: + f.write(content) + + def get_testuid() -> int: return os.getuid() @@ -677,11 +697,12 @@ def build_symlinks() -> 'None': def hands_setup() -> 'None': - """Equivalent of rsync.fns hands_setup: populate FROMDIR with a varied - tree of files and directories for the canonical 'hands' transfer test. + """Populate FROMDIR with a varied tree of files and directories for the + canonical 'hands' transfer test. - Recreates the shell behavior bit-for-bit so the tls listings match - across the shell and Python halves of the suite during the transition. + All content is generated from within the suite (srcdir contents plus + make_text_file output) so the fixture is self-contained and + reproducible on every platform. """ rmtree(FROMDIR) rmtree(TODIR) @@ -717,15 +738,12 @@ def hands_setup() -> 'None': (FROMDIR / 'dir' / 'subdir').mkdir(exist_ok=True) (FROMDIR / 'dir' / 'subdir' / 'foobar.baz').write_text("some data\n") (FROMDIR / 'dir' / 'subdir' / 'subsubdir').mkdir(exist_ok=True) - - src_listdir = '/etc' if os.access('/etc', os.R_OK) else '/' - out = subprocess.run(['ls', '-ltr', src_listdir], capture_output=True, text=True) - (FROMDIR / 'dir' / 'subdir' / 'subsubdir' / 'etc-ltr-list').write_text(out.stdout) + # Predictable, self-contained fixture files (the names etc-ltr-list / + # bin-lt-list are kept because other tests reference them by name). + make_text_file(FROMDIR / 'dir' / 'subdir' / 'subsubdir' / 'etc-ltr-list', 120) (FROMDIR / 'dir' / 'subdir' / 'subsubdir2').mkdir(exist_ok=True) - src_listdir = '/bin' if os.access('/bin', os.R_OK) else '/' - out = subprocess.run(['ls', '-lt', src_listdir], capture_output=True, text=True) - (FROMDIR / 'dir' / 'subdir' / 'subsubdir2' / 'bin-lt-list').write_text(out.stdout) + make_text_file(FROMDIR / 'dir' / 'subdir' / 'subsubdir2' / 'bin-lt-list', 200) # --- listing / verification ------------------------------------------------