testsuite: harden ki58/ki62 tests for the fleet

Two of the integrated MC/DC-audit tests were environment-fragile:

- ki58: asserted the exact string '100% done percentfile', but %f expands to the
  transfer-relative path (with leading dirs) when the source is an absolute path,
  so the basename assumption failed.  Assert the literal-percent escape '100% done '
  plus the file name instead (still RED on a broken %%: it emits '100%% done').

- ki62: killed daemon.kill() -- the listener -- but rsyncd forks a child per
  connection, so the child sender kept streaming and the receiver hung.  Parse the
  transfer child's pid from the daemon log ('[pid] rsync on <mod>/') and kill that.
  killpg is not usable: the test daemon shares this test's process group.

Fixes are unchanged; only the test drivers.
This commit is contained in:
Andrew Tridgell committed 2026-07-20 14:07:05 +10:00
1 parent 3534cab477
commit 7490ad2420
2 files changed
+29 -5

No files matched your search

+8 -3
View File
@@ -33,10 +33,15 @@ p = subprocess.run(
if p.returncode != 0:
test_fail(f"rsync exited {p.returncode}:\n{p.stderr}")
expected = '100% done percentfile'
if expected not in p.stdout:
# %f expands to the transfer-relative path, which includes leading directories
# when the source is addressed by an absolute path (as here) -- so don't pin the
# exact filename. The point of this test is the escape: "100% done " with a
# single literal percent followed by a space (a broken %% consumes the space or
# leaves a doubled percent), plus the transferred file name somewhere.
if '100% done ' not in p.stdout or 'percentfile' not in p.stdout:
test_fail(
f"expected {expected!r} in --out-format output, got:\n{p.stdout}"
"expected '100% done ' (a single literal percent) and 'percentfile' in "
f"--out-format output, got:\n{p.stdout}"
)
print("ki58-log-format-percent: %% literal-percent escape verified")
+21 -2
View File
@@ -19,12 +19,15 @@
# Exit codes: 0 pass, 1 fail, 77 skip (killable daemon testing not possible --
# the test needs --use-tcp so it can SIGKILL the sender process directly).
import os
import re
import signal
import subprocess
import sys
import time
from rsyncfns import (
FROMDIR, TODIR, claim_ports, make_data_file, makepath, rmtree,
FROMDIR, SCRATCHDIR, TODIR, claim_ports, make_data_file, makepath, rmtree,
rsync_argv, start_rsyncd, test_fail, test_skipped, build_rsyncd_conf,
USE_TCP,
)
@@ -97,7 +100,23 @@ client = subprocess.Popen(
# (the path that exercises the io_error -> exit-code mapping).
time.sleep(2)
# Kill the sender (daemon) mid-transfer.
# Kill the sender mid-transfer. rsyncd forks a child per connection, so the
# actual sender is NOT `daemon` (the listener) -- killing only the listener
# leaves the child streaming and the receiver never sees EOF. The child logs
# "[pid] rsync on <module>/" to the daemon log; kill that pid (plus the
# listener). The daemon shares this test's process group, so killpg is not an
# option (it would kill the test itself).
child_pids = []
try:
logtext = (SCRATCHDIR / 'rsyncd.log').read_text()
child_pids = [int(pid) for pid in re.findall(r'\[(\d+)\] rsync on ', logtext)]
except OSError:
pass
for pid in child_pids:
try:
os.kill(pid, signal.SIGKILL)
except (ProcessLookupError, PermissionError):
pass
if daemon.poll() is None:
daemon.kill()
try: