]> git.ipfire.org Git - thirdparty/rsync.git/commitdiff
testsuite: verify --fuzzy actually selects a basis
authorAndrew Tridgell <andrew@tridgell.net>
Sun, 24 May 2026 22:01:11 +0000 (08:01 +1000)
committerAndrew Tridgell <andrew@tridgell.net>
Mon, 25 May 2026 21:43:00 +0000 (07:43 +1000)
Both fuzzy tests asserted only that the final file content matched, which a
full transfer that ignored --fuzzy would also satisfy -- so a broken fuzzy
basis selection would pass undetected. Drive rsync directly with --debug=FUZZY
and assert the generator reports the expected basis ("fuzzy basis selected
for <f>: <basis>", generator.c find_fuzzy): rsync2.c for fuzzy, and the
closest-named candidate archive-v1.tar for fuzzy-basis. fuzzy switches from
checkit() to a manual run plus verify_dirs() so the output can be captured.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
testsuite/fuzzy-basis_test.py
testsuite/fuzzy_test.py

index 77889439b68a28a87747077a4717400f056f4614..d91a3b9ed9e7b51bc7959f5bc2e0703ad18a35a6 100644 (file)
@@ -11,7 +11,7 @@ import os
 
 from rsyncfns import (
     FROMDIR, TODIR,
-    assert_same, make_data_file, makepath, rmtree, run_rsync,
+    assert_same, make_data_file, makepath, rmtree, run_rsync, test_fail,
 )
 
 src = FROMDIR
@@ -31,7 +31,15 @@ base = (src / newfile).read_bytes()
 (TODIR / deepdir / 'archive-old.tar').write_bytes(base[:200_000])
 (TODIR / deepdir / 'unrelated.dat').write_bytes(b'nothing alike' * 1000)
 
-run_rsync('-a', '--fuzzy', '--no-whole-file', f'{src}/', f'{TODIR}/')
+# Capture --debug=FUZZY: a correct final file alone would also result from a
+# full transfer that ignored --fuzzy, so assert the scorer actually chose the
+# closest-named candidate (archive-v1.tar) as the basis, not just that bytes match.
+proc = run_rsync('-a', '--fuzzy', '--no-whole-file', '--debug=FUZZY',
+                 f'{src}/', f'{TODIR}/', capture_output=True)
+want = f'fuzzy basis selected for {newfile}: {os.path.join(deepdir, "archive-v1.tar")}'
+if want not in proc.stdout:
+    test_fail(f"--fuzzy did not score archive-v1.tar as the closest basis; "
+              f"expected {want!r}, --debug=FUZZY output was:\n{proc.stdout}")
 assert_same(TODIR / newfile, src / newfile, label='fuzzy result')
 
 print("fuzzy-basis: --fuzzy candidate scoring (fuzzy_distance) verified at depth")
index 7858fcc26bafeeea1283088661ee3b9c0428b8bb..03c57301b7da4e5d59b5006639b76daf040f237d 100644 (file)
@@ -8,7 +8,9 @@
 
 import time
 
-from rsyncfns import FROMDIR, SRCDIR, TODIR, checkit, cp_p, cp_touch
+from rsyncfns import (
+    FROMDIR, SRCDIR, TODIR, cp_p, cp_touch, run_rsync, test_fail, verify_dirs,
+)
 
 
 FROMDIR.mkdir(parents=True, exist_ok=True)
@@ -18,5 +20,15 @@ cp_p(SRCDIR / 'rsync.c', FROMDIR / 'rsync.c')
 cp_touch(FROMDIR / 'rsync.c', TODIR / 'rsync2.c')
 time.sleep(1)
 
-checkit(['-avvi', '--no-whole-file', '--fuzzy', '--delete-delay',
-         f'{FROMDIR}/', f'{TODIR}/'], FROMDIR, TODIR)
+# Drive rsync directly (rather than checkit) so we can capture --debug=FUZZY:
+# a final tree match alone would also be produced by a full transfer that
+# ignored --fuzzy, so assert the generator actually picked rsync2.c as the
+# fuzzy basis for rsync.c (generator.c find_fuzzy / "fuzzy basis selected").
+proc = run_rsync('-avvi', '--no-whole-file', '--fuzzy', '--delete-delay',
+                 '--debug=FUZZY', f'{FROMDIR}/', f'{TODIR}/',
+                 capture_output=True)
+if 'fuzzy basis selected for rsync.c: rsync2.c' not in proc.stdout:
+    test_fail("--fuzzy did not select rsync2.c as the basis for rsync.c; "
+              f"--debug=FUZZY output was:\n{proc.stdout}")
+# ...and --delete-delay still removes the stale basis, leaving TODIR == FROMDIR.
+verify_dirs(FROMDIR, TODIR)