]> git.ipfire.org Git - thirdparty/rsync.git/commitdiff
testsuite: generate predictable fixture files instead of reading /etc, /bin, /
authorAndrew Tridgell <andrew@tridgell.net>
Fri, 22 May 2026 04:41:17 +0000 (14:41 +1000)
committerAndrew Tridgell <andrew@tridgell.net>
Fri, 22 May 2026 05:15:22 +0000 (15:15 +1000)
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) <noreply@anthropic.com>
testsuite/longdir_test.py
testsuite/rsyncfns.py

index ead1fa4d105a9485c5a3ad335a9a1438b8c28426..4676c3f1a1e43447f20c6e20ada38a8c3a6cf8c2 100644 (file)
@@ -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)
index 5b42654e518d7fe609e6577b28df631e6f40c873..371c2c251b0cdd3fd4d26149ddeaf4aa42494b98 100644 (file)
@@ -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 ------------------------------------------------