mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-06-08 06:05:57 -04:00
Fill the highest-restructure-risk gap: options that do two-directory / rename /
outside-tree work, asserted at >=3 levels deep with the aux tree kept outside
the main tree, and asserting the option's specific property rather than just
tree equality (which the ported tests already cover).
alt-dest-deep --link-dest hardlinks unchanged files (same inode), --copy-dest
copies (never links), --compare-dest omits unchanged files;
ref tree outside both src and dest.
temp-dir cross-dir temp->final rename at depth; temp dir left clean; a
missing --temp-dir fails (so the option is proven consulted).
partial --partial keeps the partial in the dest file; relative
--partial-dir stages per-directory at depth (pre-seed +
interrupt/resume); absolute --partial-dir writes the partial
outside the tree.
inplace --inplace keeps the destination inode across a delta update;
the default temp+rename path replaces it.
append --append completes truncated files tail-only; --append-verify
repairs a corrupted prefix (protocol >= 30).
backup-deep --suffix saves <name>S beside the new file; --backup-dir
relocates old files to a parallel deep tree outside the dest
and captures deletions under --delete.
All green on master and under --protocol=29/30.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
63 lines
2.1 KiB
Python
63 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Property-level coverage of --delay-updates at depth.
|
|
|
|
--delay-updates writes each updated file into a per-directory staging dir
|
|
(.~tmp~) during the transfer and only renames them into place at the very end,
|
|
so an interrupted run never leaves a half-written file visible. The staging dir
|
|
sits inside each destination directory, so the staging write and the
|
|
end-of-run rename are parent-directory operations the resolver restructure
|
|
rewrites; the ported delay-updates_test.py only exercises the tree root, so
|
|
this companion drives a >=3-deep tree.
|
|
|
|
Asserts, at every level of the tree:
|
|
* a --delay-updates transfer reproduces the source and leaves no .~tmp~
|
|
staging directory behind;
|
|
* a stale file pre-planted in a deep .~tmp~ staging dir is overwritten
|
|
cleanly and the staging dir is removed.
|
|
"""
|
|
|
|
import os
|
|
|
|
from rsyncfns import (
|
|
FROMDIR, TODIR,
|
|
assert_same, make_tree, rmtree, run_rsync, test_fail, walk_dirs,
|
|
walk_files,
|
|
)
|
|
|
|
src = FROMDIR
|
|
deepdir = os.path.join('d1', 'd2', 'd3')
|
|
|
|
|
|
def no_staging_left():
|
|
leftover = [p for p in walk_dirs(TODIR) if p.name == '.~tmp~']
|
|
if leftover:
|
|
test_fail(f"--delay-updates left staging dirs behind: {leftover}")
|
|
|
|
|
|
# --- initial --delay-updates over a deep tree -------------------------------
|
|
rmtree(src)
|
|
rmtree(TODIR)
|
|
make_tree(src, depth=3, data=True, data_size=4096)
|
|
rels = [p.relative_to(src) for p in walk_files(src)]
|
|
|
|
run_rsync('-a', '--delay-updates', f'{src}/', f'{TODIR}/')
|
|
for rel in rels:
|
|
assert_same(TODIR / rel, src / rel, label=f'delay-updates initial {rel}')
|
|
no_staging_left()
|
|
|
|
# --- update every file, with a stale staging file planted deep --------------
|
|
for rel in rels:
|
|
with open(src / rel, 'ab') as f:
|
|
f.write(b'\nupdated content\n')
|
|
|
|
stage = TODIR / deepdir / '.~tmp~'
|
|
stage.mkdir(parents=True, exist_ok=True)
|
|
(stage / 'f3').write_bytes(b'stale staged junk\n') # must be overwritten
|
|
|
|
run_rsync('-a', '--delay-updates', f'{src}/', f'{TODIR}/')
|
|
for rel in rels:
|
|
assert_same(TODIR / rel, src / rel, label=f'delay-updates update {rel}')
|
|
no_staging_left()
|
|
|
|
print("delay-updates-deep: staging + clean overwrite verified at depth")
|