. "$BST_LIB" require rsync rsync/present # rsync's LOCAL mode is the platform claim this suite is really making. `rsync # SRC DST` with no remote host still forks a child (pipe.c:local_child) and # speaks the rsync wire protocol to it over two fd pairs -- so every transfer # below exercises fork + a bidirectional pipe conversation between two # processes, not just a file copy. fd_pair() uses socketpair() only when # HAVE_SOCKETPAIR is defined, and configure's probe for it is an AC_RUN_IFELSE # whose cross-compile default is the string "cross" rather than "yes" -- so this # cross build takes the pipe() branch and the local path needs no AF_UNIX socket # at all, which is exactly what makes rsync portable to this target. # # No `sleep` anywhere: relative timers are broken here # (linux-in-the-browser#120, `sleep N` returns in ~1s and `timeout` hangs), so # every mtime this suite depends on is set explicitly with `touch`, never waited # for. The whole fixture is built under $TMPDIR (tmpfs) rather than the virtiofs # root, which refuses anything that is not a regular file. tmp=${TMPDIR:-/tmp} w=$tmp/bst-rsync.$$ rm -rf "$w" mkdir -p "$w/src/sub" printf 'alpha\n' > "$w/src/a.txt" printf 'bravo\n' > "$w/src/sub/b.txt" printf 'charlie\n' > "$w/src/skipme.log" chmod 0641 "$w/src/a.txt" ln -s a.txt "$w/src/link" # --- 1. identity: this is rsync, and it is THIS rsync ---------------------- check_out rsync/version "rsync version" rsync --version check_out rsync/protocol "protocol version" rsync --version # --- 2. the fork + pipe local protocol ------------------------------------- mkdir -p "$w/dst" check rsync/copy-file rsync "$w/src/a.txt" "$w/dst/" check_eq rsync/copy-file-content "alpha" "$(cat "$w/dst/a.txt" 2>/dev/null)" rm -rf "$w/dst"; mkdir -p "$w/dst" check rsync/copy-tree rsync -a "$w/src/" "$w/dst/" check_eq rsync/tree-nested "bravo" "$(cat "$w/dst/sub/b.txt" 2>/dev/null)" check_eq rsync/tree-symlink "a.txt" "$(readlink "$w/dst/link" 2>/dev/null)" check_eq rsync/tree-perms "641" "$(stat -c %a "$w/dst/a.txt" 2>/dev/null)" # --- 3. the delta engine: what rsync adds over cp -------------------------- # A second run of an already-synced tree must itemize NOTHING. This is the # quiescence property cp cannot express -- and it is also the strongest evidence # the mtime/size comparison survived the round trip through the child. out=$(rsync -ai "$w/src/" "$w/dst/" 2>&1) if [ -z "$out" ]; then bst_ok rsync/incremental-noop else bst_notok rsync/incremental-noop "expected no itemized changes, got: $(_bst_1line "$out")"; fi # Change ONE file; only that file may be itemized. printf 'alpha-changed\n' > "$w/src/a.txt" out=$(rsync -ai "$w/src/" "$w/dst/" 2>&1) case "$out" in *a.txt*) bst_ok rsync/incremental-changed ;; *) bst_notok rsync/incremental-changed "changed file not itemized: $(_bst_1line "$out")" ;; esac case "$out" in *b.txt*) bst_notok rsync/incremental-scoped "unchanged b.txt was re-sent: $(_bst_1line "$out")" ;; *) bst_ok rsync/incremental-scoped ;; esac check_eq rsync/incremental-content "alpha-changed" "$(cat "$w/dst/a.txt" 2>/dev/null)" # --- 4. --delete: mirror semantics, which plain copying has no analogue for - : > "$w/dst/extra.txt" check rsync/delete-run rsync -a --delete "$w/src/" "$w/dst/" if [ -e "$w/dst/extra.txt" ]; then bst_notok rsync/delete "extra.txt survived --delete" else bst_ok rsync/delete; fi # --- 5. filter rules ------------------------------------------------------- mkdir -p "$w/dst2" check rsync/exclude-run rsync -a --exclude='*.log' "$w/src/" "$w/dst2/" if [ -e "$w/dst2/skipme.log" ]; then bst_notok rsync/exclude "excluded file was copied" else bst_ok rsync/exclude; fi # the non-excluded siblings must still be there -- an --exclude that copies # nothing would pass the assertion above for the wrong reason. check_eq rsync/exclude-scoped "alpha-changed" "$(cat "$w/dst2/a.txt" 2>/dev/null)" # --- 6. --dry-run reports without acting ----------------------------------- mkdir -p "$w/dst3" out=$(rsync -ain "$w/src/" "$w/dst3/" 2>&1) case "$out" in *a.txt*) bst_ok rsync/dry-run-reports ;; *) bst_notok rsync/dry-run-reports "-n itemized nothing: $(_bst_1line "$out")" ;; esac if [ -e "$w/dst3/a.txt" ]; then bst_notok rsync/dry-run-inert "-n actually created the file" else bst_ok rsync/dry-run-inert; fi # --- 7. --checksum: the comparison cp and mtime-matching cannot make ------- # Same size, same mtime, different content: the default size+mtime heuristic # must MISS it and -c must CATCH it. Asserting both directions is the point -- # the first half proves the second is not just "rsync copies everything". mkdir -p "$w/csum" printf 'AAAA\n' > "$w/csum/f.txt" rsync -a "$w/csum/f.txt" "$w/dst4-f.txt" 2>/dev/null || mkdir -p "$w/dst4" mkdir -p "$w/dst4" rsync -a "$w/csum/" "$w/dst4/" >/dev/null 2>&1 printf 'BBBB\n' > "$w/csum/f.txt" touch -r "$w/dst4/f.txt" "$w/csum/f.txt" # identical size AND mtime out=$(rsync -ai "$w/csum/" "$w/dst4/" 2>&1) if [ -z "$out" ]; then bst_ok rsync/quick-check-misses else bst_notok rsync/quick-check-misses "size+mtime check unexpectedly re-sent: $(_bst_1line "$out")"; fi out=$(rsync -aic "$w/csum/" "$w/dst4/" 2>&1) case "$out" in *f.txt*) bst_ok rsync/checksum-catches ;; *) bst_notok rsync/checksum-catches "-c missed a same-size same-mtime change: $(_bst_1line "$out")" ;; esac check_eq rsync/checksum-content "BBBB" "$(cat "$w/dst4/f.txt" 2>/dev/null)" # --- 8. --stats: the summary the protocol child reports back --------------- check_out rsync/stats "Number of files" rsync -a --stats "$w/src/" "$w/dst/" # --- 9. discrimination: a missing source must FAIL, not silently succeed ---- out=$(rsync -a "$w/does-not-exist/" "$w/dst/" 2>&1); rc=$? if [ "$rc" -ne 0 ]; then bst_ok rsync/missing-src else bst_notok rsync/missing-src "absent source returned rc=0: $(_bst_1line "$out")"; fi rm -rf "$w"