Files
rsync/testsuite/read-batch-pipe_test.py
Andrew Tridgell 26a2984094 fix fleettest for recent changes (#1096)
* testsuite: fix fleettest example json for IDN

match CI for IDN tests

* testsuite: fix fleettest example skip extras and pin Cygwin python

#1069 replaced daemon-max-alloc-zero with max-alloc-zero, which no
longer needs the old static client, but the per-box expect_skip_extra
entries still named the old test, so runtests refused the skip list
("no such test") on every Linux target.  Drop those entries.

Pin python3.9 on the cygwin target to match the workflow, which installs
only python39: the box's default python3 (3.12) has a working AF_UNIX
socketpair, so daemon-stdin-local-socket ran where cygwin.txt expects a
skip.  Note that python39 must be 3.9.25+ for os.getxattr, which the
xattr tests need to run as they do in CI.

* testsuite: run bash without POSIXLY_CORRECT; tolerate Cygwin symlink copystat

runtests.py exports POSIXLY_CORRECT=1 to every test, and bash before 5.1
disables process substitution in POSIX mode, so pseudo-paths,
pseudo-paths-daemon and read-batch-pipe silently skipped on bash 4.4/5.0
hosts (Ubuntu 18.04/20.04, AlmaLinux 8).  Strip the variable from bash's
environment: the tests require bash precisely for that syntax.

That was also why they skipped in the AlmaLinux 8 CI container, so its
skip list (which held only those three) goes away along with the
workflow's reference to it.  The macOS list keeps them: there they skip
as Linux-only.

malicious-sender-delete-scope copies the source tree with symlinks=True.
Cygwin's python 3.9.25 gained os.listxattr, and copystat() on a fresh
symlink then fails with EACCES, although the link itself was created.
Ignore copytree errors that are only about symlink pairs; anything else
still raises.

* testsuite: update max-alloc entry
2026-09-21 08:28:02 +10:00

86 lines
2.9 KiB
Python

#!/usr/bin/env python3
"""--read-batch process substitution /dev/fd/ pipe must not crash with strict file-type checks."""
import os
import shlex
import shutil
import subprocess
import tempfile
import sys
from pathlib import Path
from rsyncfns import SCRATCHDIR, makepath, rmtree, rsync_argv, test_fail, test_skipped
# We require bash specifically because standard POSIX /bin/sh does not
# guarantee support for <(...) process substitution syntax.
if not sys.platform.startswith('linux'):
test_skipped('This test requires Linux platform')
bash = shutil.which('bash')
if bash is None:
test_skipped('bash is unavailable, cannot test process substitution')
# runtests.py exports POSIXLY_CORRECT=1, and bash before 5.1 disables process
# substitution in POSIX mode -- the very syntax bash is required for here.
bash_env = {k: v for k, v in os.environ.items() if k != 'POSIXLY_CORRECT'}
# Verify the host bash actually supports process substitution
probe = subprocess.run(
[bash, '-c', 'cat <(echo "probe")'],
env=bash_env,
capture_output=True)
if probe.returncode != 0:
test_skipped('bash process substitution is not supported on this system')
base = Path(SCRATCHDIR / 'rsync-batch-fifo')
src = base / 'src'
dest = base / 'dest'
batch_file = base / 'update.batch'
makepath(src, dest)
# 1. Create dummy data
(src / 'payload.txt').write_text('batch payload data\n')
# 2. Generate a valid batch file so `cat` actually has a real file to read.
# Note: This operation also copies the file to `dest` as a side effect.
subprocess.run([*rsync_argv('-a', f'--write-batch={batch_file}'), f'{src}/', f'{dest}/'], check=True)
# must wipe and recreate the destination directory so the test can
# properly prove that --read-batch recreates the files from scratch.
rmtree(dest)
makepath(dest)
# 3. Now we can test reading it via bash process substitution
rsync_base_cmd = shlex.join(rsync_argv('-a'))
batch_path = shlex.quote(str(batch_file))
dest_path = shlex.quote(str(dest) + '/')
# Construct the bash command: rsync -a --read-batch=<(cat /path/to/batch) /dest/
bash_script = f"{rsync_base_cmd} --read-batch=<(cat {batch_path}) {dest_path}"
try:
proc_read = subprocess.run(
[bash, '-c', bash_script],
capture_output=True,
env=bash_env,
text=True,
timeout=10,
)
except subprocess.TimeoutExpired:
rmtree(base)
test_fail('process substitution batch test timed out')
ctx = f'rc={proc_read.returncode}, stderr={proc_read.stderr.strip()!r}'
# Evaluate result against the strict S_ISREG check bug
if proc_read.returncode != 0:
rmtree(base)
test_fail(f'rsync crashed reading batch file from pipe ({ctx})')
if not (dest / 'payload.txt').is_file():
rmtree(base)
test_fail(f'rsync exited successfully but payload is missing in target ({ctx})')
rmtree(base)
print('rsync successfully parsed batch stream via process substitution pseudo-path')
raise SystemExit(0)