diff --git a/testsuite/ki58-log-format-percent_test.py b/testsuite/ki58-log-format-percent_test.py index 99bcc61f..826a7004 100644 --- a/testsuite/ki58-log-format-percent_test.py +++ b/testsuite/ki58-log-format-percent_test.py @@ -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") diff --git a/testsuite/ki62-io-error-mask_test.py b/testsuite/ki62-io-error-mask_test.py index c0dc98f2..d57b4579 100644 --- a/testsuite/ki62-io-error-mask_test.py +++ b/testsuite/ki62-io-error-mask_test.py @@ -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 /" 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: