]> arthur.barton.de Git - bup.git/commitdiff
Remove extensions from test command names
authorRob Browning <rlb@defaultvalue.org>
Sun, 15 Nov 2020 19:53:42 +0000 (13:53 -0600)
committerRob Browning <rlb@defaultvalue.org>
Thu, 26 Nov 2020 21:53:09 +0000 (15:53 -0600)
Signed-off-by: Rob Browning <rlb@defaultvalue.org>
63 files changed:
test/ext/conftest.py
test/ext/test-cat-file [new file with mode: 0755]
test/ext/test-cat-file.sh [deleted file]
test/ext/test-command-without-init-fails [new file with mode: 0755]
test/ext/test-command-without-init-fails.sh [deleted file]
test/ext/test-compression [new file with mode: 0755]
test/ext/test-compression.sh [deleted file]
test/ext/test-drecurse [new file with mode: 0755]
test/ext/test-drecurse.sh [deleted file]
test/ext/test-fsck [new file with mode: 0755]
test/ext/test-fsck.sh [deleted file]
test/ext/test-fuse [new file with mode: 0755]
test/ext/test-fuse.sh [deleted file]
test/ext/test-gc [new file with mode: 0755]
test/ext/test-gc.sh [deleted file]
test/ext/test-import-duplicity [new file with mode: 0755]
test/ext/test-import-duplicity.sh [deleted file]
test/ext/test-import-rdiff-backup [new file with mode: 0755]
test/ext/test-import-rdiff-backup.sh [deleted file]
test/ext/test-index [new file with mode: 0755]
test/ext/test-index-check-device [new file with mode: 0755]
test/ext/test-index-check-device.sh [deleted file]
test/ext/test-index-clear [new file with mode: 0755]
test/ext/test-index-clear.sh [deleted file]
test/ext/test-index.sh [deleted file]
test/ext/test-list-idx [new file with mode: 0755]
test/ext/test-list-idx.sh [deleted file]
test/ext/test-main [new file with mode: 0755]
test/ext/test-main.sh [deleted file]
test/ext/test-meta [new file with mode: 0755]
test/ext/test-meta.sh [deleted file]
test/ext/test-on [new file with mode: 0755]
test/ext/test-on.sh [deleted file]
test/ext/test-redundant-saves [new file with mode: 0755]
test/ext/test-redundant-saves.sh [deleted file]
test/ext/test-release-archive [new file with mode: 0755]
test/ext/test-release-archive.sh [deleted file]
test/ext/test-restore-map-owner [new file with mode: 0755]
test/ext/test-restore-map-owner.sh [deleted file]
test/ext/test-restore-single-file [new file with mode: 0755]
test/ext/test-restore-single-file.sh [deleted file]
test/ext/test-rm [new file with mode: 0755]
test/ext/test-rm-between-index-and-save [new file with mode: 0755]
test/ext/test-rm-between-index-and-save.sh [deleted file]
test/ext/test-rm.sh [deleted file]
test/ext/test-save-creates-no-unrefs [new file with mode: 0755]
test/ext/test-save-creates-no-unrefs.sh [deleted file]
test/ext/test-save-restore-excludes [new file with mode: 0755]
test/ext/test-save-restore-excludes.sh [deleted file]
test/ext/test-save-strip-graft [new file with mode: 0755]
test/ext/test-save-strip-graft.sh [deleted file]
test/ext/test-save-with-valid-parent [new file with mode: 0755]
test/ext/test-save-with-valid-parent.sh [deleted file]
test/ext/test-sparse-files [new file with mode: 0755]
test/ext/test-sparse-files.sh [deleted file]
test/ext/test-split-join [new file with mode: 0755]
test/ext/test-split-join.sh [deleted file]
test/ext/test-tz [new file with mode: 0755]
test/ext/test-tz.sh [deleted file]
test/ext/test-web [new file with mode: 0755]
test/ext/test-web.sh [deleted file]
test/ext/test-xdev [new file with mode: 0755]
test/ext/test-xdev.sh [deleted file]

index be1b6821f8a6591840c5f5553fc3174a709b8fce..a79d03374f6cdefe60042a58e193aa160d5026d7 100644 (file)
@@ -71,6 +71,6 @@ def pytest_collect_file(parent, path):
             item = BupSubprocTestFile.from_parent(parent, fspath=path)
         except AttributeError:
             item = BupSubprocTestFile(path, parent)
-        if base == 'test-release-archive.sh':
+        if base == 'test-release-archive':
             item.add_marker(pytest.mark.release)
         return item
diff --git a/test/ext/test-cat-file b/test/ext/test-cat-file
new file mode 100755 (executable)
index 0000000..055ade0
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS bup init
+WVPASS cd "$tmpdir"
+
+WVSTART "cat-file"
+WVPASS mkdir src
+WVPASS date > src/foo
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS bup cat-file "src/latest/$(pwd)/src/foo" > cat-foo
+WVPASS diff -u src/foo cat-foo
+
+WVSTART "cat-file --meta"
+WVPASS bup meta --create --no-paths src/foo > src-foo.meta
+WVPASS bup cat-file --meta "src/latest/$(pwd)/src/foo" > cat-foo.meta
+
+WVPASS bup meta -tvvf src-foo.meta | WVPASS grep -vE '^atime: ' > src-foo.list
+WVPASS bup meta -tvvf cat-foo.meta | WVPASS grep -vE '^atime: ' > cat-foo.list
+WVPASS diff -u src-foo.list cat-foo.list
+
+WVSTART "cat-file --bupm"
+WVPASS bup cat-file --bupm "src/latest/$(pwd)/src/" > bup-cat-bupm
+src_hash=$(WVPASS bup ls -s "src/latest/$(pwd)" | cut -d' ' -f 1) || exit $?
+bupm_hash=$(WVPASS git ls-tree "$src_hash" | grep -F .bupm | cut -d' ' -f 3) \
+    || exit $?
+bupm_hash=$(WVPASS echo "$bupm_hash" | cut -d' ' -f 1) || exit $?
+WVPASS "$top/dev/git-cat-tree" "$bupm_hash" > git-cat-bupm
+if ! cmp git-cat-bupm bup-cat-bupm; then
+    cmp -l git-cat-bupm bup-cat-bupm
+    diff -uN <(bup meta -tvvf git-cat-bupm) <(bup meta -tvvf bup-cat-bupm)
+    WVPASS cmp git-cat-bupm bup-cat-bupm
+fi
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-cat-file.sh b/test/ext/test-cat-file.sh
deleted file mode 100755 (executable)
index 055ade0..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS bup init
-WVPASS cd "$tmpdir"
-
-WVSTART "cat-file"
-WVPASS mkdir src
-WVPASS date > src/foo
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS bup cat-file "src/latest/$(pwd)/src/foo" > cat-foo
-WVPASS diff -u src/foo cat-foo
-
-WVSTART "cat-file --meta"
-WVPASS bup meta --create --no-paths src/foo > src-foo.meta
-WVPASS bup cat-file --meta "src/latest/$(pwd)/src/foo" > cat-foo.meta
-
-WVPASS bup meta -tvvf src-foo.meta | WVPASS grep -vE '^atime: ' > src-foo.list
-WVPASS bup meta -tvvf cat-foo.meta | WVPASS grep -vE '^atime: ' > cat-foo.list
-WVPASS diff -u src-foo.list cat-foo.list
-
-WVSTART "cat-file --bupm"
-WVPASS bup cat-file --bupm "src/latest/$(pwd)/src/" > bup-cat-bupm
-src_hash=$(WVPASS bup ls -s "src/latest/$(pwd)" | cut -d' ' -f 1) || exit $?
-bupm_hash=$(WVPASS git ls-tree "$src_hash" | grep -F .bupm | cut -d' ' -f 3) \
-    || exit $?
-bupm_hash=$(WVPASS echo "$bupm_hash" | cut -d' ' -f 1) || exit $?
-WVPASS "$top/dev/git-cat-tree" "$bupm_hash" > git-cat-bupm
-if ! cmp git-cat-bupm bup-cat-bupm; then
-    cmp -l git-cat-bupm bup-cat-bupm
-    diff -uN <(bup meta -tvvf git-cat-bupm) <(bup meta -tvvf bup-cat-bupm)
-    WVPASS cmp git-cat-bupm bup-cat-bupm
-fi
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-command-without-init-fails b/test/ext/test-command-without-init-fails
new file mode 100755 (executable)
index 0000000..32efe2c
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+WVSTART 'all'
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS mkdir "$tmpdir/foo"
+
+bup index "$tmpdir/foo" &> /dev/null
+index_rc=$?
+WVPASSEQ "$index_rc" "15"
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-command-without-init-fails.sh b/test/ext/test-command-without-init-fails.sh
deleted file mode 100755 (executable)
index 32efe2c..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-
-set -o pipefail
-
-WVSTART 'all'
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS mkdir "$tmpdir/foo"
-
-bup index "$tmpdir/foo" &> /dev/null
-index_rc=$?
-WVPASSEQ "$index_rc" "15"
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-compression b/test/ext/test-compression
new file mode 100755 (executable)
index 0000000..7ca49b4
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+fs-size() { tar cf - "$@" | wc -c; }
+
+WVSTART "compression"
+WVPASS cd "$tmpdir"
+
+D=compression0.tmp
+WVPASS force-delete "$BUP_DIR"
+WVPASS bup init
+WVPASS mkdir $D
+WVPASS bup index "$top/Documentation"
+WVPASS bup save -n compression -0 --strip "$top/Documentation"
+# Some platforms set -A by default when root, so just use it everywhere.
+expected="$(WVPASS ls -A "$top/Documentation" | WVPASS sort)" || exit $?
+actual="$(WVPASS bup ls -A compression/latest/ | WVPASS sort)" || exit $?
+WVPASSEQ "$actual" "$expected"
+compression_0_size=$(WVPASS fs-size "$BUP_DIR") || exit $?
+
+D=compression9.tmp
+WVPASS force-delete "$BUP_DIR"
+WVPASS bup init
+WVPASS mkdir $D
+WVPASS bup index "$top/Documentation"
+WVPASS bup save -n compression -9 --strip "$top/Documentation"
+expected="$(ls -A "$top/Documentation" | sort)" || exit $?
+actual="$(bup ls -A compression/latest/ | sort)" || exit $?
+WVPASSEQ "$actual" "$expected"
+compression_9_size=$(WVPASS fs-size "$BUP_DIR") || exit $?
+
+WVPASS [ "$compression_9_size" -lt "$compression_0_size" ]
+
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-compression.sh b/test/ext/test-compression.sh
deleted file mode 100755 (executable)
index 7ca49b4..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-fs-size() { tar cf - "$@" | wc -c; }
-
-WVSTART "compression"
-WVPASS cd "$tmpdir"
-
-D=compression0.tmp
-WVPASS force-delete "$BUP_DIR"
-WVPASS bup init
-WVPASS mkdir $D
-WVPASS bup index "$top/Documentation"
-WVPASS bup save -n compression -0 --strip "$top/Documentation"
-# Some platforms set -A by default when root, so just use it everywhere.
-expected="$(WVPASS ls -A "$top/Documentation" | WVPASS sort)" || exit $?
-actual="$(WVPASS bup ls -A compression/latest/ | WVPASS sort)" || exit $?
-WVPASSEQ "$actual" "$expected"
-compression_0_size=$(WVPASS fs-size "$BUP_DIR") || exit $?
-
-D=compression9.tmp
-WVPASS force-delete "$BUP_DIR"
-WVPASS bup init
-WVPASS mkdir $D
-WVPASS bup index "$top/Documentation"
-WVPASS bup save -n compression -9 --strip "$top/Documentation"
-expected="$(ls -A "$top/Documentation" | sort)" || exit $?
-actual="$(bup ls -A compression/latest/ | sort)" || exit $?
-WVPASSEQ "$actual" "$expected"
-compression_9_size=$(WVPASS fs-size "$BUP_DIR") || exit $?
-
-WVPASS [ "$compression_9_size" -lt "$compression_0_size" ]
-
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-drecurse b/test/ext/test-drecurse
new file mode 100755 (executable)
index 0000000..1611384
--- /dev/null
@@ -0,0 +1,100 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS cd "$tmpdir"
+
+# These tests aren't comprehensive, but test-save-restore-excludes.sh
+# exercises some of the same code more thoroughly via index, and
+# --xdev is handled in test-xdev.sh.
+
+WVSTART "drecurse"
+WVPASS bup init
+WVPASS mkdir src src/a src/b
+WVPASS touch src/a/1 src/a/2 src/b/1 src/b/2 src/c
+(cd src && WVPASS ln -s a a-link)
+WVPASSEQ "$(bup drecurse src)" "src/c
+src/b/2
+src/b/1
+src/b/
+src/a/2
+src/a/1
+src/a/
+src/a-link
+src/"
+
+WVSTART "drecurse --exclude (file)"
+WVPASSEQ "$(bup drecurse --exclude src/b/2 src)" "src/c
+src/b/1
+src/b/
+src/a/2
+src/a/1
+src/a/
+src/a-link
+src/"
+
+WVSTART "drecurse --exclude (dir)"
+WVPASSEQ "$(bup drecurse --exclude src/b/ src)" "src/c
+src/a/2
+src/a/1
+src/a/
+src/a-link
+src/"
+
+WVSTART "drecurse --exclude (symlink)"
+WVPASSEQ "$(bup drecurse --exclude src/a-link src)" "src/c
+src/b/2
+src/b/1
+src/b/
+src/a/2
+src/a/1
+src/a/
+src/"
+
+WVSTART "drecurse --exclude (absolute path)"
+WVPASSEQ "$(bup drecurse --exclude src/b/2 "$(pwd)/src")" "$(pwd)/src/c
+$(pwd)/src/b/1
+$(pwd)/src/b/
+$(pwd)/src/a/2
+$(pwd)/src/a/1
+$(pwd)/src/a/
+$(pwd)/src/a-link
+$(pwd)/src/"
+
+WVSTART "drecurse --exclude-from"
+WVPASS echo "src/b" > exclude-list
+WVPASSEQ "$(bup drecurse --exclude-from exclude-list src)" "src/c
+src/a/2
+src/a/1
+src/a/
+src/a-link
+src/"
+
+WVSTART "drecurse --exclude-rx (trivial)"
+WVPASSEQ "$(bup drecurse --exclude-rx '^src/b' src)" "src/c
+src/a/2
+src/a/1
+src/a/
+src/a-link
+src/"
+
+WVSTART "drecurse --exclude-rx (trivial - absolute path)"
+WVPASSEQ "$(bup drecurse --exclude-rx "^$(pwd)/src/b" "$(pwd)/src")" \
+"$(pwd)/src/c
+$(pwd)/src/a/2
+$(pwd)/src/a/1
+$(pwd)/src/a/
+$(pwd)/src/a-link
+$(pwd)/src/"
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-drecurse.sh b/test/ext/test-drecurse.sh
deleted file mode 100755 (executable)
index 1611384..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS cd "$tmpdir"
-
-# These tests aren't comprehensive, but test-save-restore-excludes.sh
-# exercises some of the same code more thoroughly via index, and
-# --xdev is handled in test-xdev.sh.
-
-WVSTART "drecurse"
-WVPASS bup init
-WVPASS mkdir src src/a src/b
-WVPASS touch src/a/1 src/a/2 src/b/1 src/b/2 src/c
-(cd src && WVPASS ln -s a a-link)
-WVPASSEQ "$(bup drecurse src)" "src/c
-src/b/2
-src/b/1
-src/b/
-src/a/2
-src/a/1
-src/a/
-src/a-link
-src/"
-
-WVSTART "drecurse --exclude (file)"
-WVPASSEQ "$(bup drecurse --exclude src/b/2 src)" "src/c
-src/b/1
-src/b/
-src/a/2
-src/a/1
-src/a/
-src/a-link
-src/"
-
-WVSTART "drecurse --exclude (dir)"
-WVPASSEQ "$(bup drecurse --exclude src/b/ src)" "src/c
-src/a/2
-src/a/1
-src/a/
-src/a-link
-src/"
-
-WVSTART "drecurse --exclude (symlink)"
-WVPASSEQ "$(bup drecurse --exclude src/a-link src)" "src/c
-src/b/2
-src/b/1
-src/b/
-src/a/2
-src/a/1
-src/a/
-src/"
-
-WVSTART "drecurse --exclude (absolute path)"
-WVPASSEQ "$(bup drecurse --exclude src/b/2 "$(pwd)/src")" "$(pwd)/src/c
-$(pwd)/src/b/1
-$(pwd)/src/b/
-$(pwd)/src/a/2
-$(pwd)/src/a/1
-$(pwd)/src/a/
-$(pwd)/src/a-link
-$(pwd)/src/"
-
-WVSTART "drecurse --exclude-from"
-WVPASS echo "src/b" > exclude-list
-WVPASSEQ "$(bup drecurse --exclude-from exclude-list src)" "src/c
-src/a/2
-src/a/1
-src/a/
-src/a-link
-src/"
-
-WVSTART "drecurse --exclude-rx (trivial)"
-WVPASSEQ "$(bup drecurse --exclude-rx '^src/b' src)" "src/c
-src/a/2
-src/a/1
-src/a/
-src/a-link
-src/"
-
-WVSTART "drecurse --exclude-rx (trivial - absolute path)"
-WVPASSEQ "$(bup drecurse --exclude-rx "^$(pwd)/src/b" "$(pwd)/src")" \
-"$(pwd)/src/c
-$(pwd)/src/a/2
-$(pwd)/src/a/1
-$(pwd)/src/a/
-$(pwd)/src/a-link
-$(pwd)/src/"
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-fsck b/test/ext/test-fsck
new file mode 100755 (executable)
index 0000000..0531814
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS "$top/dev/sync-tree" "$top/test/sampledata/" "$tmpdir/src/"
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+WVPASS bup init
+WVPASS cd "$tmpdir"
+
+WVSTART "fsck"
+
+WVPASS bup index src
+WVPASS bup save -n fsck-test src/b2
+WVPASS bup save -n fsck-test src/var/cmd
+WVPASS bup save -n fsck-test src/var/doc
+WVPASS bup save -n fsck-test src/var/lib
+WVPASS bup save -n fsck-test src/y
+WVPASS bup fsck
+WVPASS bup fsck "$BUP_DIR"/objects/pack/pack-*.pack
+WVPASS bup fsck --quick
+if bup fsck --par2-ok; then
+    WVSTART "fsck (par2)"
+else
+    WVSTART "fsck (PAR2 IS MISSING)"
+fi
+WVPASS bup fsck -g
+WVPASS bup fsck -r
+WVPASS bup damage "$BUP_DIR"/objects/pack/*.pack -n10 -s1 -S0
+WVFAIL bup fsck --quick
+WVFAIL bup fsck --quick --disable-par2
+WVPASS chmod u+w "$BUP_DIR"/objects/pack/*.idx
+WVPASS bup damage "$BUP_DIR"/objects/pack/*.idx -n10 -s1 -S0
+WVFAIL bup fsck --quick -j4
+WVPASS bup damage "$BUP_DIR"/objects/pack/*.pack -n10 -s1024 --percent 0.4 -S0
+WVFAIL bup fsck --quick
+WVFAIL bup fsck --quick -rvv -j99   # fails because repairs were needed
+if bup fsck --par2-ok; then
+    WVPASS bup fsck -r # ok because of repairs from last time
+    WVPASS bup damage "$BUP_DIR"/objects/pack/*.pack -n202 -s1 --equal -S0
+    WVFAIL bup fsck
+    WVFAIL bup fsck -rvv   # too many errors to be repairable
+    WVFAIL bup fsck -r   # too many errors to be repairable
+else
+    WVFAIL bup fsck --quick -r # still fails because par2 was missing
+fi
+
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-fsck.sh b/test/ext/test-fsck.sh
deleted file mode 100755 (executable)
index 0531814..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS "$top/dev/sync-tree" "$top/test/sampledata/" "$tmpdir/src/"
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-WVPASS bup init
-WVPASS cd "$tmpdir"
-
-WVSTART "fsck"
-
-WVPASS bup index src
-WVPASS bup save -n fsck-test src/b2
-WVPASS bup save -n fsck-test src/var/cmd
-WVPASS bup save -n fsck-test src/var/doc
-WVPASS bup save -n fsck-test src/var/lib
-WVPASS bup save -n fsck-test src/y
-WVPASS bup fsck
-WVPASS bup fsck "$BUP_DIR"/objects/pack/pack-*.pack
-WVPASS bup fsck --quick
-if bup fsck --par2-ok; then
-    WVSTART "fsck (par2)"
-else
-    WVSTART "fsck (PAR2 IS MISSING)"
-fi
-WVPASS bup fsck -g
-WVPASS bup fsck -r
-WVPASS bup damage "$BUP_DIR"/objects/pack/*.pack -n10 -s1 -S0
-WVFAIL bup fsck --quick
-WVFAIL bup fsck --quick --disable-par2
-WVPASS chmod u+w "$BUP_DIR"/objects/pack/*.idx
-WVPASS bup damage "$BUP_DIR"/objects/pack/*.idx -n10 -s1 -S0
-WVFAIL bup fsck --quick -j4
-WVPASS bup damage "$BUP_DIR"/objects/pack/*.pack -n10 -s1024 --percent 0.4 -S0
-WVFAIL bup fsck --quick
-WVFAIL bup fsck --quick -rvv -j99   # fails because repairs were needed
-if bup fsck --par2-ok; then
-    WVPASS bup fsck -r # ok because of repairs from last time
-    WVPASS bup damage "$BUP_DIR"/objects/pack/*.pack -n202 -s1 --equal -S0
-    WVFAIL bup fsck
-    WVFAIL bup fsck -rvv   # too many errors to be repairable
-    WVFAIL bup fsck -r   # too many errors to be repairable
-else
-    WVFAIL bup fsck --quick -r # still fails because par2 was missing
-fi
-
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-fuse b/test/ext/test-fuse
new file mode 100755 (executable)
index 0000000..08d7e2e
--- /dev/null
@@ -0,0 +1,106 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+unset BLOCKSIZE BLOCK_SIZE DF_BLOCK_SIZE
+
+root_status="$(dev/root-status)" || exit $?
+
+if ! bup-python -c 'import fuse' 2> /dev/null; then
+    WVSTART 'unable to import fuse; skipping test'
+    exit 0
+fi
+
+if test -n "$(type -p modprobe)" && ! modprobe fuse; then
+    echo 'Unable to load fuse module; skipping dependent tests.' 1>&2
+    exit 0
+fi
+
+if ! fusermount -V; then
+    echo 'skipping FUSE tests: fusermount does not appear to work'
+    exit 0
+fi
+
+if ! groups | grep -q fuse && test "$root_status" != root; then
+    echo 'skipping FUSE tests: you are not root and not in the fuse group'
+    exit 0
+fi
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+# Some versions of bash's printf don't support the relevant date expansion.
+savename()
+{
+    readonly secs="$1"
+    WVPASS bup-cfg-py -c "from time import strftime, localtime; \
+       print(strftime('%Y-%m-%d-%H%M%S', localtime($secs)))"
+}
+
+export TZ=UTC
+
+WVPASS bup init
+WVPASS cd "$tmpdir"
+
+savestamp1=$(WVPASS bup-cfg-py -c 'import time; print(int(time.time()))') || exit $?
+savestamp2=$(($savestamp1 + 1))
+
+savename1="$(savename "$savestamp1")" || exit $?
+savename2="$(savename "$savestamp2")" || exit $?
+
+WVPASS mkdir src
+WVPASS echo content > src/foo
+WVPASS chmod 644 src/foo
+WVPASS touch -t 201111111111 src/foo
+# FUSE, python-fuse, something, can't handle negative epoch times.
+# Use pre-epoch to make sure bup properly "bottoms out" at 0 for now.
+WVPASS echo content > src/pre-epoch
+WVPASS chmod 644 src/pre-epoch
+WVPASS touch -t 196907202018 src/pre-epoch
+WVPASS bup index src
+WVPASS bup save -n src -d "$savestamp1" --strip src
+
+WVSTART "basics"
+WVPASS mkdir mnt
+WVPASS bup fuse mnt
+
+result=$(WVPASS ls mnt) || exit $?
+WVPASSEQ src "$result"
+
+result=$(WVPASS ls mnt/src) || exit $?
+WVPASSEQ "$result" "$savename1
+latest"
+
+result=$(WVPASS ls mnt/src/latest) || exit $?
+WVPASSEQ "$result" "foo
+pre-epoch"
+
+result=$(WVPASS cat mnt/src/latest/foo) || exit $?
+WVPASSEQ "$result" "content"
+
+# Right now we don't detect new saves.
+WVPASS bup save -n src -d "$savestamp2" --strip src
+result=$(WVPASS ls mnt/src) || exit $?
+WVPASSEQ "$result" "$savename1
+latest"
+
+WVPASS fusermount -uz mnt
+
+WVSTART "extended metadata"
+WVPASS bup fuse --meta mnt
+readonly user=$(WVPASS id -un) || $?
+readonly group=$(WVPASS id -gn) || $?
+result="$(stat --format='%A %U %G %x' mnt/src/latest/foo)"
+WVPASSEQ "$result" "-rw-r--r-- $user $group 2011-11-11 11:11:00.000000000 +0000"
+result="$(stat --format='%A %U %G %x' mnt/src/latest/pre-epoch)"
+WVPASSEQ "$result" "-rw-r--r-- $user $group 1970-01-01 00:00:00.000000000 +0000"
+
+WVPASS fusermount -uz mnt
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-fuse.sh b/test/ext/test-fuse.sh
deleted file mode 100755 (executable)
index 08d7e2e..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-unset BLOCKSIZE BLOCK_SIZE DF_BLOCK_SIZE
-
-root_status="$(dev/root-status)" || exit $?
-
-if ! bup-python -c 'import fuse' 2> /dev/null; then
-    WVSTART 'unable to import fuse; skipping test'
-    exit 0
-fi
-
-if test -n "$(type -p modprobe)" && ! modprobe fuse; then
-    echo 'Unable to load fuse module; skipping dependent tests.' 1>&2
-    exit 0
-fi
-
-if ! fusermount -V; then
-    echo 'skipping FUSE tests: fusermount does not appear to work'
-    exit 0
-fi
-
-if ! groups | grep -q fuse && test "$root_status" != root; then
-    echo 'skipping FUSE tests: you are not root and not in the fuse group'
-    exit 0
-fi
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-# Some versions of bash's printf don't support the relevant date expansion.
-savename()
-{
-    readonly secs="$1"
-    WVPASS bup-cfg-py -c "from time import strftime, localtime; \
-       print(strftime('%Y-%m-%d-%H%M%S', localtime($secs)))"
-}
-
-export TZ=UTC
-
-WVPASS bup init
-WVPASS cd "$tmpdir"
-
-savestamp1=$(WVPASS bup-cfg-py -c 'import time; print(int(time.time()))') || exit $?
-savestamp2=$(($savestamp1 + 1))
-
-savename1="$(savename "$savestamp1")" || exit $?
-savename2="$(savename "$savestamp2")" || exit $?
-
-WVPASS mkdir src
-WVPASS echo content > src/foo
-WVPASS chmod 644 src/foo
-WVPASS touch -t 201111111111 src/foo
-# FUSE, python-fuse, something, can't handle negative epoch times.
-# Use pre-epoch to make sure bup properly "bottoms out" at 0 for now.
-WVPASS echo content > src/pre-epoch
-WVPASS chmod 644 src/pre-epoch
-WVPASS touch -t 196907202018 src/pre-epoch
-WVPASS bup index src
-WVPASS bup save -n src -d "$savestamp1" --strip src
-
-WVSTART "basics"
-WVPASS mkdir mnt
-WVPASS bup fuse mnt
-
-result=$(WVPASS ls mnt) || exit $?
-WVPASSEQ src "$result"
-
-result=$(WVPASS ls mnt/src) || exit $?
-WVPASSEQ "$result" "$savename1
-latest"
-
-result=$(WVPASS ls mnt/src/latest) || exit $?
-WVPASSEQ "$result" "foo
-pre-epoch"
-
-result=$(WVPASS cat mnt/src/latest/foo) || exit $?
-WVPASSEQ "$result" "content"
-
-# Right now we don't detect new saves.
-WVPASS bup save -n src -d "$savestamp2" --strip src
-result=$(WVPASS ls mnt/src) || exit $?
-WVPASSEQ "$result" "$savename1
-latest"
-
-WVPASS fusermount -uz mnt
-
-WVSTART "extended metadata"
-WVPASS bup fuse --meta mnt
-readonly user=$(WVPASS id -un) || $?
-readonly group=$(WVPASS id -gn) || $?
-result="$(stat --format='%A %U %G %x' mnt/src/latest/foo)"
-WVPASSEQ "$result" "-rw-r--r-- $user $group 2011-11-11 11:11:00.000000000 +0000"
-result="$(stat --format='%A %U %G %x' mnt/src/latest/pre-epoch)"
-WVPASSEQ "$result" "-rw-r--r-- $user $group 1970-01-01 00:00:00.000000000 +0000"
-
-WVPASS fusermount -uz mnt
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-gc b/test/ext/test-gc
new file mode 100755 (executable)
index 0000000..e29370d
--- /dev/null
@@ -0,0 +1,241 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+GC_OPTS=--unsafe
+
+bup() { "$top/bup" "$@"; }
+compare-trees() { "$top/dev/compare-trees" "$@"; }
+data-size() { "$top/dev/data-size" "$@"; }
+
+WVPASS cd "$tmpdir"
+WVPASS bup init
+
+
+WVSTART "gc (unchanged repo)"
+
+WVPASS mkdir src-1
+WVPASS bup random 1k > src-1/1
+WVPASS bup index src-1
+WVPASS bup save --strip -n src-1 src-1
+
+WVPASS bup gc $GC_OPTS -v
+
+WVPASS bup restore -C "$tmpdir/restore" /src-1/latest
+WVPASS compare-trees src-1/ "$tmpdir/restore/latest/"
+
+
+WVSTART "gc (unchanged, new branch)"
+
+WVPASS mkdir src-2
+WVPASS bup random 10M > src-2/1
+WVPASS bup index src-2
+WVPASS bup save --strip -n src-2 src-2
+
+WVPASS bup gc $GC_OPTS -v
+
+WVPASS rm -r "$tmpdir/restore"
+WVPASS bup restore -C "$tmpdir/restore" /src-1/latest
+WVPASS compare-trees src-1/ "$tmpdir/restore/latest/"
+
+WVPASS rm -r "$tmpdir/restore"
+WVPASS bup restore -C "$tmpdir/restore" /src-2/latest
+WVPASS compare-trees src-2/ "$tmpdir/restore/latest/"
+
+
+WVSTART "gc (removed branch)"
+
+size_before=$(WVPASS data-size "$BUP_DIR") || exit $?
+WVPASS rm "$BUP_DIR/refs/heads/src-2"
+WVPASS bup gc $GC_OPTS -v
+size_after=$(WVPASS data-size "$BUP_DIR") || exit $?
+
+WVPASS [ "$size_before" -gt 5000000 ]
+WVPASS [ "$size_after" -lt 50000 ]
+
+WVPASS rm -r "$tmpdir/restore"
+WVPASS bup restore -C "$tmpdir/restore" /src-1/latest
+WVPASS compare-trees src-1/ "$tmpdir/restore/latest/"
+
+WVPASS rm -r "$tmpdir/restore"
+WVFAIL bup restore -C "$tmpdir/restore" /src-2/latest
+
+WVPASS mkdir src-ab-clean src-ab-clean/a src-ab-clean/b
+WVPASS bup random 1k > src-ab-clean/a/1
+WVPASS bup random 10M > src-ab-clean/b/1
+
+
+WVSTART "gc (rewriting)"
+
+WVPASS rm -rf "$BUP_DIR"
+WVPASS bup init
+WVPASS rm -rf src-ab
+WVPASS cp -pPR src-ab-clean src-ab
+
+WVPASS bup index src-ab
+WVPASS bup save --strip -n src-ab src-ab
+WVPASS bup index --clear
+WVPASS bup index src-ab
+WVPASS bup save -vvv --strip -n a src-ab/a
+
+size_before=$(WVPASS data-size "$BUP_DIR") || exit $?
+WVPASS rm "$BUP_DIR/refs/heads/src-ab"
+WVPASS bup gc $GC_OPTS -v
+size_after=$(WVPASS data-size "$BUP_DIR") || exit $?
+
+WVPASS [ "$size_before" -gt 5000000 ]
+WVPASS [ "$size_after" -lt 100000 ]
+
+WVPASS rm -r "$tmpdir/restore"
+WVPASS bup restore -C "$tmpdir/restore" /a/latest
+WVPASS compare-trees src-ab/a/ "$tmpdir/restore/latest/"
+
+WVPASS rm -r "$tmpdir/restore"
+WVFAIL bup restore -C "$tmpdir/restore" /src-ab/latest
+
+
+WVSTART "gc (save -r after repo rewriting)"
+
+WVPASS rm -rf "$BUP_DIR"
+WVPASS bup init
+WVPASS bup -d bup-remote init
+WVPASS rm -rf src-ab
+WVPASS cp -pPR src-ab-clean src-ab
+
+WVPASS bup index src-ab
+WVPASS bup save -r :bup-remote --strip -n src-ab src-ab
+WVPASS bup index --clear
+WVPASS bup index src-ab
+WVPASS bup save -r :bup-remote -vvv --strip -n a src-ab/a
+
+size_before=$(WVPASS data-size bup-remote) || exit $?
+WVPASS rm bup-remote/refs/heads/src-ab
+WVPASS bup -d bup-remote gc $GC_OPTS -v
+size_after=$(WVPASS data-size bup-remote) || exit $?
+
+WVPASS [ "$size_before" -gt 5000000 ]
+WVPASS [ "$size_after" -lt 100000 ]
+
+WVPASS rm -rf "$tmpdir/restore"
+WVPASS bup -d bup-remote restore -C "$tmpdir/restore" /a/latest
+WVPASS compare-trees src-ab/a/ "$tmpdir/restore/latest/"
+
+WVPASS rm -r "$tmpdir/restore"
+WVFAIL bup -d bup-remote restore -C "$tmpdir/restore" /src-ab/latest
+
+# Make sure a post-gc index/save that includes gc-ed data works
+WVPASS bup index src-ab
+WVPASS bup save -r :bup-remote --strip -n src-ab src-ab
+WVPASS rm -r "$tmpdir/restore"
+WVPASS bup -d bup-remote restore -C "$tmpdir/restore" /src-ab/latest
+WVPASS compare-trees src-ab/ "$tmpdir/restore/latest/"
+
+
+WVSTART "gc (bup on after repo rewriting)"
+
+WVPASS rm -rf "$BUP_DIR"
+WVPASS bup init
+WVPASS rm -rf src-ab
+WVPASS cp -pPR src-ab-clean src-ab
+
+WVPASS bup on - index src-ab
+WVPASS bup on - save --strip -n src-ab src-ab
+WVPASS bup index --clear
+WVPASS bup on - index src-ab
+WVPASS bup on - save -vvv --strip -n a src-ab/a
+
+size_before=$(WVPASS data-size "$BUP_DIR") || exit $?
+WVPASS rm "$BUP_DIR/refs/heads/src-ab"
+WVPASS bup gc $GC_OPTS -v
+size_after=$(WVPASS data-size "$BUP_DIR") || exit $?
+
+WVPASS [ "$size_before" -gt 5000000 ]
+WVPASS [ "$size_after" -lt 100000 ]
+
+WVPASS rm -r "$tmpdir/restore"
+WVPASS bup restore -C "$tmpdir/restore" /a/latest
+WVPASS compare-trees src-ab/a/ "$tmpdir/restore/latest/"
+
+WVPASS rm -r "$tmpdir/restore"
+WVFAIL bup restore -C "$tmpdir/restore" /src-ab/latest
+
+# Make sure a post-gc index/save that includes gc-ed data works
+WVPASS bup on - index src-ab
+WVPASS bup on - save --strip -n src-ab src-ab
+WVPASS rm -r "$tmpdir/restore"
+WVPASS bup restore -C "$tmpdir/restore" /src-ab/latest
+WVPASS compare-trees src-ab/ "$tmpdir/restore/latest/"
+
+
+WVSTART "gc (threshold)"
+
+WVPASS rm -rf "$BUP_DIR"
+WVPASS bup init
+WVPASS rm -rf src && mkdir src
+WVPASS echo 0 > src/0
+WVPASS echo 1 > src/1
+
+WVPASS bup index src
+WVPASS bup save -n src-1 src
+WVPASS rm src/0
+WVPASS bup index src
+WVPASS bup save -n src-2 src
+
+WVPASS bup rm --unsafe src-1
+packs_before="$(ls "$BUP_DIR/objects/pack/"*.pack)" || exit $?
+WVPASS bup gc -v $GC_OPTS --threshold 99 2>&1 | tee gc.log
+packs_after="$(ls "$BUP_DIR/objects/pack/"*.pack)" || exit $?
+WVPASSEQ 0 "$(grep -cE '^rewriting ' gc.log)"
+WVPASSEQ "$packs_before" "$packs_after"
+
+WVPASS bup gc -v $GC_OPTS --threshold 1 2>&1 | tee gc.log
+packs_after="$(ls "$BUP_DIR/objects/pack/"*.pack)" || exit $?
+WVPASSEQ 1 "$(grep -cE '^rewriting ' gc.log)"
+
+# Check that only one pack was rewritten
+
+# Accommodate some systems that apparently used to change the default
+# ls sort order which must match LC_COLLATE for comm to work.
+packs_before="$(sort <(echo "$packs_before"))" || die $?
+packs_after="$(sort <(echo "$packs_after"))" || die $?
+
+only_in_before="$(comm -2 -3 <(echo "$packs_before") <(echo "$packs_after"))" \
+    || die $?
+
+only_in_after="$(comm -1 -3 <(echo "$packs_before") <(echo "$packs_after"))" \
+    || die $?
+
+in_both="$(comm -1 -2 <(echo "$packs_before") <(echo "$packs_after"))" || die $?
+
+WVPASSEQ 1 $(echo "$only_in_before" | wc -l)
+WVPASSEQ 1 $(echo "$only_in_after" | wc -l)
+WVPASSEQ 1 $(echo "$in_both" | wc -l)
+
+WVSTART "gc (threshold 0)"
+
+WVPASS rm -rf "$BUP_DIR"
+WVPASS bup init
+WVPASS rm -rf src && mkdir src
+WVPASS echo 0 > src/0
+WVPASS echo 1 > src/1
+
+WVPASS bup index src
+WVPASS bup save -n src-1 src
+
+packs_before="$(ls "$BUP_DIR/objects/pack/"*.pack)" || exit $?
+WVPASS bup gc -v $GC_OPTS --threshold 0 2>&1 | tee gc.log
+packs_after="$(ls "$BUP_DIR/objects/pack/"*.pack)" || exit $?
+# Check that the pack was rewritten, but not removed (since the
+# result-pack is equal to the source pack)
+WVPASSEQ 1 "$(grep -cE '^rewriting ' gc.log)"
+WVPASSEQ "$packs_before" "$packs_after"
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-gc.sh b/test/ext/test-gc.sh
deleted file mode 100755 (executable)
index e29370d..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-GC_OPTS=--unsafe
-
-bup() { "$top/bup" "$@"; }
-compare-trees() { "$top/dev/compare-trees" "$@"; }
-data-size() { "$top/dev/data-size" "$@"; }
-
-WVPASS cd "$tmpdir"
-WVPASS bup init
-
-
-WVSTART "gc (unchanged repo)"
-
-WVPASS mkdir src-1
-WVPASS bup random 1k > src-1/1
-WVPASS bup index src-1
-WVPASS bup save --strip -n src-1 src-1
-
-WVPASS bup gc $GC_OPTS -v
-
-WVPASS bup restore -C "$tmpdir/restore" /src-1/latest
-WVPASS compare-trees src-1/ "$tmpdir/restore/latest/"
-
-
-WVSTART "gc (unchanged, new branch)"
-
-WVPASS mkdir src-2
-WVPASS bup random 10M > src-2/1
-WVPASS bup index src-2
-WVPASS bup save --strip -n src-2 src-2
-
-WVPASS bup gc $GC_OPTS -v
-
-WVPASS rm -r "$tmpdir/restore"
-WVPASS bup restore -C "$tmpdir/restore" /src-1/latest
-WVPASS compare-trees src-1/ "$tmpdir/restore/latest/"
-
-WVPASS rm -r "$tmpdir/restore"
-WVPASS bup restore -C "$tmpdir/restore" /src-2/latest
-WVPASS compare-trees src-2/ "$tmpdir/restore/latest/"
-
-
-WVSTART "gc (removed branch)"
-
-size_before=$(WVPASS data-size "$BUP_DIR") || exit $?
-WVPASS rm "$BUP_DIR/refs/heads/src-2"
-WVPASS bup gc $GC_OPTS -v
-size_after=$(WVPASS data-size "$BUP_DIR") || exit $?
-
-WVPASS [ "$size_before" -gt 5000000 ]
-WVPASS [ "$size_after" -lt 50000 ]
-
-WVPASS rm -r "$tmpdir/restore"
-WVPASS bup restore -C "$tmpdir/restore" /src-1/latest
-WVPASS compare-trees src-1/ "$tmpdir/restore/latest/"
-
-WVPASS rm -r "$tmpdir/restore"
-WVFAIL bup restore -C "$tmpdir/restore" /src-2/latest
-
-WVPASS mkdir src-ab-clean src-ab-clean/a src-ab-clean/b
-WVPASS bup random 1k > src-ab-clean/a/1
-WVPASS bup random 10M > src-ab-clean/b/1
-
-
-WVSTART "gc (rewriting)"
-
-WVPASS rm -rf "$BUP_DIR"
-WVPASS bup init
-WVPASS rm -rf src-ab
-WVPASS cp -pPR src-ab-clean src-ab
-
-WVPASS bup index src-ab
-WVPASS bup save --strip -n src-ab src-ab
-WVPASS bup index --clear
-WVPASS bup index src-ab
-WVPASS bup save -vvv --strip -n a src-ab/a
-
-size_before=$(WVPASS data-size "$BUP_DIR") || exit $?
-WVPASS rm "$BUP_DIR/refs/heads/src-ab"
-WVPASS bup gc $GC_OPTS -v
-size_after=$(WVPASS data-size "$BUP_DIR") || exit $?
-
-WVPASS [ "$size_before" -gt 5000000 ]
-WVPASS [ "$size_after" -lt 100000 ]
-
-WVPASS rm -r "$tmpdir/restore"
-WVPASS bup restore -C "$tmpdir/restore" /a/latest
-WVPASS compare-trees src-ab/a/ "$tmpdir/restore/latest/"
-
-WVPASS rm -r "$tmpdir/restore"
-WVFAIL bup restore -C "$tmpdir/restore" /src-ab/latest
-
-
-WVSTART "gc (save -r after repo rewriting)"
-
-WVPASS rm -rf "$BUP_DIR"
-WVPASS bup init
-WVPASS bup -d bup-remote init
-WVPASS rm -rf src-ab
-WVPASS cp -pPR src-ab-clean src-ab
-
-WVPASS bup index src-ab
-WVPASS bup save -r :bup-remote --strip -n src-ab src-ab
-WVPASS bup index --clear
-WVPASS bup index src-ab
-WVPASS bup save -r :bup-remote -vvv --strip -n a src-ab/a
-
-size_before=$(WVPASS data-size bup-remote) || exit $?
-WVPASS rm bup-remote/refs/heads/src-ab
-WVPASS bup -d bup-remote gc $GC_OPTS -v
-size_after=$(WVPASS data-size bup-remote) || exit $?
-
-WVPASS [ "$size_before" -gt 5000000 ]
-WVPASS [ "$size_after" -lt 100000 ]
-
-WVPASS rm -rf "$tmpdir/restore"
-WVPASS bup -d bup-remote restore -C "$tmpdir/restore" /a/latest
-WVPASS compare-trees src-ab/a/ "$tmpdir/restore/latest/"
-
-WVPASS rm -r "$tmpdir/restore"
-WVFAIL bup -d bup-remote restore -C "$tmpdir/restore" /src-ab/latest
-
-# Make sure a post-gc index/save that includes gc-ed data works
-WVPASS bup index src-ab
-WVPASS bup save -r :bup-remote --strip -n src-ab src-ab
-WVPASS rm -r "$tmpdir/restore"
-WVPASS bup -d bup-remote restore -C "$tmpdir/restore" /src-ab/latest
-WVPASS compare-trees src-ab/ "$tmpdir/restore/latest/"
-
-
-WVSTART "gc (bup on after repo rewriting)"
-
-WVPASS rm -rf "$BUP_DIR"
-WVPASS bup init
-WVPASS rm -rf src-ab
-WVPASS cp -pPR src-ab-clean src-ab
-
-WVPASS bup on - index src-ab
-WVPASS bup on - save --strip -n src-ab src-ab
-WVPASS bup index --clear
-WVPASS bup on - index src-ab
-WVPASS bup on - save -vvv --strip -n a src-ab/a
-
-size_before=$(WVPASS data-size "$BUP_DIR") || exit $?
-WVPASS rm "$BUP_DIR/refs/heads/src-ab"
-WVPASS bup gc $GC_OPTS -v
-size_after=$(WVPASS data-size "$BUP_DIR") || exit $?
-
-WVPASS [ "$size_before" -gt 5000000 ]
-WVPASS [ "$size_after" -lt 100000 ]
-
-WVPASS rm -r "$tmpdir/restore"
-WVPASS bup restore -C "$tmpdir/restore" /a/latest
-WVPASS compare-trees src-ab/a/ "$tmpdir/restore/latest/"
-
-WVPASS rm -r "$tmpdir/restore"
-WVFAIL bup restore -C "$tmpdir/restore" /src-ab/latest
-
-# Make sure a post-gc index/save that includes gc-ed data works
-WVPASS bup on - index src-ab
-WVPASS bup on - save --strip -n src-ab src-ab
-WVPASS rm -r "$tmpdir/restore"
-WVPASS bup restore -C "$tmpdir/restore" /src-ab/latest
-WVPASS compare-trees src-ab/ "$tmpdir/restore/latest/"
-
-
-WVSTART "gc (threshold)"
-
-WVPASS rm -rf "$BUP_DIR"
-WVPASS bup init
-WVPASS rm -rf src && mkdir src
-WVPASS echo 0 > src/0
-WVPASS echo 1 > src/1
-
-WVPASS bup index src
-WVPASS bup save -n src-1 src
-WVPASS rm src/0
-WVPASS bup index src
-WVPASS bup save -n src-2 src
-
-WVPASS bup rm --unsafe src-1
-packs_before="$(ls "$BUP_DIR/objects/pack/"*.pack)" || exit $?
-WVPASS bup gc -v $GC_OPTS --threshold 99 2>&1 | tee gc.log
-packs_after="$(ls "$BUP_DIR/objects/pack/"*.pack)" || exit $?
-WVPASSEQ 0 "$(grep -cE '^rewriting ' gc.log)"
-WVPASSEQ "$packs_before" "$packs_after"
-
-WVPASS bup gc -v $GC_OPTS --threshold 1 2>&1 | tee gc.log
-packs_after="$(ls "$BUP_DIR/objects/pack/"*.pack)" || exit $?
-WVPASSEQ 1 "$(grep -cE '^rewriting ' gc.log)"
-
-# Check that only one pack was rewritten
-
-# Accommodate some systems that apparently used to change the default
-# ls sort order which must match LC_COLLATE for comm to work.
-packs_before="$(sort <(echo "$packs_before"))" || die $?
-packs_after="$(sort <(echo "$packs_after"))" || die $?
-
-only_in_before="$(comm -2 -3 <(echo "$packs_before") <(echo "$packs_after"))" \
-    || die $?
-
-only_in_after="$(comm -1 -3 <(echo "$packs_before") <(echo "$packs_after"))" \
-    || die $?
-
-in_both="$(comm -1 -2 <(echo "$packs_before") <(echo "$packs_after"))" || die $?
-
-WVPASSEQ 1 $(echo "$only_in_before" | wc -l)
-WVPASSEQ 1 $(echo "$only_in_after" | wc -l)
-WVPASSEQ 1 $(echo "$in_both" | wc -l)
-
-WVSTART "gc (threshold 0)"
-
-WVPASS rm -rf "$BUP_DIR"
-WVPASS bup init
-WVPASS rm -rf src && mkdir src
-WVPASS echo 0 > src/0
-WVPASS echo 1 > src/1
-
-WVPASS bup index src
-WVPASS bup save -n src-1 src
-
-packs_before="$(ls "$BUP_DIR/objects/pack/"*.pack)" || exit $?
-WVPASS bup gc -v $GC_OPTS --threshold 0 2>&1 | tee gc.log
-packs_after="$(ls "$BUP_DIR/objects/pack/"*.pack)" || exit $?
-# Check that the pack was rewritten, but not removed (since the
-# result-pack is equal to the source pack)
-WVPASSEQ 1 "$(grep -cE '^rewriting ' gc.log)"
-WVPASSEQ "$packs_before" "$packs_after"
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-import-duplicity b/test/ext/test-import-duplicity
new file mode 100755 (executable)
index 0000000..9374f9a
--- /dev/null
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+if ! [ "$(type -p duplicity)" != "" ]; then
+    # FIXME: add WVSKIP.
+    echo "Cannot find duplicity; skipping test)" 1>&2
+    exit 0
+fi
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+bup() { "$top/bup" "$@"; }
+dup() { duplicity --archive-dir "$tmpdir/dup-cache" "$@"; }
+
+WVSTART "import-duplicity"
+WVPASS "$top/dev/sync-tree" "$top/test/sampledata/" "$tmpdir/src/"
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+export PASSPHRASE=bup_duplicity_passphrase
+
+WVPASS bup init
+WVPASS cd "$tmpdir"
+WVPASS mkdir duplicity
+WVPASS dup src file://duplicity
+WVPASS bup tick
+WVPASS touch src/new-file
+WVPASS dup src file://duplicity
+WVPASS bup import-duplicity "file://duplicity" import-duplicity
+WVPASSEQ $(bup ls import-duplicity/ | wc -l) 3
+WVPASSEQ "$(bup ls import-duplicity/latest/ | sort)" "$(ls src | sort)"
+WVPASS bup restore -C restore/ import-duplicity/latest/
+WVFAIL "$top/dev/compare-trees" src/ restore/ > tmp-compare-trees
+WVPASSEQ $(cat tmp-compare-trees | wc -l) 4
+# Note: OS X rsync itemize output is currently only 9 chars, not 11.
+# FreeBSD may output 12 chars instead - accept 9-12
+# Expect something like this (without the leading spaces):
+#   .d..t...... ./
+#   .L..t...... abs-symlink -> /home/foo/bup/test/sampledata/var/abs-symlink-target
+#   .L..t...... b -> a
+#   .L..t...... c -> b
+expected_diff_rx='^\.d\.\.t\.{4,7} \./$|^\.L\.\.t\.{4,7} '
+if ! grep -qE "$expected_diff_rx" tmp-compare-trees; then
+    echo -n 'tmp-compare-trees: ' 1>&2
+    cat tmp-compare-trees 1>&2
+fi
+WVPASS grep -qE "$expected_diff_rx" tmp-compare-trees
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-import-duplicity.sh b/test/ext/test-import-duplicity.sh
deleted file mode 100755 (executable)
index 9374f9a..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-
-set -o pipefail
-
-if ! [ "$(type -p duplicity)" != "" ]; then
-    # FIXME: add WVSKIP.
-    echo "Cannot find duplicity; skipping test)" 1>&2
-    exit 0
-fi
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-bup() { "$top/bup" "$@"; }
-dup() { duplicity --archive-dir "$tmpdir/dup-cache" "$@"; }
-
-WVSTART "import-duplicity"
-WVPASS "$top/dev/sync-tree" "$top/test/sampledata/" "$tmpdir/src/"
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-export PASSPHRASE=bup_duplicity_passphrase
-
-WVPASS bup init
-WVPASS cd "$tmpdir"
-WVPASS mkdir duplicity
-WVPASS dup src file://duplicity
-WVPASS bup tick
-WVPASS touch src/new-file
-WVPASS dup src file://duplicity
-WVPASS bup import-duplicity "file://duplicity" import-duplicity
-WVPASSEQ $(bup ls import-duplicity/ | wc -l) 3
-WVPASSEQ "$(bup ls import-duplicity/latest/ | sort)" "$(ls src | sort)"
-WVPASS bup restore -C restore/ import-duplicity/latest/
-WVFAIL "$top/dev/compare-trees" src/ restore/ > tmp-compare-trees
-WVPASSEQ $(cat tmp-compare-trees | wc -l) 4
-# Note: OS X rsync itemize output is currently only 9 chars, not 11.
-# FreeBSD may output 12 chars instead - accept 9-12
-# Expect something like this (without the leading spaces):
-#   .d..t...... ./
-#   .L..t...... abs-symlink -> /home/foo/bup/test/sampledata/var/abs-symlink-target
-#   .L..t...... b -> a
-#   .L..t...... c -> b
-expected_diff_rx='^\.d\.\.t\.{4,7} \./$|^\.L\.\.t\.{4,7} '
-if ! grep -qE "$expected_diff_rx" tmp-compare-trees; then
-    echo -n 'tmp-compare-trees: ' 1>&2
-    cat tmp-compare-trees 1>&2
-fi
-WVPASS grep -qE "$expected_diff_rx" tmp-compare-trees
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-import-rdiff-backup b/test/ext/test-import-rdiff-backup
new file mode 100755 (executable)
index 0000000..359f081
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+if ! [ "$(type -p rdiff-backup)" != "" ]; then
+    # FIXME: add WVSKIP.
+    echo "Cannot find rdiff-backup; skipping test)" 1>&2
+    exit 0
+fi
+
+D=rdiff-backup.tmp
+WVSTART "import-rdiff-backup"
+WVPASS bup init
+WVPASS cd "$tmpdir"
+WVPASS mkdir rdiff-backup
+WVPASS rdiff-backup "$top/lib/cmd" rdiff-backup
+WVPASS bup tick
+WVPASS rdiff-backup "$top/Documentation" rdiff-backup
+WVPASS bup import-rdiff-backup rdiff-backup import-rdiff-backup
+WVPASSEQ $(bup ls import-rdiff-backup/ | wc -l) 3
+WVPASSEQ "$(bup ls -A import-rdiff-backup/latest/ | sort)" \
+    "$(ls -A "$top/Documentation" | sort)"
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-import-rdiff-backup.sh b/test/ext/test-import-rdiff-backup.sh
deleted file mode 100755 (executable)
index 359f081..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-if ! [ "$(type -p rdiff-backup)" != "" ]; then
-    # FIXME: add WVSKIP.
-    echo "Cannot find rdiff-backup; skipping test)" 1>&2
-    exit 0
-fi
-
-D=rdiff-backup.tmp
-WVSTART "import-rdiff-backup"
-WVPASS bup init
-WVPASS cd "$tmpdir"
-WVPASS mkdir rdiff-backup
-WVPASS rdiff-backup "$top/lib/cmd" rdiff-backup
-WVPASS bup tick
-WVPASS rdiff-backup "$top/Documentation" rdiff-backup
-WVPASS bup import-rdiff-backup rdiff-backup import-rdiff-backup
-WVPASSEQ $(bup ls import-rdiff-backup/ | wc -l) 3
-WVPASSEQ "$(bup ls -A import-rdiff-backup/latest/ | sort)" \
-    "$(ls -A "$top/Documentation" | sort)"
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-index b/test/ext/test-index
new file mode 100755 (executable)
index 0000000..6504f9c
--- /dev/null
@@ -0,0 +1,127 @@
+#!/usr/bin/env bash
+. wvtest.sh
+. wvtest-bup.sh
+. dev/lib.sh
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+export BUP_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS cd "$tmpdir"
+
+WVPASS bup init
+
+WVSTART "index"
+D=bupdata.tmp
+WVPASS force-delete $D
+WVPASS mkdir $D
+WVFAIL bup index --exclude-from $D/cannot-exist $D
+WVPASSEQ "$(bup index --check -p)" ""
+WVPASSEQ "$(bup index --check -p $D)" ""
+WVFAIL [ -e $D.fake ]
+WVFAIL bup index --check -u $D.fake
+WVPASS bup index --check -u $D
+WVPASSEQ "$(bup index --check -p $D)" "$D/"
+WVPASS touch $D/a
+WVPASS bup random 128k >$D/b
+WVPASS mkdir $D/d $D/d/e
+WVPASS bup random 512 >$D/f
+WVPASS ln -s non-existent-file $D/g
+WVPASSEQ "$(bup index -s $D/)" "A $D/"
+WVPASSEQ "$(bup index -s $D/b)" ""
+WVPASSEQ "$(bup index --check -us $D/b)" "A $D/b"
+WVPASSEQ "$(bup index --check -us $D/b $D/d)" \
+"A $D/d/e/
+A $D/d/
+A $D/b"
+WVPASS touch $D/d/z
+WVPASS bup tick
+WVPASSEQ "$(bup index --check -usx $D)" \
+"A $D/g
+A $D/f
+A $D/d/z
+A $D/d/e/
+A $D/d/
+A $D/b
+A $D/a
+A $D/"
+WVPASSEQ "$(bup index --check -us $D/a $D/b --fake-valid)" \
+"  $D/b
+  $D/a"
+WVPASSEQ "$(bup index --check -us $D/a)" "  $D/a"  # stays unmodified
+WVPASSEQ "$(bup index --check -us $D/d --fake-valid)" \
+"  $D/d/z
+  $D/d/e/
+  $D/d/"
+WVPASS touch $D/d/z
+WVPASS bup index -u $D/d/z  # becomes modified
+WVPASSEQ "$(bup index -s $D/a $D $D/b)" \
+"A $D/g
+A $D/f
+M $D/d/z
+  $D/d/e/
+M $D/d/
+  $D/b
+  $D/a
+A $D/"
+
+WVPASS bup index -u $D/d/e $D/a --fake-invalid
+WVPASSEQ "$(cd $D && bup index -m .)" \
+"./g
+./f
+./d/z
+./d/e/
+./d/
+./a
+./"
+WVPASSEQ "$(cd $D && bup index -m)" \
+"g
+f
+d/z
+d/e/
+d/
+a
+./"
+WVPASSEQ "$(cd $D && bup index -s .)" "$(cd $D && bup index -s .)"
+
+WVFAIL bup save -t $D/doesnt-exist-filename
+
+WVPASS mv "$BUP_DIR/bupindex" "$BUP_DIR/bi.old"
+WVFAIL bup save -t $D/d/e/fifotest
+WVPASS mkfifo $D/d/e/fifotest
+WVPASS bup index -u $D/d/e/fifotest
+WVPASS bup save -t $D/d/e/fifotest
+WVPASS bup save -t $D/d/e
+WVPASS rm -f $D/d/e/fifotest
+WVPASS bup index -u $D/d/e
+WVFAIL bup save -t $D/d/e/fifotest
+WVPASS mv "$BUP_DIR/bi.old" "$BUP_DIR/bupindex"
+
+WVPASS bup index -u $D/d/e
+WVPASS bup save -t $D/d/e
+WVPASSEQ "$(cd $D && bup index -m)" \
+"g
+f
+d/z
+d/
+a
+./"
+WVPASS bup save -t $D/d
+WVPASS bup index --fake-invalid $D/d/z
+WVPASS bup save -t $D/d/z
+WVPASS bup save -t $D/d/z  # test regenerating trees when no files are changed
+WVPASS bup save -t $D/d
+WVPASSEQ "$(cd $D && bup index -m)" \
+"g
+f
+a
+./"
+WVPASS bup save -r ":$BUP_DIR" -n r-test $D
+WVFAIL bup save -r ":$BUP_DIR/fake/path" -n r-test $D
+WVFAIL bup save -r ":$BUP_DIR" -n r-test $D/fake/path
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-index-check-device b/test/ext/test-index-check-device
new file mode 100755 (executable)
index 0000000..1326df1
--- /dev/null
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. ./dev/lib.sh || exit $?
+
+set -o pipefail
+
+root_status="$(dev/root-status)" || exit $?
+
+if [ "$root_status" != root ]; then
+    echo 'Not root: skipping --check-device tests.'
+    exit 0 # FIXME: add WVSKIP.
+fi
+
+if test -n "$(type -p modprobe)" && ! modprobe loop; then
+    echo 'Unable to load loopback module; skipping --check-device test.' 1>&2
+    exit 0
+fi
+
+if test -z "$(type -p losetup)"; then
+    echo 'Unable to find losetup: skipping --check-device tests.' 1>&2
+    exit 0 # FIXME: add WVSKIP.
+fi
+
+if test -z "$(type -p mke2fs)"; then
+    echo 'Unable to find mke2fs: skipping --check-device tests.' 1>&2
+    exit 0 # FIXME: add WVSKIP.
+fi
+
+WVSTART '--check-device'
+
+top="$(pwd)"
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+srcmnt="$(WVPASS wvmkmountpt)" || exit $?
+tmpmnt1="$(WVPASS wvmkmountpt)" || exit $?
+tmpmnt2="$(WVPASS wvmkmountpt)" || exit $?
+
+WVPASS cd "$tmpdir"
+
+WVPASS dd if=/dev/zero of=testfs.img bs=1M count=32
+WVPASS mke2fs -F -j -m 0 testfs.img
+WVPASS mount -o loop testfs.img "$tmpmnt1"
+# Hide, so that tests can't create risks.
+WVPASS chown root:root "$tmpmnt1"
+WVPASS chmod 0700 "$tmpmnt1"
+
+# Create trivial content.
+WVPASS date > "$tmpmnt1/foo"
+WVPASS umount "$tmpmnt1"
+
+# Mount twice, so we'll have the same content with different devices.
+WVPASS cp -pP testfs.img testfs2.img
+WVPASS mount -oro,loop testfs.img "$tmpmnt1"
+WVPASS mount -oro,loop testfs2.img "$tmpmnt2"
+
+# Test default behavior: --check-device.
+WVPASS mount -oro --bind "$tmpmnt1" "$srcmnt"
+WVPASS bup init
+WVPASS bup index --fake-valid "$srcmnt"
+WVPASS umount "$srcmnt"
+WVPASS mount -oro --bind "$tmpmnt2" "$srcmnt"
+WVPASS bup index "$srcmnt"
+WVPASSEQ "$(bup index --status "$srcmnt")" \
+"M $srcmnt/lost+found/
+M $srcmnt/foo
+M $srcmnt/"
+WVPASS umount "$srcmnt"
+
+WVSTART '--no-check-device'
+WVPASS mount -oro --bind "$tmpmnt1" "$srcmnt"
+WVPASS bup index --clear
+WVPASS bup index --fake-valid "$srcmnt"
+WVPASS umount "$srcmnt"
+WVPASS mount -oro --bind "$tmpmnt2" "$srcmnt"
+WVPASS bup index --no-check-device "$srcmnt"
+WVPASS bup index --status "$srcmnt"
+WVPASSEQ "$(bup index --status "$srcmnt")" \
+"  $srcmnt/lost+found/
+  $srcmnt/foo
+  $srcmnt/"
+
+WVPASS umount "$srcmnt"
+WVPASS umount "$tmpmnt1"
+WVPASS umount "$tmpmnt2"
+WVPASS rm -r "$tmpmnt1" "$tmpmnt2" "$tmpdir"
diff --git a/test/ext/test-index-check-device.sh b/test/ext/test-index-check-device.sh
deleted file mode 100755 (executable)
index 1326df1..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. ./dev/lib.sh || exit $?
-
-set -o pipefail
-
-root_status="$(dev/root-status)" || exit $?
-
-if [ "$root_status" != root ]; then
-    echo 'Not root: skipping --check-device tests.'
-    exit 0 # FIXME: add WVSKIP.
-fi
-
-if test -n "$(type -p modprobe)" && ! modprobe loop; then
-    echo 'Unable to load loopback module; skipping --check-device test.' 1>&2
-    exit 0
-fi
-
-if test -z "$(type -p losetup)"; then
-    echo 'Unable to find losetup: skipping --check-device tests.' 1>&2
-    exit 0 # FIXME: add WVSKIP.
-fi
-
-if test -z "$(type -p mke2fs)"; then
-    echo 'Unable to find mke2fs: skipping --check-device tests.' 1>&2
-    exit 0 # FIXME: add WVSKIP.
-fi
-
-WVSTART '--check-device'
-
-top="$(pwd)"
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-srcmnt="$(WVPASS wvmkmountpt)" || exit $?
-tmpmnt1="$(WVPASS wvmkmountpt)" || exit $?
-tmpmnt2="$(WVPASS wvmkmountpt)" || exit $?
-
-WVPASS cd "$tmpdir"
-
-WVPASS dd if=/dev/zero of=testfs.img bs=1M count=32
-WVPASS mke2fs -F -j -m 0 testfs.img
-WVPASS mount -o loop testfs.img "$tmpmnt1"
-# Hide, so that tests can't create risks.
-WVPASS chown root:root "$tmpmnt1"
-WVPASS chmod 0700 "$tmpmnt1"
-
-# Create trivial content.
-WVPASS date > "$tmpmnt1/foo"
-WVPASS umount "$tmpmnt1"
-
-# Mount twice, so we'll have the same content with different devices.
-WVPASS cp -pP testfs.img testfs2.img
-WVPASS mount -oro,loop testfs.img "$tmpmnt1"
-WVPASS mount -oro,loop testfs2.img "$tmpmnt2"
-
-# Test default behavior: --check-device.
-WVPASS mount -oro --bind "$tmpmnt1" "$srcmnt"
-WVPASS bup init
-WVPASS bup index --fake-valid "$srcmnt"
-WVPASS umount "$srcmnt"
-WVPASS mount -oro --bind "$tmpmnt2" "$srcmnt"
-WVPASS bup index "$srcmnt"
-WVPASSEQ "$(bup index --status "$srcmnt")" \
-"M $srcmnt/lost+found/
-M $srcmnt/foo
-M $srcmnt/"
-WVPASS umount "$srcmnt"
-
-WVSTART '--no-check-device'
-WVPASS mount -oro --bind "$tmpmnt1" "$srcmnt"
-WVPASS bup index --clear
-WVPASS bup index --fake-valid "$srcmnt"
-WVPASS umount "$srcmnt"
-WVPASS mount -oro --bind "$tmpmnt2" "$srcmnt"
-WVPASS bup index --no-check-device "$srcmnt"
-WVPASS bup index --status "$srcmnt"
-WVPASSEQ "$(bup index --status "$srcmnt")" \
-"  $srcmnt/lost+found/
-  $srcmnt/foo
-  $srcmnt/"
-
-WVPASS umount "$srcmnt"
-WVPASS umount "$tmpmnt1"
-WVPASS umount "$tmpmnt2"
-WVPASS rm -r "$tmpmnt1" "$tmpmnt2" "$tmpdir"
diff --git a/test/ext/test-index-clear b/test/ext/test-index-clear
new file mode 100755 (executable)
index 0000000..05cd848
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+
+WVPASS bup init
+WVPASS cd "$tmpdir"
+
+
+WVSTART "index --clear"
+WVPASS mkdir src
+WVPASS touch src/foo src/bar
+WVPASS bup index -u src
+WVPASSEQ "$(bup index -p)" "src/foo
+src/bar
+src/
+./"
+WVPASS rm src/foo
+WVPASS bup index --clear
+WVPASS bup index -u src
+expected="$(WVPASS bup index -p)" || exit $?
+WVPASSEQ "$expected" "src/bar
+src/
+./"
+
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-index-clear.sh b/test/ext/test-index-clear.sh
deleted file mode 100755 (executable)
index 05cd848..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-
-WVPASS bup init
-WVPASS cd "$tmpdir"
-
-
-WVSTART "index --clear"
-WVPASS mkdir src
-WVPASS touch src/foo src/bar
-WVPASS bup index -u src
-WVPASSEQ "$(bup index -p)" "src/foo
-src/bar
-src/
-./"
-WVPASS rm src/foo
-WVPASS bup index --clear
-WVPASS bup index -u src
-expected="$(WVPASS bup index -p)" || exit $?
-WVPASSEQ "$expected" "src/bar
-src/
-./"
-
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-index.sh b/test/ext/test-index.sh
deleted file mode 100755 (executable)
index 6504f9c..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-#!/usr/bin/env bash
-. wvtest.sh
-. wvtest-bup.sh
-. dev/lib.sh
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-export BUP_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS cd "$tmpdir"
-
-WVPASS bup init
-
-WVSTART "index"
-D=bupdata.tmp
-WVPASS force-delete $D
-WVPASS mkdir $D
-WVFAIL bup index --exclude-from $D/cannot-exist $D
-WVPASSEQ "$(bup index --check -p)" ""
-WVPASSEQ "$(bup index --check -p $D)" ""
-WVFAIL [ -e $D.fake ]
-WVFAIL bup index --check -u $D.fake
-WVPASS bup index --check -u $D
-WVPASSEQ "$(bup index --check -p $D)" "$D/"
-WVPASS touch $D/a
-WVPASS bup random 128k >$D/b
-WVPASS mkdir $D/d $D/d/e
-WVPASS bup random 512 >$D/f
-WVPASS ln -s non-existent-file $D/g
-WVPASSEQ "$(bup index -s $D/)" "A $D/"
-WVPASSEQ "$(bup index -s $D/b)" ""
-WVPASSEQ "$(bup index --check -us $D/b)" "A $D/b"
-WVPASSEQ "$(bup index --check -us $D/b $D/d)" \
-"A $D/d/e/
-A $D/d/
-A $D/b"
-WVPASS touch $D/d/z
-WVPASS bup tick
-WVPASSEQ "$(bup index --check -usx $D)" \
-"A $D/g
-A $D/f
-A $D/d/z
-A $D/d/e/
-A $D/d/
-A $D/b
-A $D/a
-A $D/"
-WVPASSEQ "$(bup index --check -us $D/a $D/b --fake-valid)" \
-"  $D/b
-  $D/a"
-WVPASSEQ "$(bup index --check -us $D/a)" "  $D/a"  # stays unmodified
-WVPASSEQ "$(bup index --check -us $D/d --fake-valid)" \
-"  $D/d/z
-  $D/d/e/
-  $D/d/"
-WVPASS touch $D/d/z
-WVPASS bup index -u $D/d/z  # becomes modified
-WVPASSEQ "$(bup index -s $D/a $D $D/b)" \
-"A $D/g
-A $D/f
-M $D/d/z
-  $D/d/e/
-M $D/d/
-  $D/b
-  $D/a
-A $D/"
-
-WVPASS bup index -u $D/d/e $D/a --fake-invalid
-WVPASSEQ "$(cd $D && bup index -m .)" \
-"./g
-./f
-./d/z
-./d/e/
-./d/
-./a
-./"
-WVPASSEQ "$(cd $D && bup index -m)" \
-"g
-f
-d/z
-d/e/
-d/
-a
-./"
-WVPASSEQ "$(cd $D && bup index -s .)" "$(cd $D && bup index -s .)"
-
-WVFAIL bup save -t $D/doesnt-exist-filename
-
-WVPASS mv "$BUP_DIR/bupindex" "$BUP_DIR/bi.old"
-WVFAIL bup save -t $D/d/e/fifotest
-WVPASS mkfifo $D/d/e/fifotest
-WVPASS bup index -u $D/d/e/fifotest
-WVPASS bup save -t $D/d/e/fifotest
-WVPASS bup save -t $D/d/e
-WVPASS rm -f $D/d/e/fifotest
-WVPASS bup index -u $D/d/e
-WVFAIL bup save -t $D/d/e/fifotest
-WVPASS mv "$BUP_DIR/bi.old" "$BUP_DIR/bupindex"
-
-WVPASS bup index -u $D/d/e
-WVPASS bup save -t $D/d/e
-WVPASSEQ "$(cd $D && bup index -m)" \
-"g
-f
-d/z
-d/
-a
-./"
-WVPASS bup save -t $D/d
-WVPASS bup index --fake-invalid $D/d/z
-WVPASS bup save -t $D/d/z
-WVPASS bup save -t $D/d/z  # test regenerating trees when no files are changed
-WVPASS bup save -t $D/d
-WVPASSEQ "$(cd $D && bup index -m)" \
-"g
-f
-a
-./"
-WVPASS bup save -r ":$BUP_DIR" -n r-test $D
-WVFAIL bup save -r ":$BUP_DIR/fake/path" -n r-test $D
-WVFAIL bup save -r ":$BUP_DIR" -n r-test $D/fake/path
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-list-idx b/test/ext/test-list-idx
new file mode 100755 (executable)
index 0000000..ad72ab4
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+. wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+TOP="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+export BUP_DIR="$tmpdir/bup"
+
+bup()
+{
+    "$TOP/bup" "$@"
+}
+
+WVSTART 'bup list-idx'
+
+WVPASS bup init
+WVPASS cd "$tmpdir"
+WVPASS mkdir src
+WVPASS bup random 1k > src/data
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS bup list-idx "$BUP_DIR"/objects/pack/*.idx
+hash1="$(WVPASS bup list-idx "$BUP_DIR"/objects/pack/*.idx)" || exit $?
+hash1="${hash1##* }"
+WVPASS bup list-idx --find "${hash1}" "$BUP_DIR"/objects/pack/*.idx \
+       > list-idx.log || exit $?
+found="$(cat list-idx.log)" || exit $?
+found="${found##* }"
+WVPASSEQ "$found" "$hash1"
+WVPASSEQ "$(wc -l < list-idx.log | tr -d ' ')" 1
+
+WVPASS rm -r "$tmpdir"
diff --git a/test/ext/test-list-idx.sh b/test/ext/test-list-idx.sh
deleted file mode 100755 (executable)
index ad72ab4..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env bash
-. wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-TOP="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-export BUP_DIR="$tmpdir/bup"
-
-bup()
-{
-    "$TOP/bup" "$@"
-}
-
-WVSTART 'bup list-idx'
-
-WVPASS bup init
-WVPASS cd "$tmpdir"
-WVPASS mkdir src
-WVPASS bup random 1k > src/data
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS bup list-idx "$BUP_DIR"/objects/pack/*.idx
-hash1="$(WVPASS bup list-idx "$BUP_DIR"/objects/pack/*.idx)" || exit $?
-hash1="${hash1##* }"
-WVPASS bup list-idx --find "${hash1}" "$BUP_DIR"/objects/pack/*.idx \
-       > list-idx.log || exit $?
-found="$(cat list-idx.log)" || exit $?
-found="${found##* }"
-WVPASSEQ "$found" "$hash1"
-WVPASSEQ "$(wc -l < list-idx.log | tr -d ' ')" 1
-
-WVPASS rm -r "$tmpdir"
diff --git a/test/ext/test-main b/test/ext/test-main
new file mode 100755 (executable)
index 0000000..60700ec
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+. wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+TOP="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+export BUP_DIR="$tmpdir/bup"
+
+bup()
+{
+    "$TOP/bup" "$@"
+}
+
+WVSTART 'main'
+
+bup
+rc=$?
+WVPASSEQ "$rc" 99
+
+WVPASS rm -r "$tmpdir"
diff --git a/test/ext/test-main.sh b/test/ext/test-main.sh
deleted file mode 100755 (executable)
index 60700ec..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-. wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-TOP="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-export BUP_DIR="$tmpdir/bup"
-
-bup()
-{
-    "$TOP/bup" "$@"
-}
-
-WVSTART 'main'
-
-bup
-rc=$?
-WVPASSEQ "$rc" 99
-
-WVPASS rm -r "$tmpdir"
diff --git a/test/ext/test-meta b/test/ext/test-meta
new file mode 100755 (executable)
index 0000000..0f8bb60
--- /dev/null
@@ -0,0 +1,783 @@
+#!/usr/bin/env bash
+. wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+root_status="$(dev/root-status)" || exit $?
+
+TOP="$(WVPASS pwd)" || exit $?
+export PATH="$TOP/test/bin:$PATH"
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+export BUP_DIR="$tmpdir/bup"
+
+# Assume that mvmktempdir will always use the same dir.
+timestamp_resolutions="$(dev/ns-timestamp-resolutions "$tmpdir/canary")" \
+    || exit $?
+atime_resolution="$(echo $timestamp_resolutions | WVPASS cut -d' ' -f 1)" \
+    || exit $?
+mtime_resolution="$(echo $timestamp_resolutions | WVPASS cut -d' ' -f 2)" \
+    || exit $?
+WVPASS rm "$tmpdir/canary"
+
+bup()
+{
+    "$TOP/bup" "$@"
+}
+
+hardlink-sets()
+{
+    "$TOP/dev/hardlink-sets" "$@"
+}
+
+id-other-than()
+{
+    "$TOP/dev/id-other-than" "$@"
+}
+
+# Very simple metadata tests -- create a test tree then check that bup
+# meta can reproduce the metadata correctly (according to bup xstat)
+# via create, extract, start-extract, and finish-extract.  The current
+# tests are crude, and this does not fully test devices, varying
+# users/groups, acls, attrs, etc.
+
+genstat()
+{
+    (
+        export PATH="$TOP/bin:$PATH" # pick up bup
+        bup version
+        # Skip atime (test elsewhere) to avoid the observer effect.
+        WVPASS find . -print0 | WVPASS sort-z \
+            | WVPASS xargs -0 bup xstat \
+            --mtime-resolution "$mtime_resolution"ns \
+            --exclude-fields ctime,atime,size
+    )
+}
+
+test-src-create-extract()
+{
+    # Test bup meta create/extract for ./src -> ./src-restore.
+    # Also writes to ./src-stat and ./src-restore-stat.
+    (
+        (WVPASS cd src; WVPASS genstat) > src-stat || exit $?
+        WVPASS bup meta --create --recurse --file src.meta src
+        # Test extract.
+        WVPASS force-delete src-restore
+        WVPASS mkdir src-restore
+        WVPASS cd src-restore
+        WVPASS bup meta --extract --file ../src.meta
+        WVPASS test -d src
+        (WVPASS cd src; WVPASS genstat >../../src-restore-stat) || exit $?
+        WVPASS diff -U5 ../src-stat ../src-restore-stat
+        # Test start/finish extract.
+        WVPASS force-delete src
+        WVPASS bup meta --start-extract --file ../src.meta
+        WVPASS test -d src
+        WVPASS bup meta --finish-extract --file ../src.meta
+        (WVPASS cd src; WVPASS genstat >../../src-restore-stat) || exit $?
+        WVPASS diff -U5 ../src-stat ../src-restore-stat
+    )
+}
+
+test-src-save-restore()
+{
+    # Test bup save/restore metadata for ./src -> ./src-restore.  Also
+    # writes to BUP_DIR.  Note that for now this just tests the
+    # restore below src/, in order to avoid having to worry about
+    # operations that require root (like chown /home).
+    (
+        WVPASS rm -rf "$BUP_DIR"
+        WVPASS bup init
+        WVPASS bup index src
+        WVPASS bup save -t -n src src
+        # Test extract.
+        WVPASS force-delete src-restore
+        WVPASS mkdir src-restore
+        WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
+        WVPASS test -d src-restore/src
+        WVPASS "$TOP/dev/compare-trees" -c src/ src-restore/src/
+        WVPASS rm -rf src.bup
+    )
+}
+
+setup-test-tree()
+{
+    WVPASS "$TOP/dev/sync-tree" "$TOP/test/sampledata/" "$tmpdir/src/"
+
+    # Add some hard links for the general tests.
+    (
+        WVPASS cd "$tmpdir"/src
+        WVPASS touch hardlink-target
+        WVPASS ln hardlink-target hardlink-1
+        WVPASS ln hardlink-target hardlink-2
+        WVPASS ln hardlink-target hardlink-3
+    ) || exit $?
+
+    # Add some trivial files for the index, modify, save tests.
+    (
+        WVPASS cd "$tmpdir"/src
+        WVPASS mkdir volatile
+        WVPASS touch volatile/{1,2,3}
+    ) || exit $?
+
+    # Regression test for metadata sort order.  Previously, these two
+    # entries would sort in the wrong order because the metadata
+    # entries were being sorted by mangled name, but the index isn't.
+    WVPASS dd if=/dev/zero of="$tmpdir"/src/foo bs=1k count=33
+    WVPASS touch -t 201111111111 "$tmpdir"/src/foo
+    WVPASS touch -t 201112121111 "$tmpdir"/src/foo-bar
+
+    dev/mksock "$tmpdir"/src/test-socket || true
+}
+
+# Use the test tree to check bup meta.
+WVSTART 'meta --create/--extract'
+(
+    tmpdir="$(WVPASS wvmktempdir)" || exit $?
+    export BUP_DIR="$tmpdir/bup"
+    WVPASS setup-test-tree
+    WVPASS cd "$tmpdir"
+    WVPASS test-src-create-extract
+
+    # Test a top-level file (not dir).
+    WVPASS touch src-file
+    WVPASS bup meta -cf src-file.meta src-file
+    WVPASS mkdir dest
+    WVPASS cd dest
+    WVPASS bup meta -xf ../src-file.meta
+    WVPASS rm -r "$tmpdir"
+) || exit $?
+
+# Use the test tree to check bup save/restore metadata.
+WVSTART 'metadata save/restore (general)'
+(
+    tmpdir="$(WVPASS wvmktempdir)" || exit $?
+    export BUP_DIR="$tmpdir/bup"
+    WVPASS setup-test-tree
+    WVPASS cd "$tmpdir"
+    WVPASS test-src-save-restore
+
+    # Test a deeper subdir/ to make sure top-level non-dir metadata is
+    # restored correctly.  We need at least one dir and one non-dir at
+    # the "top-level".
+    WVPASS test -d src/var/cmd
+    WVPASS test -f src/var/cmd/save-cmd.py
+    WVPASS rm -rf "$BUP_DIR"
+    WVPASS bup init
+    WVPASS touch -t 201111111111 src-restore # Make sure the top won't match.
+    WVPASS bup index src
+    WVPASS bup save -t -n src src
+    WVPASS force-delete src-restore
+    WVPASS bup restore -C src-restore "/src/latest$(pwd)/src/var/."
+    WVPASS touch -t 201211111111 src-restore # Make sure the top won't match.
+    # Check that the only difference is the top dir.
+    WVFAIL $TOP/dev/compare-trees -c src/var/ src-restore/ > tmp-compare-trees
+    WVPASSEQ $(cat tmp-compare-trees | wc -l) 1
+    # The number of rsync status characters varies, so accept any
+    # number of trailing dots.  For example OS X native rsync produces
+    # 9, but Homebrew's produces 12, while on other platforms, 11 is
+    # common.
+    expected_diff_rx='^\.d\.\.t\.\.\.(\.)+ \./$'
+    if ! grep -qE "$expected_diff_rx" tmp-compare-trees; then
+        echo -n 'tmp-compare-trees: ' 1>&2
+        cat tmp-compare-trees 1>&2
+    fi
+    WVPASS grep -qE "$expected_diff_rx" tmp-compare-trees
+    WVPASS rm -r "$tmpdir"
+) || exit $?
+
+# Test that we pull the index (not filesystem) metadata for any
+# unchanged files whenever we're saving other files in a given
+# directory.
+WVSTART 'metadata save/restore (using index metadata)'
+(
+    tmpdir="$(WVPASS wvmktempdir)" || exit $?
+    export BUP_DIR="$tmpdir/bup"
+    WVPASS setup-test-tree
+    WVPASS cd "$tmpdir"
+
+    # ...for now -- might be a problem with hardlink restores that was
+    # causing noise wrt this test.
+    WVPASS rm -rf src/hardlink*
+
+    # Pause here to keep the filesystem changes far enough away from
+    # the first index run that bup won't cap their index timestamps
+    # (see "bup help index" for more information).  Without this
+    # sleep, the compare-trees test below "Bup should *not* pick up
+    # these metadata..." may fail.
+    WVPASS sleep 1
+
+    WVPASS rm -rf "$BUP_DIR"
+    WVPASS bup init
+    WVPASS bup index src
+    WVPASS bup save -t -n src src
+
+    WVPASS force-delete src-restore-1
+    WVPASS mkdir src-restore-1
+    WVPASS bup restore -C src-restore-1 "/src/latest$(pwd)/"
+    WVPASS test -d src-restore-1/src
+    WVPASS "$TOP/dev/compare-trees" -c src/ src-restore-1/src/
+
+    WVPASS echo "blarg" > src/volatile/1
+    WVPASS cp -pP src/volatile/1 src-restore-1/src/volatile/
+    WVPASS bup index src
+
+    # Bup should *not* pick up these metadata changes.
+    WVPASS touch src/volatile/2
+
+    WVPASS bup save -t -n src src
+
+    WVPASS force-delete src-restore-2
+    WVPASS mkdir src-restore-2
+    WVPASS bup restore -C src-restore-2 "/src/latest$(pwd)/"
+    WVPASS test -d src-restore-2/src
+    WVPASS "$TOP/dev/compare-trees" -c src-restore-1/src/ src-restore-2/src/
+
+    WVPASS rm -r "$tmpdir"
+
+) || exit $?
+
+
+setup-hardlink-test()
+{
+    WVPASS rm -rf "$tmpdir/src" "$BUP_DIR"
+    WVPASS bup init
+    WVPASS mkdir "$tmpdir/src"
+}
+
+hardlink-test-run-restore()
+{
+    WVPASS force-delete src-restore
+    WVPASS mkdir src-restore
+    WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
+    WVPASS test -d src-restore/src
+}
+
+# Test hardlinks more carefully.
+WVSTART 'metadata save/restore (hardlinks)'
+(
+    tmpdir="$(WVPASS wvmktempdir)" || exit $?    
+    export BUP_DIR="$tmpdir/bup"
+
+    WVPASS setup-hardlink-test
+    WVPASS cd "$tmpdir"
+    
+    # Test trivial case - single hardlink.
+    (
+        WVPASS cd src
+        WVPASS touch hardlink-target
+        WVPASS ln hardlink-target hardlink-1
+    ) || exit $?
+    WVPASS bup index src
+    WVPASS bup save -t -n src src
+    WVPASS hardlink-test-run-restore
+    WVPASS "$TOP/dev/compare-trees" -c src/ src-restore/src/
+
+    # Test the case where the hardlink hasn't changed, but the tree
+    # needs to be saved again. i.e. the save-cmd.py "if hashvalid:"
+    # case.
+    (
+        WVPASS cd src
+        WVPASS echo whatever > something-new
+    ) || exit $?
+    WVPASS bup index src
+    WVPASS bup save -t -n src src
+    WVPASS hardlink-test-run-restore
+    WVPASS "$TOP/dev/compare-trees" -c src/ src-restore/src/
+
+    # Test hardlink changes between index runs.
+    #
+    WVPASS setup-hardlink-test
+    WVPASS cd src
+    WVPASS touch hardlink-target-a
+    WVPASS touch hardlink-target-b
+    WVPASS ln hardlink-target-a hardlink-b-1
+    WVPASS ln hardlink-target-a hardlink-a-1
+    WVPASS cd ..
+    WVPASS bup index -vv src
+    WVPASS rm src/hardlink-b-1
+    WVPASS ln src/hardlink-target-b src/hardlink-b-1
+    WVPASS bup index -vv src
+    WVPASS bup save -t -n src src
+    WVPASS hardlink-test-run-restore
+    WVPASS echo ./src/hardlink-a-1 > hardlink-sets.expected
+    WVPASS echo ./src/hardlink-target-a >> hardlink-sets.expected
+    WVPASS echo >> hardlink-sets.expected
+    WVPASS echo ./src/hardlink-b-1 >> hardlink-sets.expected
+    WVPASS echo ./src/hardlink-target-b >> hardlink-sets.expected
+    (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
+        || exit $?
+    WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
+
+    # Test hardlink changes between index and save -- hardlink set [a
+    # b c d] changes to [a b] [c d].  At least right now bup should
+    # notice and recreate the latter.
+    WVPASS setup-hardlink-test
+    WVPASS cd "$tmpdir"/src
+    WVPASS touch a
+    WVPASS ln a b
+    WVPASS ln a c
+    WVPASS ln a d
+    WVPASS cd ..
+    WVPASS bup index -vv src
+    WVPASS rm src/c src/d
+    WVPASS touch src/c
+    WVPASS ln src/c src/d
+    WVPASS bup save -t -n src src
+    WVPASS hardlink-test-run-restore
+    WVPASS echo ./src/a > hardlink-sets.expected
+    WVPASS echo ./src/b >> hardlink-sets.expected
+    WVPASS echo >> hardlink-sets.expected
+    WVPASS echo ./src/c >> hardlink-sets.expected
+    WVPASS echo ./src/d >> hardlink-sets.expected
+    (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
+        || exit $?
+    WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
+
+    # Test that we don't link outside restore tree.
+    WVPASS setup-hardlink-test
+    WVPASS cd "$tmpdir"
+    WVPASS mkdir src/a src/b
+    WVPASS touch src/a/1
+    WVPASS ln src/a/1 src/b/1
+    WVPASS bup index -vv src
+    WVPASS bup save -t -n src src
+    WVPASS force-delete src-restore
+    WVPASS mkdir src-restore
+    WVPASS bup restore -C src-restore "/src/latest$(pwd)/src/a/"
+    WVPASS test -e src-restore/1
+    WVPASS echo -n > hardlink-sets.expected
+    (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
+        || exit $?
+    WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
+
+    # Test that we do link within separate sub-trees.
+    WVPASS setup-hardlink-test
+    WVPASS cd "$tmpdir"
+    WVPASS mkdir src/a src/b
+    WVPASS touch src/a/1
+    WVPASS ln src/a/1 src/b/1
+    WVPASS bup index -vv src/a src/b
+    WVPASS bup save -t -n src src/a src/b
+    WVPASS hardlink-test-run-restore
+    WVPASS echo ./src/a/1 > hardlink-sets.expected
+    WVPASS echo ./src/b/1 >> hardlink-sets.expected
+    (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
+        || exit $?
+    WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
+
+    WVPASS rm -r "$tmpdir"
+
+) || exit $?
+
+WVSTART 'meta --edit'
+(
+    tmpdir="$(WVPASS wvmktempdir)" || exit $?    
+    WVPASS cd "$tmpdir"
+    WVPASS mkdir src
+
+    WVPASS bup meta -cf src.meta src
+
+    WVPASS bup meta --edit --set-uid 0 src.meta | WVPASS bup meta -tvvf - \
+        | WVPASS grep -qE '^uid: 0'
+    WVPASS bup meta --edit --set-uid 1000 src.meta | WVPASS bup meta -tvvf - \
+        | WVPASS grep -qE '^uid: 1000'
+
+    WVPASS bup meta --edit --set-gid 0 src.meta | WVPASS bup meta -tvvf - \
+        | WVPASS grep -qE '^gid: 0'
+    WVPASS bup meta --edit --set-gid 1000 src.meta | WVPASS bup meta -tvvf - \
+        | WVPASS grep -qE '^gid: 1000'
+
+    WVPASS bup meta --edit --set-user foo src.meta | WVPASS bup meta -tvvf - \
+        | WVPASS grep -qE '^user: foo'
+    WVPASS bup meta --edit --set-user bar src.meta | WVPASS bup meta -tvvf - \
+        | WVPASS grep -qE '^user: bar'
+    WVPASS bup meta --edit --unset-user src.meta | WVPASS bup meta -tvvf - \
+        | WVPASS grep -qE '^user:'
+    WVPASS bup meta --edit --set-user bar --unset-user src.meta \
+        | WVPASS bup meta -tvvf - | WVPASS grep -qE '^user:'
+    WVPASS bup meta --edit --unset-user --set-user bar src.meta \
+        | WVPASS bup meta -tvvf - | WVPASS grep -qE '^user: bar'
+
+    WVPASS bup meta --edit --set-group foo src.meta | WVPASS bup meta -tvvf - \
+        | WVPASS grep -qE '^group: foo'
+    WVPASS bup meta --edit --set-group bar src.meta | WVPASS bup meta -tvvf - \
+        | WVPASS grep -qE '^group: bar'
+    WVPASS bup meta --edit --unset-group src.meta | WVPASS bup meta -tvvf - \
+        | WVPASS grep -qE '^group:'
+    WVPASS bup meta --edit --set-group bar --unset-group src.meta \
+        | WVPASS bup meta -tvvf - | WVPASS grep -qE '^group:'
+    WVPASS bup meta --edit --unset-group --set-group bar src.meta \
+        | WVPASS bup meta -tvvf - | grep -qE '^group: bar'
+
+    WVPASS rm -r "$tmpdir"
+
+) || exit $?
+
+WVSTART 'meta --no-recurse'
+(
+    tmpdir="$(WVPASS wvmktempdir)" || exit $?    
+    WVPASS cd "$tmpdir"
+    WVPASS mkdir src
+    WVPASS mkdir src/foo
+    WVPASS touch src/foo/{1,2,3}
+    WVPASS bup meta -cf src.meta src
+    WVPASSEQ "$(bup meta -tf src.meta | LC_ALL=C sort)" "src/
+src/foo/
+src/foo/1
+src/foo/2
+src/foo/3"
+    WVPASS bup meta --no-recurse -cf src.meta src
+    WVPASSEQ "$(bup meta -tf src.meta | LC_ALL=C sort)" "src/"
+    WVPASS rm -r "$tmpdir"
+) || exit $?
+
+# Test ownership restoration (when not root or fakeroot).
+(
+    if [ "$root_status" != none ]; then
+        exit 0
+    fi
+
+    tmpdir="$(WVPASS wvmktempdir)" || exit $?    
+
+    # FIXME: binary groups
+    first_group="$(WVPASS bup-cfg-py -c 'import os,grp; \
+      print(grp.getgrgid(os.getgroups()[0])[0])')" || exit $?
+    last_group="$(bup-cfg-py -c 'import os,grp; \
+      print(grp.getgrgid(os.getgroups()[-1])[0])')" || exit $?
+    last_group_erx="$(escape-erx "$last_group")"
+
+    WVSTART 'metadata (restoration of ownership)'
+    WVPASS cd "$tmpdir"
+    WVPASS touch src
+    # Some systems always assign the parent dir group to new paths
+    # (sgid).  Make sure the group is one we're in.
+    WVPASS chgrp -R "$first_group" src
+
+    WVPASS bup meta -cf src.meta src
+
+    WVPASS mkdir dest
+    WVPASS cd dest
+    # Make sure we don't change (or try to change) the user when not root.
+    WVPASS bup meta --edit --set-user root ../src.meta | WVPASS bup meta -x
+    WVPASS bup xstat src | WVPASS grep -qvE '^user: root'
+    WVPASS rm -rf src
+    WVPASS bup meta --edit --unset-user --set-uid 0 ../src.meta \
+        | WVPASS bup meta -x
+    WVPASS bup xstat src | WVPASS grep -qvE '^user: root'
+
+    # Make sure we can restore one of the user's groups.
+    WVPASS rm -rf src
+    WVPASS bup meta --edit --set-group "$last_group" ../src.meta \
+        | WVPASS bup meta -x
+    WVPASS bup xstat src | WVPASS grep -qE "^group: $last_group_erx"
+
+    # Make sure we can restore one of the user's gids.
+    user_gids="$(id -G)" || exit $?
+    last_gid="$(echo ${user_gids/* /})" || exit $?
+    WVPASS rm -rf src
+    WVPASS bup meta --edit --unset-group --set-gid "$last_gid" ../src.meta \
+        | WVPASS bup meta -x
+    WVPASS bup xstat src | WVPASS grep -qE "^gid: $last_gid"
+
+    # Test --numeric-ids (gid).
+    WVPASS rm -rf src
+    current_gidx=$(bup meta -tvvf ../src.meta | grep -ae '^gid:') || exit $?
+    WVPASS bup meta --edit --set-group "$last_group" ../src.meta \
+        | WVPASS bup meta -x --numeric-ids
+    new_gidx=$(bup xstat src | grep -ae '^gid:') || exit $?
+    WVPASSEQ "$current_gidx" "$new_gidx"
+
+    # Test that restoring an unknown user works.
+    unknown_user=$("$TOP"/dev/unknown-owner --user) || exit $?
+    WVPASS rm -rf src
+    current_uidx=$(bup meta -tvvf ../src.meta | grep -ae '^uid:') || exit $?
+    WVPASS bup meta --edit --set-user "$unknown_user" ../src.meta \
+        | WVPASS bup meta -x
+    new_uidx=$(bup xstat src | grep -ae '^uid:') || exit $?
+    WVPASSEQ "$current_uidx" "$new_uidx"
+
+    # Test that restoring an unknown group works.
+    unknown_group=$("$TOP"/dev/unknown-owner --group) || exit $?
+    WVPASS rm -rf src
+    current_gidx=$(bup meta -tvvf ../src.meta | grep -ae '^gid:') || exit $?
+    WVPASS bup meta --edit --set-group "$unknown_group" ../src.meta \
+        | WVPASS bup meta -x
+    new_gidx=$(bup xstat src | grep -ae '^gid:') || exit $?
+    WVPASSEQ "$current_gidx" "$new_gidx"
+
+    WVPASS rm -r "$tmpdir"
+
+) || exit $?
+
+# Test ownership restoration (when root or fakeroot).
+(
+    if [ "$root_status" = none ]; then
+        exit 0
+    fi
+
+    tmpdir="$(WVPASS wvmktempdir)" || exit $?    
+
+    uid=$(WVPASS id -un) || exit $?
+    gid=$(WVPASS id -gn) || exit $?
+
+    WVSTART 'metadata (restoration of ownership as root)'
+    WVPASS cd "$tmpdir"
+    WVPASS touch src
+    WVPASS chown "$uid:$gid" src # In case the parent dir is sgid, etc.
+    WVPASS bup meta -cf src.meta src
+
+    WVPASS mkdir dest
+    WVPASS chmod 700 dest # so we can't accidentally do something insecure
+    WVPASS cd dest
+
+    other_uinfo="$(id-other-than --user "$uid")" || exit $?
+    other_user="${other_uinfo%%:*}"
+    other_uid="${other_uinfo##*:}"
+
+    other_ginfo="$(id-other-than --group "$gid")" || exit $?
+    other_group="${other_ginfo%%:*}"
+    other_gid="${other_ginfo##*:}"
+
+    # Make sure we can restore a uid (must be in /etc/passwd b/c cygwin).
+    WVPASS bup meta --edit --unset-user --set-uid "$other_uid" ../src.meta \
+        | WVPASS bup meta -x
+    WVPASS bup xstat src | WVPASS grep -qE "^uid: $other_uid"
+
+    # Make sure we can restore a gid (must be in /etc/group b/c cygwin).
+    WVPASS bup meta --edit --unset-group --set-gid "$other_gid" ../src.meta \
+        | WVPASS bup meta -x
+    WVPASS bup xstat src | WVPASS grep -qE "^gid: $other_gid"
+
+    other_uinfo2="$(id-other-than --user "$(id -un)" "$other_user")" || exit $?
+    other_user2="${other_uinfo2%%:*}"
+    other_user2_erx="$(escape-erx "$other_user2")" || exit $?
+    other_uid2="${other_uinfo2##*:}"
+
+    other_ginfo2="$(id-other-than --group "$(id -gn)" "$other_group")" || exit $?
+    other_group2="${other_ginfo2%%:*}"
+    other_group2_erx="$(escape-erx "$other_group2")" || exit $?
+    other_gid2="${other_ginfo2##*:}"
+
+    # Try to restore a user (and see that user trumps uid when uid is not 0).
+    WVPASS bup meta --edit \
+        --set-uid "$other_uid" --set-user "$other_user2" ../src.meta \
+        | WVPASS bup meta -x
+    WVPASS bup xstat src | WVPASS grep -qE "^user: $other_user2_erx"
+
+    # Try to restore a group (and see that group trumps gid when gid is not 0).
+    WVPASS bup meta --edit \
+        --set-gid "$other_gid" --set-group "$other_group2" ../src.meta \
+        | WVPASS bup meta -x
+    WVPASS bup xstat src | WVPASS grep -qE "^group: $other_group2_erx"
+
+    # Test --numeric-ids (uid).  Note the name 'root' is not handled
+    # specially, so we use that here as the test user name.  We assume
+    # that the root user's uid is never 42.
+    WVPASS rm -rf src
+    WVPASS bup meta --edit --set-user root --set-uid "$other_uid" ../src.meta \
+        | WVPASS bup meta -x --numeric-ids
+    new_uidx=$(bup xstat src | grep -e '^uid:') || exit $?
+    WVPASSEQ "$new_uidx" "uid: $other_uid"
+
+    # Test --numeric-ids (gid).  Note the name 'root' is not handled
+    # specially, so we use that here as the test group name.  We
+    # assume that the root group's gid is never 42.
+    WVPASS rm -rf src
+    WVPASS bup meta --edit --set-group root --set-gid "$other_gid" ../src.meta \
+        | WVPASS bup meta -x --numeric-ids
+    new_gidx=$(bup xstat src | grep -e '^gid:') || exit $?
+    WVPASSEQ "$new_gidx" "gid: $other_gid"
+
+    # Test that restoring an unknown user works.
+    unknown_user=$("$TOP"/dev/unknown-owner --user) || exit $?
+    WVPASS rm -rf src
+    WVPASS bup meta --edit \
+        --set-uid "$other_uid" --set-user "$unknown_user" ../src.meta \
+        | WVPASS bup meta -x
+    new_uidx=$(bup xstat src | grep -e '^uid:') || exit $?
+    WVPASSEQ "$new_uidx" "uid: $other_uid"
+
+    # Test that restoring an unknown group works.
+    unknown_group=$("$TOP"/dev/unknown-owner --group) || exit $?
+    WVPASS rm -rf src
+    WVPASS bup meta --edit \
+        --set-gid "$other_gid" --set-group "$unknown_group" ../src.meta \
+        | WVPASS bup meta -x
+    new_gidx=$(bup xstat src | grep -e '^gid:') || exit $?
+    WVPASSEQ "$new_gidx" "gid: $other_gid"
+
+    if ! [[ $(uname) =~ CYGWIN ]]; then
+        # For now, skip these on Cygwin because it doesn't allow
+        # restoring an unknown uid/gid.
+
+        # Make sure a uid of 0 trumps a non-root user.
+        WVPASS bup meta --edit --set-user "$other_user2" ../src.meta \
+            | WVPASS bup meta -x
+        WVPASS bup xstat src | WVPASS grep -qvE "^user: $other_user2_erx"
+        WVPASS bup xstat src | WVPASS grep -qE "^uid: 0"
+
+        # Make sure a gid of 0 trumps a non-root group.
+        WVPASS bup meta --edit --set-group "$other_group2" ../src.meta \
+            | WVPASS bup meta -x
+        WVPASS bup xstat src | WVPASS grep -qvE "^group: $other_group2_erx"
+        WVPASS bup xstat src | WVPASS grep -qE "^gid: 0"
+    fi
+
+    WVPASS rm -r "$tmpdir"
+
+) || exit $?
+
+
+# Root-only tests that require an FS with all the trimmings: ACLs,
+# Linux attr, Linux xattr, etc.
+if [ "$root_status" = root ]; then
+    (
+        # Some cleanup handled in universal-cleanup() above.
+        # These tests are only likely to work under Linux for now
+        # (patches welcome).
+        [[ $(uname) =~ Linux ]] || exit 0
+
+        if ! modprobe loop; then
+            echo 'Unable to load loopback module; skipping dependent tests.' 1>&2
+            exit 0
+        fi
+
+        testfs="$(WVPASS wvmkmountpt)" || exit $?
+        testfs_limited="$(WVPASS wvmkmountpt)" || exit $?
+        tmpdir="$(WVPASS wvmktempdir)" || exit $?
+        export BUP_DIR="$tmpdir/bup"
+
+        WVSTART 'meta - general (as root)'
+        WVPASS setup-test-tree
+        WVPASS cd "$tmpdir"
+
+        umount "$testfs"
+        WVPASS dd if=/dev/zero of=testfs.img bs=1M count=32
+        # Make sure we have all the options the chattr test needs
+        # (i.e. create a "normal" ext4 filesystem).
+        WVPASS mke2fs -F -m 0 \
+            -I 256 \
+            -O has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize \
+            testfs.img
+        WVPASS mount -o loop,acl,user_xattr testfs.img "$testfs"
+        # Hide, so that tests can't create risks.
+        WVPASS chown root:root "$testfs"
+        WVPASS chmod 0700 "$testfs"
+
+        umount "$testfs_limited"
+        WVPASS dd if=/dev/zero of=testfs-limited.img bs=1M count=32
+        WVPASS mkfs -t vfat testfs-limited.img
+        WVPASS mount -o loop,uid=root,gid=root,umask=0077 \
+            testfs-limited.img "$testfs_limited"
+
+        WVPASS cp -pPR src "$testfs"/src
+        (WVPASS cd "$testfs"; WVPASS test-src-create-extract) || exit $?
+
+        WVSTART 'meta - atime (as root)'
+        WVPASS force-delete "$testfs"/src
+        WVPASS mkdir "$testfs"/src
+        (
+            WVPASS mkdir "$testfs"/src/foo
+            WVPASS touch "$testfs"/src/bar
+            WVPASS bup-python -c "from bup import xstat; \
+                x = xstat.timespec_to_nsecs((42, 0));\
+                xstat.utime(b'$testfs/src/foo', (x, x));\
+                xstat.utime(b'$testfs/src/bar', (x, x));"
+            WVPASS cd "$testfs"
+            WVPASS bup meta -v --create --recurse --file src.meta src
+            WVPASS bup meta -tvf src.meta
+            # Test extract.
+            WVPASS force-delete src-restore
+            WVPASS mkdir src-restore
+            WVPASS cd src-restore
+            WVPASS bup meta --extract --file ../src.meta
+            WVPASSEQ "$(bup xstat --include-fields=atime src/foo)" "atime: 42"
+            WVPASSEQ "$(bup xstat --include-fields=atime src/bar)" "atime: 42"
+            # Test start/finish extract.
+            WVPASS force-delete src
+            WVPASS bup meta --start-extract --file ../src.meta
+            WVPASS test -d src
+            WVPASS bup meta --finish-extract --file ../src.meta
+            WVPASSEQ "$(bup xstat --include-fields=atime src/foo)" "atime: 42"
+            WVPASSEQ "$(bup xstat --include-fields=atime src/bar)" "atime: 42"
+        ) || exit $?
+
+        WVSTART 'meta - Linux attr (as root)'
+        WVPASS force-delete "$testfs"/src
+        WVPASS mkdir "$testfs"/src
+        (
+            WVPASS touch "$testfs"/src/foo
+            WVPASS mkdir "$testfs"/src/bar
+            WVPASS chattr +acdeijstuADS "$testfs"/src/foo
+            WVPASS chattr +acdeijstuADST "$testfs"/src/bar
+            (WVPASS cd "$testfs"; WVPASS test-src-create-extract) || exit $?
+            # Test restoration to a limited filesystem (vfat).
+            (
+                WVPASS bup meta --create --recurse --file "$testfs"/src.meta \
+                    "$testfs"/src
+                WVPASS force-delete "$testfs_limited"/src-restore
+                WVPASS mkdir "$testfs_limited"/src-restore
+                WVPASS cd "$testfs_limited"/src-restore
+                WVFAIL bup meta --extract --file "$testfs"/src.meta 2>&1 \
+                    | WVPASS grep -e '^Linux chattr:' \
+                    | WVPASS bup-cfg-py -c \
+                    'import sys; exit(not len(sys.stdin.readlines()) == 3)'
+            ) || exit $?
+        ) || exit $?
+
+        WVSTART 'meta - Linux xattr (as root)'
+        WVPASS force-delete "$testfs"/src
+        WVPASS mkdir "$testfs"/src
+        WVPASS touch "$testfs"/src/foo
+        WVPASS mkdir "$testfs"/src/bar
+        WVPASS attr -s foo -V bar "$testfs"/src/foo
+        WVPASS attr -s foo -V bar "$testfs"/src/bar
+        (WVPASS cd "$testfs"; WVPASS test-src-create-extract) || exit $?
+
+        # Test restoration to a limited filesystem (vfat).
+        (
+            WVPASS bup meta --create --recurse --file "$testfs"/src.meta \
+                "$testfs"/src
+            WVPASS force-delete "$testfs_limited"/src-restore
+            WVPASS mkdir "$testfs_limited"/src-restore
+            WVPASS cd "$testfs_limited"/src-restore
+            WVFAIL bup meta --extract --file "$testfs"/src.meta
+            WVFAIL bup meta --extract --file "$testfs"/src.meta 2>&1 \
+                | WVPASS grep -e "^xattr\.set u\?'" \
+                | WVPASS bup-cfg-py -c \
+                'import sys; exit(not len(sys.stdin.readlines()) == 2)'
+        ) || exit $?
+
+        WVSTART 'meta - POSIX.1e ACLs (as root)'
+        WVPASS force-delete "$testfs"/src
+        WVPASS mkdir "$testfs"/src
+        WVPASS touch "$testfs"/src/foo
+        WVPASS mkdir "$testfs"/src/bar
+        WVPASS setfacl -m u:root:r "$testfs"/src/foo
+        WVPASS setfacl -m u:root:r "$testfs"/src/bar
+        (WVPASS cd "$testfs"; WVPASS test-src-create-extract) || exit $?
+
+        # Test restoration to a limited filesystem (vfat).
+        (
+            WVPASS bup meta --create --recurse --file "$testfs"/src.meta \
+                "$testfs"/src
+            WVPASS force-delete "$testfs_limited"/src-restore
+            WVPASS mkdir "$testfs_limited"/src-restore
+            WVPASS cd "$testfs_limited"/src-restore
+            WVFAIL bup meta --extract --file "$testfs"/src.meta 2>&1 \
+                | WVPASS grep -e '^POSIX1e ACL applyto:' \
+                | WVPASS bup-cfg-py -c \
+                'import sys; exit(not len(sys.stdin.readlines()) == 2)'
+        ) || exit $?
+
+        WVPASS umount "$testfs"
+        WVPASS umount "$testfs_limited"
+        WVPASS rm -r "$testfs" "$testfs_limited"
+
+        WVPASS rm -r "$tmpdir"
+
+    ) || exit $?
+fi
+
+WVPASS rm -r "$tmpdir"
diff --git a/test/ext/test-meta.sh b/test/ext/test-meta.sh
deleted file mode 100755 (executable)
index 0f8bb60..0000000
+++ /dev/null
@@ -1,783 +0,0 @@
-#!/usr/bin/env bash
-. wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-root_status="$(dev/root-status)" || exit $?
-
-TOP="$(WVPASS pwd)" || exit $?
-export PATH="$TOP/test/bin:$PATH"
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-export BUP_DIR="$tmpdir/bup"
-
-# Assume that mvmktempdir will always use the same dir.
-timestamp_resolutions="$(dev/ns-timestamp-resolutions "$tmpdir/canary")" \
-    || exit $?
-atime_resolution="$(echo $timestamp_resolutions | WVPASS cut -d' ' -f 1)" \
-    || exit $?
-mtime_resolution="$(echo $timestamp_resolutions | WVPASS cut -d' ' -f 2)" \
-    || exit $?
-WVPASS rm "$tmpdir/canary"
-
-bup()
-{
-    "$TOP/bup" "$@"
-}
-
-hardlink-sets()
-{
-    "$TOP/dev/hardlink-sets" "$@"
-}
-
-id-other-than()
-{
-    "$TOP/dev/id-other-than" "$@"
-}
-
-# Very simple metadata tests -- create a test tree then check that bup
-# meta can reproduce the metadata correctly (according to bup xstat)
-# via create, extract, start-extract, and finish-extract.  The current
-# tests are crude, and this does not fully test devices, varying
-# users/groups, acls, attrs, etc.
-
-genstat()
-{
-    (
-        export PATH="$TOP/bin:$PATH" # pick up bup
-        bup version
-        # Skip atime (test elsewhere) to avoid the observer effect.
-        WVPASS find . -print0 | WVPASS sort-z \
-            | WVPASS xargs -0 bup xstat \
-            --mtime-resolution "$mtime_resolution"ns \
-            --exclude-fields ctime,atime,size
-    )
-}
-
-test-src-create-extract()
-{
-    # Test bup meta create/extract for ./src -> ./src-restore.
-    # Also writes to ./src-stat and ./src-restore-stat.
-    (
-        (WVPASS cd src; WVPASS genstat) > src-stat || exit $?
-        WVPASS bup meta --create --recurse --file src.meta src
-        # Test extract.
-        WVPASS force-delete src-restore
-        WVPASS mkdir src-restore
-        WVPASS cd src-restore
-        WVPASS bup meta --extract --file ../src.meta
-        WVPASS test -d src
-        (WVPASS cd src; WVPASS genstat >../../src-restore-stat) || exit $?
-        WVPASS diff -U5 ../src-stat ../src-restore-stat
-        # Test start/finish extract.
-        WVPASS force-delete src
-        WVPASS bup meta --start-extract --file ../src.meta
-        WVPASS test -d src
-        WVPASS bup meta --finish-extract --file ../src.meta
-        (WVPASS cd src; WVPASS genstat >../../src-restore-stat) || exit $?
-        WVPASS diff -U5 ../src-stat ../src-restore-stat
-    )
-}
-
-test-src-save-restore()
-{
-    # Test bup save/restore metadata for ./src -> ./src-restore.  Also
-    # writes to BUP_DIR.  Note that for now this just tests the
-    # restore below src/, in order to avoid having to worry about
-    # operations that require root (like chown /home).
-    (
-        WVPASS rm -rf "$BUP_DIR"
-        WVPASS bup init
-        WVPASS bup index src
-        WVPASS bup save -t -n src src
-        # Test extract.
-        WVPASS force-delete src-restore
-        WVPASS mkdir src-restore
-        WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
-        WVPASS test -d src-restore/src
-        WVPASS "$TOP/dev/compare-trees" -c src/ src-restore/src/
-        WVPASS rm -rf src.bup
-    )
-}
-
-setup-test-tree()
-{
-    WVPASS "$TOP/dev/sync-tree" "$TOP/test/sampledata/" "$tmpdir/src/"
-
-    # Add some hard links for the general tests.
-    (
-        WVPASS cd "$tmpdir"/src
-        WVPASS touch hardlink-target
-        WVPASS ln hardlink-target hardlink-1
-        WVPASS ln hardlink-target hardlink-2
-        WVPASS ln hardlink-target hardlink-3
-    ) || exit $?
-
-    # Add some trivial files for the index, modify, save tests.
-    (
-        WVPASS cd "$tmpdir"/src
-        WVPASS mkdir volatile
-        WVPASS touch volatile/{1,2,3}
-    ) || exit $?
-
-    # Regression test for metadata sort order.  Previously, these two
-    # entries would sort in the wrong order because the metadata
-    # entries were being sorted by mangled name, but the index isn't.
-    WVPASS dd if=/dev/zero of="$tmpdir"/src/foo bs=1k count=33
-    WVPASS touch -t 201111111111 "$tmpdir"/src/foo
-    WVPASS touch -t 201112121111 "$tmpdir"/src/foo-bar
-
-    dev/mksock "$tmpdir"/src/test-socket || true
-}
-
-# Use the test tree to check bup meta.
-WVSTART 'meta --create/--extract'
-(
-    tmpdir="$(WVPASS wvmktempdir)" || exit $?
-    export BUP_DIR="$tmpdir/bup"
-    WVPASS setup-test-tree
-    WVPASS cd "$tmpdir"
-    WVPASS test-src-create-extract
-
-    # Test a top-level file (not dir).
-    WVPASS touch src-file
-    WVPASS bup meta -cf src-file.meta src-file
-    WVPASS mkdir dest
-    WVPASS cd dest
-    WVPASS bup meta -xf ../src-file.meta
-    WVPASS rm -r "$tmpdir"
-) || exit $?
-
-# Use the test tree to check bup save/restore metadata.
-WVSTART 'metadata save/restore (general)'
-(
-    tmpdir="$(WVPASS wvmktempdir)" || exit $?
-    export BUP_DIR="$tmpdir/bup"
-    WVPASS setup-test-tree
-    WVPASS cd "$tmpdir"
-    WVPASS test-src-save-restore
-
-    # Test a deeper subdir/ to make sure top-level non-dir metadata is
-    # restored correctly.  We need at least one dir and one non-dir at
-    # the "top-level".
-    WVPASS test -d src/var/cmd
-    WVPASS test -f src/var/cmd/save-cmd.py
-    WVPASS rm -rf "$BUP_DIR"
-    WVPASS bup init
-    WVPASS touch -t 201111111111 src-restore # Make sure the top won't match.
-    WVPASS bup index src
-    WVPASS bup save -t -n src src
-    WVPASS force-delete src-restore
-    WVPASS bup restore -C src-restore "/src/latest$(pwd)/src/var/."
-    WVPASS touch -t 201211111111 src-restore # Make sure the top won't match.
-    # Check that the only difference is the top dir.
-    WVFAIL $TOP/dev/compare-trees -c src/var/ src-restore/ > tmp-compare-trees
-    WVPASSEQ $(cat tmp-compare-trees | wc -l) 1
-    # The number of rsync status characters varies, so accept any
-    # number of trailing dots.  For example OS X native rsync produces
-    # 9, but Homebrew's produces 12, while on other platforms, 11 is
-    # common.
-    expected_diff_rx='^\.d\.\.t\.\.\.(\.)+ \./$'
-    if ! grep -qE "$expected_diff_rx" tmp-compare-trees; then
-        echo -n 'tmp-compare-trees: ' 1>&2
-        cat tmp-compare-trees 1>&2
-    fi
-    WVPASS grep -qE "$expected_diff_rx" tmp-compare-trees
-    WVPASS rm -r "$tmpdir"
-) || exit $?
-
-# Test that we pull the index (not filesystem) metadata for any
-# unchanged files whenever we're saving other files in a given
-# directory.
-WVSTART 'metadata save/restore (using index metadata)'
-(
-    tmpdir="$(WVPASS wvmktempdir)" || exit $?
-    export BUP_DIR="$tmpdir/bup"
-    WVPASS setup-test-tree
-    WVPASS cd "$tmpdir"
-
-    # ...for now -- might be a problem with hardlink restores that was
-    # causing noise wrt this test.
-    WVPASS rm -rf src/hardlink*
-
-    # Pause here to keep the filesystem changes far enough away from
-    # the first index run that bup won't cap their index timestamps
-    # (see "bup help index" for more information).  Without this
-    # sleep, the compare-trees test below "Bup should *not* pick up
-    # these metadata..." may fail.
-    WVPASS sleep 1
-
-    WVPASS rm -rf "$BUP_DIR"
-    WVPASS bup init
-    WVPASS bup index src
-    WVPASS bup save -t -n src src
-
-    WVPASS force-delete src-restore-1
-    WVPASS mkdir src-restore-1
-    WVPASS bup restore -C src-restore-1 "/src/latest$(pwd)/"
-    WVPASS test -d src-restore-1/src
-    WVPASS "$TOP/dev/compare-trees" -c src/ src-restore-1/src/
-
-    WVPASS echo "blarg" > src/volatile/1
-    WVPASS cp -pP src/volatile/1 src-restore-1/src/volatile/
-    WVPASS bup index src
-
-    # Bup should *not* pick up these metadata changes.
-    WVPASS touch src/volatile/2
-
-    WVPASS bup save -t -n src src
-
-    WVPASS force-delete src-restore-2
-    WVPASS mkdir src-restore-2
-    WVPASS bup restore -C src-restore-2 "/src/latest$(pwd)/"
-    WVPASS test -d src-restore-2/src
-    WVPASS "$TOP/dev/compare-trees" -c src-restore-1/src/ src-restore-2/src/
-
-    WVPASS rm -r "$tmpdir"
-
-) || exit $?
-
-
-setup-hardlink-test()
-{
-    WVPASS rm -rf "$tmpdir/src" "$BUP_DIR"
-    WVPASS bup init
-    WVPASS mkdir "$tmpdir/src"
-}
-
-hardlink-test-run-restore()
-{
-    WVPASS force-delete src-restore
-    WVPASS mkdir src-restore
-    WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
-    WVPASS test -d src-restore/src
-}
-
-# Test hardlinks more carefully.
-WVSTART 'metadata save/restore (hardlinks)'
-(
-    tmpdir="$(WVPASS wvmktempdir)" || exit $?    
-    export BUP_DIR="$tmpdir/bup"
-
-    WVPASS setup-hardlink-test
-    WVPASS cd "$tmpdir"
-    
-    # Test trivial case - single hardlink.
-    (
-        WVPASS cd src
-        WVPASS touch hardlink-target
-        WVPASS ln hardlink-target hardlink-1
-    ) || exit $?
-    WVPASS bup index src
-    WVPASS bup save -t -n src src
-    WVPASS hardlink-test-run-restore
-    WVPASS "$TOP/dev/compare-trees" -c src/ src-restore/src/
-
-    # Test the case where the hardlink hasn't changed, but the tree
-    # needs to be saved again. i.e. the save-cmd.py "if hashvalid:"
-    # case.
-    (
-        WVPASS cd src
-        WVPASS echo whatever > something-new
-    ) || exit $?
-    WVPASS bup index src
-    WVPASS bup save -t -n src src
-    WVPASS hardlink-test-run-restore
-    WVPASS "$TOP/dev/compare-trees" -c src/ src-restore/src/
-
-    # Test hardlink changes between index runs.
-    #
-    WVPASS setup-hardlink-test
-    WVPASS cd src
-    WVPASS touch hardlink-target-a
-    WVPASS touch hardlink-target-b
-    WVPASS ln hardlink-target-a hardlink-b-1
-    WVPASS ln hardlink-target-a hardlink-a-1
-    WVPASS cd ..
-    WVPASS bup index -vv src
-    WVPASS rm src/hardlink-b-1
-    WVPASS ln src/hardlink-target-b src/hardlink-b-1
-    WVPASS bup index -vv src
-    WVPASS bup save -t -n src src
-    WVPASS hardlink-test-run-restore
-    WVPASS echo ./src/hardlink-a-1 > hardlink-sets.expected
-    WVPASS echo ./src/hardlink-target-a >> hardlink-sets.expected
-    WVPASS echo >> hardlink-sets.expected
-    WVPASS echo ./src/hardlink-b-1 >> hardlink-sets.expected
-    WVPASS echo ./src/hardlink-target-b >> hardlink-sets.expected
-    (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
-        || exit $?
-    WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
-
-    # Test hardlink changes between index and save -- hardlink set [a
-    # b c d] changes to [a b] [c d].  At least right now bup should
-    # notice and recreate the latter.
-    WVPASS setup-hardlink-test
-    WVPASS cd "$tmpdir"/src
-    WVPASS touch a
-    WVPASS ln a b
-    WVPASS ln a c
-    WVPASS ln a d
-    WVPASS cd ..
-    WVPASS bup index -vv src
-    WVPASS rm src/c src/d
-    WVPASS touch src/c
-    WVPASS ln src/c src/d
-    WVPASS bup save -t -n src src
-    WVPASS hardlink-test-run-restore
-    WVPASS echo ./src/a > hardlink-sets.expected
-    WVPASS echo ./src/b >> hardlink-sets.expected
-    WVPASS echo >> hardlink-sets.expected
-    WVPASS echo ./src/c >> hardlink-sets.expected
-    WVPASS echo ./src/d >> hardlink-sets.expected
-    (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
-        || exit $?
-    WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
-
-    # Test that we don't link outside restore tree.
-    WVPASS setup-hardlink-test
-    WVPASS cd "$tmpdir"
-    WVPASS mkdir src/a src/b
-    WVPASS touch src/a/1
-    WVPASS ln src/a/1 src/b/1
-    WVPASS bup index -vv src
-    WVPASS bup save -t -n src src
-    WVPASS force-delete src-restore
-    WVPASS mkdir src-restore
-    WVPASS bup restore -C src-restore "/src/latest$(pwd)/src/a/"
-    WVPASS test -e src-restore/1
-    WVPASS echo -n > hardlink-sets.expected
-    (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
-        || exit $?
-    WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
-
-    # Test that we do link within separate sub-trees.
-    WVPASS setup-hardlink-test
-    WVPASS cd "$tmpdir"
-    WVPASS mkdir src/a src/b
-    WVPASS touch src/a/1
-    WVPASS ln src/a/1 src/b/1
-    WVPASS bup index -vv src/a src/b
-    WVPASS bup save -t -n src src/a src/b
-    WVPASS hardlink-test-run-restore
-    WVPASS echo ./src/a/1 > hardlink-sets.expected
-    WVPASS echo ./src/b/1 >> hardlink-sets.expected
-    (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
-        || exit $?
-    WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
-
-    WVPASS rm -r "$tmpdir"
-
-) || exit $?
-
-WVSTART 'meta --edit'
-(
-    tmpdir="$(WVPASS wvmktempdir)" || exit $?    
-    WVPASS cd "$tmpdir"
-    WVPASS mkdir src
-
-    WVPASS bup meta -cf src.meta src
-
-    WVPASS bup meta --edit --set-uid 0 src.meta | WVPASS bup meta -tvvf - \
-        | WVPASS grep -qE '^uid: 0'
-    WVPASS bup meta --edit --set-uid 1000 src.meta | WVPASS bup meta -tvvf - \
-        | WVPASS grep -qE '^uid: 1000'
-
-    WVPASS bup meta --edit --set-gid 0 src.meta | WVPASS bup meta -tvvf - \
-        | WVPASS grep -qE '^gid: 0'
-    WVPASS bup meta --edit --set-gid 1000 src.meta | WVPASS bup meta -tvvf - \
-        | WVPASS grep -qE '^gid: 1000'
-
-    WVPASS bup meta --edit --set-user foo src.meta | WVPASS bup meta -tvvf - \
-        | WVPASS grep -qE '^user: foo'
-    WVPASS bup meta --edit --set-user bar src.meta | WVPASS bup meta -tvvf - \
-        | WVPASS grep -qE '^user: bar'
-    WVPASS bup meta --edit --unset-user src.meta | WVPASS bup meta -tvvf - \
-        | WVPASS grep -qE '^user:'
-    WVPASS bup meta --edit --set-user bar --unset-user src.meta \
-        | WVPASS bup meta -tvvf - | WVPASS grep -qE '^user:'
-    WVPASS bup meta --edit --unset-user --set-user bar src.meta \
-        | WVPASS bup meta -tvvf - | WVPASS grep -qE '^user: bar'
-
-    WVPASS bup meta --edit --set-group foo src.meta | WVPASS bup meta -tvvf - \
-        | WVPASS grep -qE '^group: foo'
-    WVPASS bup meta --edit --set-group bar src.meta | WVPASS bup meta -tvvf - \
-        | WVPASS grep -qE '^group: bar'
-    WVPASS bup meta --edit --unset-group src.meta | WVPASS bup meta -tvvf - \
-        | WVPASS grep -qE '^group:'
-    WVPASS bup meta --edit --set-group bar --unset-group src.meta \
-        | WVPASS bup meta -tvvf - | WVPASS grep -qE '^group:'
-    WVPASS bup meta --edit --unset-group --set-group bar src.meta \
-        | WVPASS bup meta -tvvf - | grep -qE '^group: bar'
-
-    WVPASS rm -r "$tmpdir"
-
-) || exit $?
-
-WVSTART 'meta --no-recurse'
-(
-    tmpdir="$(WVPASS wvmktempdir)" || exit $?    
-    WVPASS cd "$tmpdir"
-    WVPASS mkdir src
-    WVPASS mkdir src/foo
-    WVPASS touch src/foo/{1,2,3}
-    WVPASS bup meta -cf src.meta src
-    WVPASSEQ "$(bup meta -tf src.meta | LC_ALL=C sort)" "src/
-src/foo/
-src/foo/1
-src/foo/2
-src/foo/3"
-    WVPASS bup meta --no-recurse -cf src.meta src
-    WVPASSEQ "$(bup meta -tf src.meta | LC_ALL=C sort)" "src/"
-    WVPASS rm -r "$tmpdir"
-) || exit $?
-
-# Test ownership restoration (when not root or fakeroot).
-(
-    if [ "$root_status" != none ]; then
-        exit 0
-    fi
-
-    tmpdir="$(WVPASS wvmktempdir)" || exit $?    
-
-    # FIXME: binary groups
-    first_group="$(WVPASS bup-cfg-py -c 'import os,grp; \
-      print(grp.getgrgid(os.getgroups()[0])[0])')" || exit $?
-    last_group="$(bup-cfg-py -c 'import os,grp; \
-      print(grp.getgrgid(os.getgroups()[-1])[0])')" || exit $?
-    last_group_erx="$(escape-erx "$last_group")"
-
-    WVSTART 'metadata (restoration of ownership)'
-    WVPASS cd "$tmpdir"
-    WVPASS touch src
-    # Some systems always assign the parent dir group to new paths
-    # (sgid).  Make sure the group is one we're in.
-    WVPASS chgrp -R "$first_group" src
-
-    WVPASS bup meta -cf src.meta src
-
-    WVPASS mkdir dest
-    WVPASS cd dest
-    # Make sure we don't change (or try to change) the user when not root.
-    WVPASS bup meta --edit --set-user root ../src.meta | WVPASS bup meta -x
-    WVPASS bup xstat src | WVPASS grep -qvE '^user: root'
-    WVPASS rm -rf src
-    WVPASS bup meta --edit --unset-user --set-uid 0 ../src.meta \
-        | WVPASS bup meta -x
-    WVPASS bup xstat src | WVPASS grep -qvE '^user: root'
-
-    # Make sure we can restore one of the user's groups.
-    WVPASS rm -rf src
-    WVPASS bup meta --edit --set-group "$last_group" ../src.meta \
-        | WVPASS bup meta -x
-    WVPASS bup xstat src | WVPASS grep -qE "^group: $last_group_erx"
-
-    # Make sure we can restore one of the user's gids.
-    user_gids="$(id -G)" || exit $?
-    last_gid="$(echo ${user_gids/* /})" || exit $?
-    WVPASS rm -rf src
-    WVPASS bup meta --edit --unset-group --set-gid "$last_gid" ../src.meta \
-        | WVPASS bup meta -x
-    WVPASS bup xstat src | WVPASS grep -qE "^gid: $last_gid"
-
-    # Test --numeric-ids (gid).
-    WVPASS rm -rf src
-    current_gidx=$(bup meta -tvvf ../src.meta | grep -ae '^gid:') || exit $?
-    WVPASS bup meta --edit --set-group "$last_group" ../src.meta \
-        | WVPASS bup meta -x --numeric-ids
-    new_gidx=$(bup xstat src | grep -ae '^gid:') || exit $?
-    WVPASSEQ "$current_gidx" "$new_gidx"
-
-    # Test that restoring an unknown user works.
-    unknown_user=$("$TOP"/dev/unknown-owner --user) || exit $?
-    WVPASS rm -rf src
-    current_uidx=$(bup meta -tvvf ../src.meta | grep -ae '^uid:') || exit $?
-    WVPASS bup meta --edit --set-user "$unknown_user" ../src.meta \
-        | WVPASS bup meta -x
-    new_uidx=$(bup xstat src | grep -ae '^uid:') || exit $?
-    WVPASSEQ "$current_uidx" "$new_uidx"
-
-    # Test that restoring an unknown group works.
-    unknown_group=$("$TOP"/dev/unknown-owner --group) || exit $?
-    WVPASS rm -rf src
-    current_gidx=$(bup meta -tvvf ../src.meta | grep -ae '^gid:') || exit $?
-    WVPASS bup meta --edit --set-group "$unknown_group" ../src.meta \
-        | WVPASS bup meta -x
-    new_gidx=$(bup xstat src | grep -ae '^gid:') || exit $?
-    WVPASSEQ "$current_gidx" "$new_gidx"
-
-    WVPASS rm -r "$tmpdir"
-
-) || exit $?
-
-# Test ownership restoration (when root or fakeroot).
-(
-    if [ "$root_status" = none ]; then
-        exit 0
-    fi
-
-    tmpdir="$(WVPASS wvmktempdir)" || exit $?    
-
-    uid=$(WVPASS id -un) || exit $?
-    gid=$(WVPASS id -gn) || exit $?
-
-    WVSTART 'metadata (restoration of ownership as root)'
-    WVPASS cd "$tmpdir"
-    WVPASS touch src
-    WVPASS chown "$uid:$gid" src # In case the parent dir is sgid, etc.
-    WVPASS bup meta -cf src.meta src
-
-    WVPASS mkdir dest
-    WVPASS chmod 700 dest # so we can't accidentally do something insecure
-    WVPASS cd dest
-
-    other_uinfo="$(id-other-than --user "$uid")" || exit $?
-    other_user="${other_uinfo%%:*}"
-    other_uid="${other_uinfo##*:}"
-
-    other_ginfo="$(id-other-than --group "$gid")" || exit $?
-    other_group="${other_ginfo%%:*}"
-    other_gid="${other_ginfo##*:}"
-
-    # Make sure we can restore a uid (must be in /etc/passwd b/c cygwin).
-    WVPASS bup meta --edit --unset-user --set-uid "$other_uid" ../src.meta \
-        | WVPASS bup meta -x
-    WVPASS bup xstat src | WVPASS grep -qE "^uid: $other_uid"
-
-    # Make sure we can restore a gid (must be in /etc/group b/c cygwin).
-    WVPASS bup meta --edit --unset-group --set-gid "$other_gid" ../src.meta \
-        | WVPASS bup meta -x
-    WVPASS bup xstat src | WVPASS grep -qE "^gid: $other_gid"
-
-    other_uinfo2="$(id-other-than --user "$(id -un)" "$other_user")" || exit $?
-    other_user2="${other_uinfo2%%:*}"
-    other_user2_erx="$(escape-erx "$other_user2")" || exit $?
-    other_uid2="${other_uinfo2##*:}"
-
-    other_ginfo2="$(id-other-than --group "$(id -gn)" "$other_group")" || exit $?
-    other_group2="${other_ginfo2%%:*}"
-    other_group2_erx="$(escape-erx "$other_group2")" || exit $?
-    other_gid2="${other_ginfo2##*:}"
-
-    # Try to restore a user (and see that user trumps uid when uid is not 0).
-    WVPASS bup meta --edit \
-        --set-uid "$other_uid" --set-user "$other_user2" ../src.meta \
-        | WVPASS bup meta -x
-    WVPASS bup xstat src | WVPASS grep -qE "^user: $other_user2_erx"
-
-    # Try to restore a group (and see that group trumps gid when gid is not 0).
-    WVPASS bup meta --edit \
-        --set-gid "$other_gid" --set-group "$other_group2" ../src.meta \
-        | WVPASS bup meta -x
-    WVPASS bup xstat src | WVPASS grep -qE "^group: $other_group2_erx"
-
-    # Test --numeric-ids (uid).  Note the name 'root' is not handled
-    # specially, so we use that here as the test user name.  We assume
-    # that the root user's uid is never 42.
-    WVPASS rm -rf src
-    WVPASS bup meta --edit --set-user root --set-uid "$other_uid" ../src.meta \
-        | WVPASS bup meta -x --numeric-ids
-    new_uidx=$(bup xstat src | grep -e '^uid:') || exit $?
-    WVPASSEQ "$new_uidx" "uid: $other_uid"
-
-    # Test --numeric-ids (gid).  Note the name 'root' is not handled
-    # specially, so we use that here as the test group name.  We
-    # assume that the root group's gid is never 42.
-    WVPASS rm -rf src
-    WVPASS bup meta --edit --set-group root --set-gid "$other_gid" ../src.meta \
-        | WVPASS bup meta -x --numeric-ids
-    new_gidx=$(bup xstat src | grep -e '^gid:') || exit $?
-    WVPASSEQ "$new_gidx" "gid: $other_gid"
-
-    # Test that restoring an unknown user works.
-    unknown_user=$("$TOP"/dev/unknown-owner --user) || exit $?
-    WVPASS rm -rf src
-    WVPASS bup meta --edit \
-        --set-uid "$other_uid" --set-user "$unknown_user" ../src.meta \
-        | WVPASS bup meta -x
-    new_uidx=$(bup xstat src | grep -e '^uid:') || exit $?
-    WVPASSEQ "$new_uidx" "uid: $other_uid"
-
-    # Test that restoring an unknown group works.
-    unknown_group=$("$TOP"/dev/unknown-owner --group) || exit $?
-    WVPASS rm -rf src
-    WVPASS bup meta --edit \
-        --set-gid "$other_gid" --set-group "$unknown_group" ../src.meta \
-        | WVPASS bup meta -x
-    new_gidx=$(bup xstat src | grep -e '^gid:') || exit $?
-    WVPASSEQ "$new_gidx" "gid: $other_gid"
-
-    if ! [[ $(uname) =~ CYGWIN ]]; then
-        # For now, skip these on Cygwin because it doesn't allow
-        # restoring an unknown uid/gid.
-
-        # Make sure a uid of 0 trumps a non-root user.
-        WVPASS bup meta --edit --set-user "$other_user2" ../src.meta \
-            | WVPASS bup meta -x
-        WVPASS bup xstat src | WVPASS grep -qvE "^user: $other_user2_erx"
-        WVPASS bup xstat src | WVPASS grep -qE "^uid: 0"
-
-        # Make sure a gid of 0 trumps a non-root group.
-        WVPASS bup meta --edit --set-group "$other_group2" ../src.meta \
-            | WVPASS bup meta -x
-        WVPASS bup xstat src | WVPASS grep -qvE "^group: $other_group2_erx"
-        WVPASS bup xstat src | WVPASS grep -qE "^gid: 0"
-    fi
-
-    WVPASS rm -r "$tmpdir"
-
-) || exit $?
-
-
-# Root-only tests that require an FS with all the trimmings: ACLs,
-# Linux attr, Linux xattr, etc.
-if [ "$root_status" = root ]; then
-    (
-        # Some cleanup handled in universal-cleanup() above.
-        # These tests are only likely to work under Linux for now
-        # (patches welcome).
-        [[ $(uname) =~ Linux ]] || exit 0
-
-        if ! modprobe loop; then
-            echo 'Unable to load loopback module; skipping dependent tests.' 1>&2
-            exit 0
-        fi
-
-        testfs="$(WVPASS wvmkmountpt)" || exit $?
-        testfs_limited="$(WVPASS wvmkmountpt)" || exit $?
-        tmpdir="$(WVPASS wvmktempdir)" || exit $?
-        export BUP_DIR="$tmpdir/bup"
-
-        WVSTART 'meta - general (as root)'
-        WVPASS setup-test-tree
-        WVPASS cd "$tmpdir"
-
-        umount "$testfs"
-        WVPASS dd if=/dev/zero of=testfs.img bs=1M count=32
-        # Make sure we have all the options the chattr test needs
-        # (i.e. create a "normal" ext4 filesystem).
-        WVPASS mke2fs -F -m 0 \
-            -I 256 \
-            -O has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize \
-            testfs.img
-        WVPASS mount -o loop,acl,user_xattr testfs.img "$testfs"
-        # Hide, so that tests can't create risks.
-        WVPASS chown root:root "$testfs"
-        WVPASS chmod 0700 "$testfs"
-
-        umount "$testfs_limited"
-        WVPASS dd if=/dev/zero of=testfs-limited.img bs=1M count=32
-        WVPASS mkfs -t vfat testfs-limited.img
-        WVPASS mount -o loop,uid=root,gid=root,umask=0077 \
-            testfs-limited.img "$testfs_limited"
-
-        WVPASS cp -pPR src "$testfs"/src
-        (WVPASS cd "$testfs"; WVPASS test-src-create-extract) || exit $?
-
-        WVSTART 'meta - atime (as root)'
-        WVPASS force-delete "$testfs"/src
-        WVPASS mkdir "$testfs"/src
-        (
-            WVPASS mkdir "$testfs"/src/foo
-            WVPASS touch "$testfs"/src/bar
-            WVPASS bup-python -c "from bup import xstat; \
-                x = xstat.timespec_to_nsecs((42, 0));\
-                xstat.utime(b'$testfs/src/foo', (x, x));\
-                xstat.utime(b'$testfs/src/bar', (x, x));"
-            WVPASS cd "$testfs"
-            WVPASS bup meta -v --create --recurse --file src.meta src
-            WVPASS bup meta -tvf src.meta
-            # Test extract.
-            WVPASS force-delete src-restore
-            WVPASS mkdir src-restore
-            WVPASS cd src-restore
-            WVPASS bup meta --extract --file ../src.meta
-            WVPASSEQ "$(bup xstat --include-fields=atime src/foo)" "atime: 42"
-            WVPASSEQ "$(bup xstat --include-fields=atime src/bar)" "atime: 42"
-            # Test start/finish extract.
-            WVPASS force-delete src
-            WVPASS bup meta --start-extract --file ../src.meta
-            WVPASS test -d src
-            WVPASS bup meta --finish-extract --file ../src.meta
-            WVPASSEQ "$(bup xstat --include-fields=atime src/foo)" "atime: 42"
-            WVPASSEQ "$(bup xstat --include-fields=atime src/bar)" "atime: 42"
-        ) || exit $?
-
-        WVSTART 'meta - Linux attr (as root)'
-        WVPASS force-delete "$testfs"/src
-        WVPASS mkdir "$testfs"/src
-        (
-            WVPASS touch "$testfs"/src/foo
-            WVPASS mkdir "$testfs"/src/bar
-            WVPASS chattr +acdeijstuADS "$testfs"/src/foo
-            WVPASS chattr +acdeijstuADST "$testfs"/src/bar
-            (WVPASS cd "$testfs"; WVPASS test-src-create-extract) || exit $?
-            # Test restoration to a limited filesystem (vfat).
-            (
-                WVPASS bup meta --create --recurse --file "$testfs"/src.meta \
-                    "$testfs"/src
-                WVPASS force-delete "$testfs_limited"/src-restore
-                WVPASS mkdir "$testfs_limited"/src-restore
-                WVPASS cd "$testfs_limited"/src-restore
-                WVFAIL bup meta --extract --file "$testfs"/src.meta 2>&1 \
-                    | WVPASS grep -e '^Linux chattr:' \
-                    | WVPASS bup-cfg-py -c \
-                    'import sys; exit(not len(sys.stdin.readlines()) == 3)'
-            ) || exit $?
-        ) || exit $?
-
-        WVSTART 'meta - Linux xattr (as root)'
-        WVPASS force-delete "$testfs"/src
-        WVPASS mkdir "$testfs"/src
-        WVPASS touch "$testfs"/src/foo
-        WVPASS mkdir "$testfs"/src/bar
-        WVPASS attr -s foo -V bar "$testfs"/src/foo
-        WVPASS attr -s foo -V bar "$testfs"/src/bar
-        (WVPASS cd "$testfs"; WVPASS test-src-create-extract) || exit $?
-
-        # Test restoration to a limited filesystem (vfat).
-        (
-            WVPASS bup meta --create --recurse --file "$testfs"/src.meta \
-                "$testfs"/src
-            WVPASS force-delete "$testfs_limited"/src-restore
-            WVPASS mkdir "$testfs_limited"/src-restore
-            WVPASS cd "$testfs_limited"/src-restore
-            WVFAIL bup meta --extract --file "$testfs"/src.meta
-            WVFAIL bup meta --extract --file "$testfs"/src.meta 2>&1 \
-                | WVPASS grep -e "^xattr\.set u\?'" \
-                | WVPASS bup-cfg-py -c \
-                'import sys; exit(not len(sys.stdin.readlines()) == 2)'
-        ) || exit $?
-
-        WVSTART 'meta - POSIX.1e ACLs (as root)'
-        WVPASS force-delete "$testfs"/src
-        WVPASS mkdir "$testfs"/src
-        WVPASS touch "$testfs"/src/foo
-        WVPASS mkdir "$testfs"/src/bar
-        WVPASS setfacl -m u:root:r "$testfs"/src/foo
-        WVPASS setfacl -m u:root:r "$testfs"/src/bar
-        (WVPASS cd "$testfs"; WVPASS test-src-create-extract) || exit $?
-
-        # Test restoration to a limited filesystem (vfat).
-        (
-            WVPASS bup meta --create --recurse --file "$testfs"/src.meta \
-                "$testfs"/src
-            WVPASS force-delete "$testfs_limited"/src-restore
-            WVPASS mkdir "$testfs_limited"/src-restore
-            WVPASS cd "$testfs_limited"/src-restore
-            WVFAIL bup meta --extract --file "$testfs"/src.meta 2>&1 \
-                | WVPASS grep -e '^POSIX1e ACL applyto:' \
-                | WVPASS bup-cfg-py -c \
-                'import sys; exit(not len(sys.stdin.readlines()) == 2)'
-        ) || exit $?
-
-        WVPASS umount "$testfs"
-        WVPASS umount "$testfs_limited"
-        WVPASS rm -r "$testfs" "$testfs_limited"
-
-        WVPASS rm -r "$tmpdir"
-
-    ) || exit $?
-fi
-
-WVPASS rm -r "$tmpdir"
diff --git a/test/ext/test-on b/test/ext/test-on
new file mode 100755 (executable)
index 0000000..a4384c3
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. ./dev/lib.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+compare-trees() { "$top/dev/compare-trees" "$@"; }
+
+WVPASS bup init
+WVPASS cd "$tmpdir"
+
+WVSTART "index/save"
+WVPASS mkdir src src/foo
+WVPASS date > src/bar
+WVPASS bup random 1k > src/baz
+WVPASS bup on - index src
+WVPASS bup on - save -ctn src src > get.log
+WVPASSEQ $(WVPASS cat get.log | WVPASS wc -l) 2
+tree_id=$(WVPASS awk 'FNR == 1' get.log) || exit $?
+commit_id=$(WVPASS awk 'FNR == 2' get.log) || exit $?
+WVPASS git ls-tree "$tree_id"
+WVPASS git cat-file commit "$commit_id" | head -n 1 \
+    | WVPASS grep "^tree $tree_id\$"
+
+WVPASS bup restore -C restore "src/latest/$(pwd)/src/."
+WVPASS compare-trees src/ restore/
+WVPASS rm -r restore
+
+WVSTART "split"
+WVPASS bup on - split -ctn baz src/baz > get.log
+tree_id=$(WVPASS awk 'FNR == 1' get.log) || exit $?
+commit_id=$(WVPASS awk 'FNR == 2' get.log) || exit $?
+WVPASS git ls-tree "$tree_id"
+WVPASS git cat-file commit "$commit_id" | head -n 1 \
+    | WVPASS grep "^tree $tree_id\$"
+WVPASS bup join baz > restore-baz
+WVPASS cmp src/baz restore-baz
+
+WVSTART "index-cache"
+# the 'a-zA-Z0-9_' is '\w' from python,
+# the trailing _ is because there's no dir specified
+# and that should thus be empty
+hostname=$(uname -n)
+idxcache=$(echo "$hostname" | sed 's/[^@a-zA-Z0-9_]/_/g')_
+# there should be an index-cache now
+for idx in "$tmpdir"/bup/objects/pack/*.idx ; do
+    cachedidx="$tmpdir/bup/index-cache/$idxcache/$(basename "$idx")"
+    WVPASS cmp "$idx" "$cachedidx"
+done
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-on.sh b/test/ext/test-on.sh
deleted file mode 100755 (executable)
index a4384c3..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. ./dev/lib.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-compare-trees() { "$top/dev/compare-trees" "$@"; }
-
-WVPASS bup init
-WVPASS cd "$tmpdir"
-
-WVSTART "index/save"
-WVPASS mkdir src src/foo
-WVPASS date > src/bar
-WVPASS bup random 1k > src/baz
-WVPASS bup on - index src
-WVPASS bup on - save -ctn src src > get.log
-WVPASSEQ $(WVPASS cat get.log | WVPASS wc -l) 2
-tree_id=$(WVPASS awk 'FNR == 1' get.log) || exit $?
-commit_id=$(WVPASS awk 'FNR == 2' get.log) || exit $?
-WVPASS git ls-tree "$tree_id"
-WVPASS git cat-file commit "$commit_id" | head -n 1 \
-    | WVPASS grep "^tree $tree_id\$"
-
-WVPASS bup restore -C restore "src/latest/$(pwd)/src/."
-WVPASS compare-trees src/ restore/
-WVPASS rm -r restore
-
-WVSTART "split"
-WVPASS bup on - split -ctn baz src/baz > get.log
-tree_id=$(WVPASS awk 'FNR == 1' get.log) || exit $?
-commit_id=$(WVPASS awk 'FNR == 2' get.log) || exit $?
-WVPASS git ls-tree "$tree_id"
-WVPASS git cat-file commit "$commit_id" | head -n 1 \
-    | WVPASS grep "^tree $tree_id\$"
-WVPASS bup join baz > restore-baz
-WVPASS cmp src/baz restore-baz
-
-WVSTART "index-cache"
-# the 'a-zA-Z0-9_' is '\w' from python,
-# the trailing _ is because there's no dir specified
-# and that should thus be empty
-hostname=$(uname -n)
-idxcache=$(echo "$hostname" | sed 's/[^@a-zA-Z0-9_]/_/g')_
-# there should be an index-cache now
-for idx in "$tmpdir"/bup/objects/pack/*.idx ; do
-    cachedidx="$tmpdir/bup/index-cache/$idxcache/$(basename "$idx")"
-    WVPASS cmp "$idx" "$cachedidx"
-done
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-redundant-saves b/test/ext/test-redundant-saves
new file mode 100755 (executable)
index 0000000..e89fbdb
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+
+# Test that running save more than once with no other changes produces
+# the exact same tree.
+
+# Note: we can't compare the top-level hash (i.e. the output of "save
+# -t" because that currently pulls the metadata for unindexed parent
+# directories directly from the filesystem, and the relevant atimes
+# may change between runs.  So instead we extract the roots of the
+# indexed trees for comparison via dev/subtree-hash.
+
+. ./wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+WVSTART 'all'
+
+top="$(pwd)"
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$BUP_DIR"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS mkdir -p "$tmpdir/src"
+WVPASS mkdir -p "$tmpdir/src/d"
+WVPASS mkdir -p "$tmpdir/src/d/e"
+WVPASS touch "$tmpdir/src/"{f,b,a,d}
+WVPASS touch "$tmpdir/src/d/z"
+
+WVPASS bup init
+WVPASS bup index -u "$tmpdir/src"
+
+declare -a indexed_top
+IFS=/
+indexed_top="${tmpdir##/}"
+indexed_top=(${indexed_top%%/})
+unset IFS
+
+tree1=$(WVPASS bup save -t "$tmpdir/src") || exit $?
+indexed_tree1="$(WVPASS dev/subtree-hash "$tree1" "${indexed_top[@]}" src)" \
+    || exit $?
+
+result="$(WVPASS cd "$tmpdir/src"; WVPASS bup index -m)" || exit $?
+WVPASSEQ "$result" ""
+
+tree2=$(WVPASS bup save -t "$tmpdir/src") || exit $?
+indexed_tree2="$(WVPASS dev/subtree-hash "$tree2" "${indexed_top[@]}" src)" \
+    || exit $?
+
+WVPASSEQ "$indexed_tree1" "$indexed_tree2"
+
+result="$(WVPASS bup index -s / | WVFAIL grep ^D)" || exit $?
+WVPASSEQ "$result" ""
+
+tree3=$(WVPASS bup save -t /) || exit $?
+indexed_tree3="$(WVPASS dev/subtree-hash "$tree3" "${indexed_top[@]}" src)" || exit $?
+WVPASSEQ "$indexed_tree1" "$indexed_tree3"
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-redundant-saves.sh b/test/ext/test-redundant-saves.sh
deleted file mode 100755 (executable)
index e89fbdb..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env bash
-
-# Test that running save more than once with no other changes produces
-# the exact same tree.
-
-# Note: we can't compare the top-level hash (i.e. the output of "save
-# -t" because that currently pulls the metadata for unindexed parent
-# directories directly from the filesystem, and the relevant atimes
-# may change between runs.  So instead we extract the roots of the
-# indexed trees for comparison via dev/subtree-hash.
-
-. ./wvtest-bup.sh || exit $?
-
-set -o pipefail
-
-WVSTART 'all'
-
-top="$(pwd)"
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$BUP_DIR"
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS mkdir -p "$tmpdir/src"
-WVPASS mkdir -p "$tmpdir/src/d"
-WVPASS mkdir -p "$tmpdir/src/d/e"
-WVPASS touch "$tmpdir/src/"{f,b,a,d}
-WVPASS touch "$tmpdir/src/d/z"
-
-WVPASS bup init
-WVPASS bup index -u "$tmpdir/src"
-
-declare -a indexed_top
-IFS=/
-indexed_top="${tmpdir##/}"
-indexed_top=(${indexed_top%%/})
-unset IFS
-
-tree1=$(WVPASS bup save -t "$tmpdir/src") || exit $?
-indexed_tree1="$(WVPASS dev/subtree-hash "$tree1" "${indexed_top[@]}" src)" \
-    || exit $?
-
-result="$(WVPASS cd "$tmpdir/src"; WVPASS bup index -m)" || exit $?
-WVPASSEQ "$result" ""
-
-tree2=$(WVPASS bup save -t "$tmpdir/src") || exit $?
-indexed_tree2="$(WVPASS dev/subtree-hash "$tree2" "${indexed_top[@]}" src)" \
-    || exit $?
-
-WVPASSEQ "$indexed_tree1" "$indexed_tree2"
-
-result="$(WVPASS bup index -s / | WVFAIL grep ^D)" || exit $?
-WVPASSEQ "$result" ""
-
-tree3=$(WVPASS bup save -t /) || exit $?
-indexed_tree3="$(WVPASS dev/subtree-hash "$tree3" "${indexed_top[@]}" src)" || exit $?
-WVPASSEQ "$indexed_tree1" "$indexed_tree3"
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-release-archive b/test/ext/test-release-archive
new file mode 100755 (executable)
index 0000000..4c8c378
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+bup_make=$(< config/config.var/bup-make)
+
+WVPASS git status > /dev/null
+
+if ! git diff-index --quiet HEAD; then
+    WVDIE "uncommitted changes; cannot continue"
+fi
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS cd "$tmpdir"
+
+WVPASS git clone "$top" clone
+
+for ver in 11.11 11.11.11; do
+    WVSTART "version $ver"
+    WVPASS cd clone
+    WVPASS git tag "$ver"
+    WVPASS git archive --prefix=bup-"$ver"/ -o "$tmpdir"/bup-"$ver".tgz "$ver"
+    WVPASS cd "$tmpdir"
+    WVPASS tar xzf bup-"$ver".tgz
+    WVPASS cd bup-"$ver"
+    WVPASS "$bup_make"
+    WVPASSEQ "$ver" "$(./bup version)"
+    WVPASS cd "$tmpdir"
+done
+
+WVSTART 'make check in unpacked archive'
+WVPASS cd bup-11.11.11
+if ! "$bup_make" -j5 check > archive-tests.log 2>&1; then
+    cat archive-tests.log 1>&2
+    WVPASS false
+fi
+
+WVPASS cd "$top"
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-release-archive.sh b/test/ext/test-release-archive.sh
deleted file mode 100755 (executable)
index 4c8c378..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-bup_make=$(< config/config.var/bup-make)
-
-WVPASS git status > /dev/null
-
-if ! git diff-index --quiet HEAD; then
-    WVDIE "uncommitted changes; cannot continue"
-fi
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS cd "$tmpdir"
-
-WVPASS git clone "$top" clone
-
-for ver in 11.11 11.11.11; do
-    WVSTART "version $ver"
-    WVPASS cd clone
-    WVPASS git tag "$ver"
-    WVPASS git archive --prefix=bup-"$ver"/ -o "$tmpdir"/bup-"$ver".tgz "$ver"
-    WVPASS cd "$tmpdir"
-    WVPASS tar xzf bup-"$ver".tgz
-    WVPASS cd bup-"$ver"
-    WVPASS "$bup_make"
-    WVPASSEQ "$ver" "$(./bup version)"
-    WVPASS cd "$tmpdir"
-done
-
-WVSTART 'make check in unpacked archive'
-WVPASS cd bup-11.11.11
-if ! "$bup_make" -j5 check > archive-tests.log 2>&1; then
-    cat archive-tests.log 1>&2
-    WVPASS false
-fi
-
-WVPASS cd "$top"
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-restore-map-owner b/test/ext/test-restore-map-owner
new file mode 100755 (executable)
index 0000000..348ce3c
--- /dev/null
@@ -0,0 +1,107 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+root_status="$(dev/root-status)" || exit $?
+
+if [ "$root_status" != root ]; then
+    echo 'Not root: skipping restore --map-* tests.'
+    exit 0 # FIXME: add WVSKIP.
+fi
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+export BUP_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+uid=$(WVPASS id -u) || exit $?
+user=$(WVPASS id -un) || exit $?
+gid=$(WVPASS id -g) || exit $?
+group=$(WVPASS id -gn) || exit $?
+
+other_uinfo=$(WVPASS dev/id-other-than --user "$user") || exit $?
+other_user="${other_uinfo%%:*}"
+other_uid="${other_uinfo##*:}"
+
+other_ginfo=$(WVPASS dev/id-other-than --group "$group" 0) || exit $?
+other_group="${other_ginfo%%:*}"
+other_gid="${other_ginfo##*:}"
+
+WVPASS bup init
+WVPASS cd "$tmpdir"
+
+WVSTART "restore --map-user/group/uid/gid (control)"
+WVPASS mkdir src
+WVPASS touch src/foo
+# Some systems assign the parent dir group to new paths.
+WVPASS chgrp -R "$group" src
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS bup restore -C dest "src/latest/$(pwd)/src/"
+WVPASS bup xstat dest/foo > foo-xstat
+WVPASS grep -qE "^user: $user\$" foo-xstat
+WVPASS grep -qE "^uid: $uid\$" foo-xstat
+WVPASS grep -qE "^group: $group\$" foo-xstat
+WVPASS grep -qE "^gid: $gid\$" foo-xstat
+
+WVSTART "restore --map-user/group/uid/gid (user/group)"
+WVPASS rm -rf dest
+# Have to remap uid/gid too because we're root and 0 would win).
+WVPASS bup restore -C dest \
+    --map-uid "$uid=$other_uid" --map-gid "$gid=$other_gid" \
+    --map-user "$user=$other_user" --map-group "$group=$other_group" \
+    "src/latest/$(pwd)/src/"
+WVPASS bup xstat dest/foo > foo-xstat
+WVPASS grep -qE "^user: $other_user\$" foo-xstat
+WVPASS grep -qE "^uid: $other_uid\$" foo-xstat
+WVPASS grep -qE "^group: $other_group\$" foo-xstat
+WVPASS grep -qE "^gid: $other_gid\$" foo-xstat
+
+WVSTART "restore --map-user/group/uid/gid (user/group trumps uid/gid)"
+WVPASS rm -rf dest
+WVPASS bup restore -C dest \
+    --map-uid "$uid=$other_uid" --map-gid "$gid=$other_gid" \
+    "src/latest/$(pwd)/src/"
+# Should be no changes.
+WVPASS bup xstat dest/foo > foo-xstat
+WVPASS grep -qE "^user: $user\$" foo-xstat
+WVPASS grep -qE "^uid: $uid\$" foo-xstat
+WVPASS grep -qE "^group: $group\$" foo-xstat
+WVPASS grep -qE "^gid: $gid\$" foo-xstat
+
+WVSTART "restore --map-user/group/uid/gid (uid/gid)"
+WVPASS rm -rf dest
+WVPASS bup restore -C dest \
+    --map-user "$user=" --map-group "$group=" \
+    --map-uid "$uid=$other_uid" --map-gid "$gid=$other_gid" \
+    "src/latest/$(pwd)/src/"
+WVPASS bup xstat dest/foo > foo-xstat
+WVPASS grep -qE "^user: $other_user\$" foo-xstat
+WVPASS grep -qE "^uid: $other_uid\$" foo-xstat
+WVPASS grep -qE "^group: $other_group\$" foo-xstat
+WVPASS grep -qE "^gid: $other_gid\$" foo-xstat
+
+has_uid_gid_0=$(WVPASS bup-cfg-py -c "
+import grp, pwd
+try:
+  pwd.getpwuid(0)
+  grp.getgrgid(0)
+  print('yes')
+except KeyError as ex:
+  pass
+") || exit $?
+if [ "$has_uid_gid_0" == yes ]
+then
+    WVSTART "restore --map-user/group/uid/gid (zero uid/gid trumps all)"
+    WVPASS rm -rf dest
+    WVPASS bup restore -C dest \
+        --map-user "$user=$other_user" --map-group "$group=$other_group" \
+        --map-uid "$uid=0" --map-gid "$gid=0" \
+        "src/latest/$(pwd)/src/"
+    WVPASS bup xstat dest/foo > foo-xstat
+    WVPASS grep -qE "^uid: 0\$" foo-xstat
+    WVPASS grep -qE "^gid: 0\$" foo-xstat
+
+    WVPASS rm -rf "$tmpdir"
+fi
diff --git a/test/ext/test-restore-map-owner.sh b/test/ext/test-restore-map-owner.sh
deleted file mode 100755 (executable)
index 348ce3c..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-root_status="$(dev/root-status)" || exit $?
-
-if [ "$root_status" != root ]; then
-    echo 'Not root: skipping restore --map-* tests.'
-    exit 0 # FIXME: add WVSKIP.
-fi
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-export BUP_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-uid=$(WVPASS id -u) || exit $?
-user=$(WVPASS id -un) || exit $?
-gid=$(WVPASS id -g) || exit $?
-group=$(WVPASS id -gn) || exit $?
-
-other_uinfo=$(WVPASS dev/id-other-than --user "$user") || exit $?
-other_user="${other_uinfo%%:*}"
-other_uid="${other_uinfo##*:}"
-
-other_ginfo=$(WVPASS dev/id-other-than --group "$group" 0) || exit $?
-other_group="${other_ginfo%%:*}"
-other_gid="${other_ginfo##*:}"
-
-WVPASS bup init
-WVPASS cd "$tmpdir"
-
-WVSTART "restore --map-user/group/uid/gid (control)"
-WVPASS mkdir src
-WVPASS touch src/foo
-# Some systems assign the parent dir group to new paths.
-WVPASS chgrp -R "$group" src
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS bup restore -C dest "src/latest/$(pwd)/src/"
-WVPASS bup xstat dest/foo > foo-xstat
-WVPASS grep -qE "^user: $user\$" foo-xstat
-WVPASS grep -qE "^uid: $uid\$" foo-xstat
-WVPASS grep -qE "^group: $group\$" foo-xstat
-WVPASS grep -qE "^gid: $gid\$" foo-xstat
-
-WVSTART "restore --map-user/group/uid/gid (user/group)"
-WVPASS rm -rf dest
-# Have to remap uid/gid too because we're root and 0 would win).
-WVPASS bup restore -C dest \
-    --map-uid "$uid=$other_uid" --map-gid "$gid=$other_gid" \
-    --map-user "$user=$other_user" --map-group "$group=$other_group" \
-    "src/latest/$(pwd)/src/"
-WVPASS bup xstat dest/foo > foo-xstat
-WVPASS grep -qE "^user: $other_user\$" foo-xstat
-WVPASS grep -qE "^uid: $other_uid\$" foo-xstat
-WVPASS grep -qE "^group: $other_group\$" foo-xstat
-WVPASS grep -qE "^gid: $other_gid\$" foo-xstat
-
-WVSTART "restore --map-user/group/uid/gid (user/group trumps uid/gid)"
-WVPASS rm -rf dest
-WVPASS bup restore -C dest \
-    --map-uid "$uid=$other_uid" --map-gid "$gid=$other_gid" \
-    "src/latest/$(pwd)/src/"
-# Should be no changes.
-WVPASS bup xstat dest/foo > foo-xstat
-WVPASS grep -qE "^user: $user\$" foo-xstat
-WVPASS grep -qE "^uid: $uid\$" foo-xstat
-WVPASS grep -qE "^group: $group\$" foo-xstat
-WVPASS grep -qE "^gid: $gid\$" foo-xstat
-
-WVSTART "restore --map-user/group/uid/gid (uid/gid)"
-WVPASS rm -rf dest
-WVPASS bup restore -C dest \
-    --map-user "$user=" --map-group "$group=" \
-    --map-uid "$uid=$other_uid" --map-gid "$gid=$other_gid" \
-    "src/latest/$(pwd)/src/"
-WVPASS bup xstat dest/foo > foo-xstat
-WVPASS grep -qE "^user: $other_user\$" foo-xstat
-WVPASS grep -qE "^uid: $other_uid\$" foo-xstat
-WVPASS grep -qE "^group: $other_group\$" foo-xstat
-WVPASS grep -qE "^gid: $other_gid\$" foo-xstat
-
-has_uid_gid_0=$(WVPASS bup-cfg-py -c "
-import grp, pwd
-try:
-  pwd.getpwuid(0)
-  grp.getgrgid(0)
-  print('yes')
-except KeyError as ex:
-  pass
-") || exit $?
-if [ "$has_uid_gid_0" == yes ]
-then
-    WVSTART "restore --map-user/group/uid/gid (zero uid/gid trumps all)"
-    WVPASS rm -rf dest
-    WVPASS bup restore -C dest \
-        --map-user "$user=$other_user" --map-group "$group=$other_group" \
-        --map-uid "$uid=0" --map-gid "$gid=0" \
-        "src/latest/$(pwd)/src/"
-    WVPASS bup xstat dest/foo > foo-xstat
-    WVPASS grep -qE "^uid: 0\$" foo-xstat
-    WVPASS grep -qE "^gid: 0\$" foo-xstat
-
-    WVPASS rm -rf "$tmpdir"
-fi
diff --git a/test/ext/test-restore-single-file b/test/ext/test-restore-single-file
new file mode 100755 (executable)
index 0000000..c151327
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+WVSTART 'all'
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+export BUP_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS mkdir "$tmpdir/foo"
+WVPASS mkdir "$tmpdir/foo/bar" # Make sure a dir sorts before baz (regression test).
+WVPASS touch "$tmpdir/foo/baz"
+WVPASS WVPASS bup init
+WVPASS WVPASS bup index "$tmpdir/foo"
+WVPASS bup save -n foo "$tmpdir/foo"
+# Make sure the timestamps will differ if metadata isn't being restored.
+WVPASS bup tick
+WVPASS bup restore -C "$tmpdir/restore" "foo/latest/$tmpdir/foo/baz"
+WVPASS "$top/dev/compare-trees" "$tmpdir/foo/baz" "$tmpdir/restore/baz"
+
+WVPASS rm -rf "$tmpdir"
+
diff --git a/test/ext/test-restore-single-file.sh b/test/ext/test-restore-single-file.sh
deleted file mode 100755 (executable)
index c151327..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-
-set -o pipefail
-
-WVSTART 'all'
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-export BUP_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS mkdir "$tmpdir/foo"
-WVPASS mkdir "$tmpdir/foo/bar" # Make sure a dir sorts before baz (regression test).
-WVPASS touch "$tmpdir/foo/baz"
-WVPASS WVPASS bup init
-WVPASS WVPASS bup index "$tmpdir/foo"
-WVPASS bup save -n foo "$tmpdir/foo"
-# Make sure the timestamps will differ if metadata isn't being restored.
-WVPASS bup tick
-WVPASS bup restore -C "$tmpdir/restore" "foo/latest/$tmpdir/foo/baz"
-WVPASS "$top/dev/compare-trees" "$tmpdir/foo/baz" "$tmpdir/restore/baz"
-
-WVPASS rm -rf "$tmpdir"
-
diff --git a/test/ext/test-rm b/test/ext/test-rm
new file mode 100755 (executable)
index 0000000..3aca68d
--- /dev/null
@@ -0,0 +1,256 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. ./dev/lib.sh || exit $?
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+
+bup() { "$top/bup" "$@"; }
+compare-trees() { "$top/dev/compare-trees" "$@"; }
+
+wv_matches_rx()
+{
+    local caller_file=${BASH_SOURCE[0]}
+    local caller_line=${BASH_LINENO[0]}
+    local src="$caller_file:$caller_line"
+    if test $# -ne 2; then
+        echo "! $src wv_matches_rx requires 2 arguments FAILED" 1>&2
+        return
+    fi
+    local str="$1"
+    local rx="$2"
+    echo "Matching:" 1>&2 || exit $?
+    echo "$str" | sed 's/^\(.*\)/  \1/' 1>&2 || exit $?
+    echo "Against:" 1>&2 || exit $?
+    echo "$rx" | sed 's/^\(.*\)/  \1/' 1>&2 || exit $?
+    if [[ "$str" =~ ^${rx}$ ]]; then
+        echo "! $src regex matches ok" 1>&2 || exit $?
+    else
+        echo "! $src regex doesn't match FAILED" 1>&2 || exit $?
+    fi
+}
+
+
+WVPASS bup init
+WVPASS cd "$tmpdir"
+
+
+WVSTART "rm /foo (lone branch)"
+WVPASS mkdir src src/foo
+WVPASS echo twisty-maze > src/1
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS "$top"/dev/sync-tree bup/ bup-baseline/
+# FIXME: test -n
+WVPASS bup tick # Make sure we always get the timestamp changes below
+WVPASS bup rm --unsafe /src
+observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
+wv_matches_rx "$observed" \
+'\*deleting[ ]+logs/refs/heads/src
+\*deleting[ ]+refs/heads/src(
+\.d\.\.t\.\.\.[.]*[ ]+\./)?
+\.d\.\.t\.\.\.[.]*[ ]+logs/refs/heads/
+\.d\.\.t\.\.\.[.]*[ ]+refs/heads/(
+>f\+\+\+\+\+\+\+\+\+[ ]+packed-refs)?'
+
+
+WVSTART "rm /foo (one of many)"
+WVPASS rm -rf bup
+WVPASS mv bup-baseline bup
+WVPASS echo twisty-maze > src/2
+WVPASS bup index src
+WVPASS bup save -n src-2 src
+WVPASS echo twisty-maze > src/3
+WVPASS bup index src
+WVPASS bup save -n src-3 src
+WVPASS "$top"/dev/sync-tree bup/ bup-baseline/
+WVPASS bup tick # Make sure we always get the timestamp changes below
+WVPASS bup rm --unsafe /src
+observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
+wv_matches_rx "$observed" \
+"\*deleting[ ]+logs/refs/heads/src
+\*deleting[ ]+refs/heads/src(
+\.d\.\.t\.\.\.[.]*[ ]+\./)?
+\.d\.\.t\.\.\.[.]*[ ]+logs/refs/heads/
+\.d\.\.t\.\.\.[.]*[ ]+refs/heads/(
+>f\+\+\+\+\+\+\+\+\+[ ]+packed-refs)?"
+
+
+WVSTART "rm /foo /bar (multiple of many)"
+WVPASS rm -rf bup
+WVPASS mv bup-baseline bup
+WVPASS echo twisty-maze > src/4
+WVPASS bup index src
+WVPASS bup save -n src-4 src
+WVPASS echo twisty-maze > src/5
+WVPASS bup index src
+WVPASS bup save -n src-5 src
+WVPASS "$top"/dev/sync-tree bup/ bup-baseline/
+WVPASS bup tick # Make sure we always get the timestamp changes below
+WVPASS bup rm --unsafe /src-2 /src-4
+observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
+wv_matches_rx "$observed" \
+"\*deleting[ ]+logs/refs/heads/src-2
+\*deleting[ ]+logs/refs/heads/src-4
+\*deleting[ ]+refs/heads/src-2
+\*deleting[ ]+refs/heads/src-4(
+\.d\.\.t\.\.\.[.]*[ ]+\./)?
+\.d\.\.t\.\.\.[.]*[ ]+logs/refs/heads/
+\.d\.\.t\.\.\.[.]*[ ]+refs/heads/(
+>f\+\+\+\+\+\+\+\+\+[ ]+packed-refs)?"
+
+
+WVSTART "rm /foo /bar (all)"
+WVPASS rm -rf bup
+WVPASS mv bup-baseline bup
+WVPASS "$top"/dev/sync-tree bup/ bup-baseline/
+WVPASS bup tick # Make sure we always get the timestamp changes below
+WVPASS bup rm --unsafe /src /src-2 /src-3 /src-4 /src-5
+observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
+wv_matches_rx "$observed" \
+"\*deleting[ ]+logs/refs/heads/src
+\*deleting[ ]+logs/refs/heads/src-2
+\*deleting[ ]+logs/refs/heads/src-3
+\*deleting[ ]+logs/refs/heads/src-4
+\*deleting[ ]+logs/refs/heads/src-5
+\*deleting[ ]+refs/heads/src
+\*deleting[ ]+refs/heads/src-2
+\*deleting[ ]+refs/heads/src-3
+\*deleting[ ]+refs/heads/src-4
+\*deleting[ ]+refs/heads/src-5(
+\.d\.\.t\.\.\.[.]*[ ]+\./)?
+\.d\.\.t\.\.\.[.]*[ ]+logs/refs/heads/
+\.d\.\.t\.\.\.[.]*[ ]+refs/heads/(
+>f\+\+\+\+\+\+\+\+\+[ ]+packed-refs)?"
+
+
+WVSTART "rm /foo/bar (lone save - equivalent to rm /foo)"
+WVPASS rm -rf bup bup-baseline src
+WVPASS bup init
+WVPASS mkdir src
+WVPASS echo twisty-maze > src/1
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS bup ls src > tmp-ls
+save1="$(WVPASS head -n 1 tmp-ls)" || exit $?
+WVPASS "$top"/dev/sync-tree bup/ bup-baseline/
+WVPASS bup tick # Make sure we always get the timestamp changes below
+WVFAIL bup rm --unsafe /src/latest
+WVPASS bup rm --unsafe /src/"$save1"
+observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
+wv_matches_rx "$observed" \
+"\*deleting[ ]+logs/refs/heads/src
+\*deleting[ ]+refs/heads/src(
+\.d\.\.t\.\.\.[.]*[ ]+\./)?
+\.d\.\.t\.\.\.[.]*[ ]+logs/refs/heads/
+\.d\.\.t\.\.\.[.]*[ ]+refs/heads/(
+>f\+\+\+\+\+\+\+\+\+[ ]+packed-refs)?"
+
+
+verify-changes-caused-by-rewriting-save()
+{
+    local before="$1" after="$2" tmpdir
+    tmpdir="$(WVPASS wvmktempdir)" || exit $?
+    (WVPASS cd "$before" && WVPASS find . | WVPASS sort) \
+        > "$tmpdir/before" || exit $?
+    (WVPASS cd "$after" && WVPASS find . | WVPASS sort) \
+        > "$tmpdir/after" || exit $?
+    local new_paths new_idx new_pack observed
+    new_paths="$(WVPASS comm -13 "$tmpdir/before" "$tmpdir/after")" || exit $?
+    new_idx="$(echo "$new_paths" | WVPASS grep -E '^\./objects/pack/pack-.*\.idx$' | cut -b 3-)" || exit $?
+    new_pack="$(echo "$new_paths" | WVPASS grep -E '^\./objects/pack/pack-.*\.pack$' | cut -b 3-)" || exit $?
+    wv_matches_rx "$(compare-trees "$after/" "$before/")" \
+">fcst\.\.\.[.]*[ ]+logs/refs/heads/src
+\.d\.\.t\.\.\.[.]*[ ]+objects/
+\.d\.\.t\.\.\.[.]*[ ]+objects/pack/
+>fcst\.\.\.[.]*[ ]+objects/pack/bup\.bloom
+>f\+\+\+\+\+\+\+[+]*[ ]+$new_idx
+>f\+\+\+\+\+\+\+[+]*[ ]+$new_pack
+\.d\.\.t\.\.\.[.]*[ ]+refs/heads/
+>fc\.t\.\.\.[.]*[ ]+refs/heads/src"
+    WVPASS rm -rf "$tmpdir"
+}
+
+commit-hash-n()
+{
+    local n="$1" repo="$2" branch="$3"
+    GIT_DIR="$repo" WVPASS git rev-list --reverse "$branch" \
+        | WVPASS awk "FNR == $n"
+}
+
+rm-safe-cinfo()
+{
+    local n="$1" repo="$2" branch="$3" hash
+    hash="$(commit-hash-n "$n" "$repo" "$branch")" || exit $?
+    local fmt='Tree: %T%n'
+    fmt="${fmt}Author: %an <%ae> %ai%n"
+    fmt="${fmt}Committer: %cn <%ce> %ci%n"
+    fmt="${fmt}%n%s%n%b"
+    GIT_DIR="$repo" WVPASS git log -n1 --pretty=format:"$fmt" "$hash"
+}
+
+
+WVSTART 'rm /foo/BAR (setup)'
+WVPASS rm -rf bup bup-baseline src
+WVPASS bup init
+WVPASS mkdir src
+WVPASS echo twisty-maze > src/1
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS echo twisty-maze > src/2
+WVPASS bup index src
+WVPASS bup tick
+WVPASS bup save -n src src
+WVPASS echo twisty-maze > src/3
+WVPASS bup index src
+WVPASS bup tick
+WVPASS bup save -n src src
+WVPASS mv bup bup-baseline
+WVPASS bup tick # Make sure we always get the timestamp changes below
+
+
+WVSTART "rm /foo/BAR (first of many)"
+WVPASS "$top"/dev/sync-tree bup-baseline/ bup/
+WVPASS bup ls src > tmp-ls
+victim="$(WVPASS head -n 1 tmp-ls)" || exit $?
+WVPASS bup rm --unsafe /src/"$victim"
+verify-changes-caused-by-rewriting-save bup-baseline bup
+observed=$(WVPASS git rev-list src | WVPASS wc -l) || exit $?
+WVPASSEQ 2 $observed
+WVPASSEQ "$(rm-safe-cinfo 1 bup src)" "$(rm-safe-cinfo 2 bup-baseline src)"
+WVPASSEQ "$(rm-safe-cinfo 2 bup src)" "$(rm-safe-cinfo 3 bup-baseline src)"
+
+
+WVSTART "rm /foo/BAR (one of many)"
+WVPASS "$top"/dev/sync-tree bup-baseline/ bup/
+victim="$(WVPASS bup ls src | tail -n +2 | head -n 1)" || exit $?
+WVPASS bup rm --unsafe /src/"$victim"
+verify-changes-caused-by-rewriting-save bup-baseline bup
+observed=$(git rev-list src | wc -l) || exit $?
+WVPASSEQ 2 $observed
+WVPASSEQ "$(commit-hash-n 1 bup src)" "$(commit-hash-n 1 bup-baseline src)"
+WVPASSEQ "$(rm-safe-cinfo 2 bup src)" "$(rm-safe-cinfo 3 bup-baseline src)"
+
+
+WVSTART "rm /foo/BAR (last of many)"
+WVPASS "$top"/dev/sync-tree bup-baseline/ bup/
+victim="$(WVPASS bup ls src | tail -n 2 | head -n 1)" || exit $?
+WVPASS bup rm --unsafe -vv /src/"$victim"
+observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
+wv_matches_rx "$observed" \
+"\.d\.\.t\.\.\.[.]*[ ]+refs/heads/
+>fc\.t\.\.\.[.]*[ ]+refs/heads/src
+>fcst\.\.\.[.]*[ ]+logs/refs/heads/src"
+observed=$(git rev-list src | wc -l) || exit $?
+WVPASSEQ 2 $observed
+WVPASSEQ "$(commit-hash-n 1 bup src)" "$(commit-hash-n 1 bup-baseline src)"
+WVPASSEQ "$(commit-hash-n 2 bup src)" "$(commit-hash-n 2 bup-baseline src)"
+
+
+# FIXME: test that committer changes when rewriting, when appropriate
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-rm-between-index-and-save b/test/ext/test-rm-between-index-and-save
new file mode 100755 (executable)
index 0000000..67c5b8a
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+export BUP_DIR="$tmpdir/bup"
+D="$tmpdir/data"
+
+bup() { "$top/bup" "$@"; }
+
+WVSTART "remove file"
+# Fixed in commit 8585613c1f45f3e20feec00b24fc7e3a948fa23e ("Store
+# metadata in the index....")
+WVPASS mkdir "$D"
+WVPASS bup init
+WVPASS echo "content" > "$D"/foo
+WVPASS echo "content" > "$D"/bar
+WVPASS bup tick
+WVPASS bup index -ux "$D"
+WVPASS bup save -n save-fail-missing "$D"
+WVPASS echo "content" > "$D"/baz
+WVPASS bup tick
+WVPASS bup index -ux "$D"
+WVPASS rm "$D"/foo
+# When "bup tick" is removed above, this may fail (complete with warning),
+# since the ctime/mtime of "foo" might be pushed back:
+WVPASS bup save -n save-fail-missing "$D"
+# when the save-call failed, foo is missing from output, since only
+# then bup notices, that it was removed:
+WVPASSEQ "$(bup ls -A save-fail-missing/latest/$TOP/$D/)" "bar
+baz
+foo"
+# index/save again
+WVPASS bup tick
+WVPASS bup index -ux "$D"
+WVPASS bup save -n save-fail-missing "$D"
+# now foo is gone:
+WVPASSEQ "$(bup ls -A save-fail-missing/latest/$TOP/$D/)" "bar
+baz"
+
+
+# TODO: Test for racecondition between reading a file and reading its metadata?
+
+WVSTART "remove dir"
+WVPASS rm -r "$D"
+WVPASS mkdir "$D"
+WVPASS rm -r "$BUP_DIR"
+WVPASS bup init
+WVPASS mkdir "$D"/foo
+WVPASS mkdir "$D"/bar
+WVPASS bup tick
+WVPASS bup index -ux "$D"
+WVPASS bup save -n save-fail-missing "$D"
+WVPASS touch "$D"/bar
+WVPASS mkdir "$D"/baz
+WVPASS bup tick
+WVPASS bup index -ux "$D"
+WVPASS rmdir "$D"/foo
+# with directories, bup notices that foo is missing, so it fails
+# (complete with delayed error)
+WVFAIL bup save -n save-fail-missing "$D"
+# ...but foo is still saved since it was just fine in the index
+WVPASSEQ "$(bup ls -AF save-fail-missing/latest/$TOP/$D/)" "bar/
+baz/
+foo/"
+# Index again:
+WVPASS bup tick
+WVPASS bup index -ux "$D"
+# no non-zero-exitcode anymore:
+WVPASS bup save -n save-fail-missing "$D"
+# foo is now gone
+WVPASSEQ "$(bup ls -AF save-fail-missing/latest/$TOP/$D/)" "bar/
+baz/"
+
+WVPASS rm -rf "$tmpdir"
+
diff --git a/test/ext/test-rm-between-index-and-save.sh b/test/ext/test-rm-between-index-and-save.sh
deleted file mode 100755 (executable)
index 67c5b8a..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-export BUP_DIR="$tmpdir/bup"
-D="$tmpdir/data"
-
-bup() { "$top/bup" "$@"; }
-
-WVSTART "remove file"
-# Fixed in commit 8585613c1f45f3e20feec00b24fc7e3a948fa23e ("Store
-# metadata in the index....")
-WVPASS mkdir "$D"
-WVPASS bup init
-WVPASS echo "content" > "$D"/foo
-WVPASS echo "content" > "$D"/bar
-WVPASS bup tick
-WVPASS bup index -ux "$D"
-WVPASS bup save -n save-fail-missing "$D"
-WVPASS echo "content" > "$D"/baz
-WVPASS bup tick
-WVPASS bup index -ux "$D"
-WVPASS rm "$D"/foo
-# When "bup tick" is removed above, this may fail (complete with warning),
-# since the ctime/mtime of "foo" might be pushed back:
-WVPASS bup save -n save-fail-missing "$D"
-# when the save-call failed, foo is missing from output, since only
-# then bup notices, that it was removed:
-WVPASSEQ "$(bup ls -A save-fail-missing/latest/$TOP/$D/)" "bar
-baz
-foo"
-# index/save again
-WVPASS bup tick
-WVPASS bup index -ux "$D"
-WVPASS bup save -n save-fail-missing "$D"
-# now foo is gone:
-WVPASSEQ "$(bup ls -A save-fail-missing/latest/$TOP/$D/)" "bar
-baz"
-
-
-# TODO: Test for racecondition between reading a file and reading its metadata?
-
-WVSTART "remove dir"
-WVPASS rm -r "$D"
-WVPASS mkdir "$D"
-WVPASS rm -r "$BUP_DIR"
-WVPASS bup init
-WVPASS mkdir "$D"/foo
-WVPASS mkdir "$D"/bar
-WVPASS bup tick
-WVPASS bup index -ux "$D"
-WVPASS bup save -n save-fail-missing "$D"
-WVPASS touch "$D"/bar
-WVPASS mkdir "$D"/baz
-WVPASS bup tick
-WVPASS bup index -ux "$D"
-WVPASS rmdir "$D"/foo
-# with directories, bup notices that foo is missing, so it fails
-# (complete with delayed error)
-WVFAIL bup save -n save-fail-missing "$D"
-# ...but foo is still saved since it was just fine in the index
-WVPASSEQ "$(bup ls -AF save-fail-missing/latest/$TOP/$D/)" "bar/
-baz/
-foo/"
-# Index again:
-WVPASS bup tick
-WVPASS bup index -ux "$D"
-# no non-zero-exitcode anymore:
-WVPASS bup save -n save-fail-missing "$D"
-# foo is now gone
-WVPASSEQ "$(bup ls -AF save-fail-missing/latest/$TOP/$D/)" "bar/
-baz/"
-
-WVPASS rm -rf "$tmpdir"
-
diff --git a/test/ext/test-rm.sh b/test/ext/test-rm.sh
deleted file mode 100755 (executable)
index 3aca68d..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. ./dev/lib.sh || exit $?
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-
-bup() { "$top/bup" "$@"; }
-compare-trees() { "$top/dev/compare-trees" "$@"; }
-
-wv_matches_rx()
-{
-    local caller_file=${BASH_SOURCE[0]}
-    local caller_line=${BASH_LINENO[0]}
-    local src="$caller_file:$caller_line"
-    if test $# -ne 2; then
-        echo "! $src wv_matches_rx requires 2 arguments FAILED" 1>&2
-        return
-    fi
-    local str="$1"
-    local rx="$2"
-    echo "Matching:" 1>&2 || exit $?
-    echo "$str" | sed 's/^\(.*\)/  \1/' 1>&2 || exit $?
-    echo "Against:" 1>&2 || exit $?
-    echo "$rx" | sed 's/^\(.*\)/  \1/' 1>&2 || exit $?
-    if [[ "$str" =~ ^${rx}$ ]]; then
-        echo "! $src regex matches ok" 1>&2 || exit $?
-    else
-        echo "! $src regex doesn't match FAILED" 1>&2 || exit $?
-    fi
-}
-
-
-WVPASS bup init
-WVPASS cd "$tmpdir"
-
-
-WVSTART "rm /foo (lone branch)"
-WVPASS mkdir src src/foo
-WVPASS echo twisty-maze > src/1
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS "$top"/dev/sync-tree bup/ bup-baseline/
-# FIXME: test -n
-WVPASS bup tick # Make sure we always get the timestamp changes below
-WVPASS bup rm --unsafe /src
-observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
-wv_matches_rx "$observed" \
-'\*deleting[ ]+logs/refs/heads/src
-\*deleting[ ]+refs/heads/src(
-\.d\.\.t\.\.\.[.]*[ ]+\./)?
-\.d\.\.t\.\.\.[.]*[ ]+logs/refs/heads/
-\.d\.\.t\.\.\.[.]*[ ]+refs/heads/(
->f\+\+\+\+\+\+\+\+\+[ ]+packed-refs)?'
-
-
-WVSTART "rm /foo (one of many)"
-WVPASS rm -rf bup
-WVPASS mv bup-baseline bup
-WVPASS echo twisty-maze > src/2
-WVPASS bup index src
-WVPASS bup save -n src-2 src
-WVPASS echo twisty-maze > src/3
-WVPASS bup index src
-WVPASS bup save -n src-3 src
-WVPASS "$top"/dev/sync-tree bup/ bup-baseline/
-WVPASS bup tick # Make sure we always get the timestamp changes below
-WVPASS bup rm --unsafe /src
-observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
-wv_matches_rx "$observed" \
-"\*deleting[ ]+logs/refs/heads/src
-\*deleting[ ]+refs/heads/src(
-\.d\.\.t\.\.\.[.]*[ ]+\./)?
-\.d\.\.t\.\.\.[.]*[ ]+logs/refs/heads/
-\.d\.\.t\.\.\.[.]*[ ]+refs/heads/(
->f\+\+\+\+\+\+\+\+\+[ ]+packed-refs)?"
-
-
-WVSTART "rm /foo /bar (multiple of many)"
-WVPASS rm -rf bup
-WVPASS mv bup-baseline bup
-WVPASS echo twisty-maze > src/4
-WVPASS bup index src
-WVPASS bup save -n src-4 src
-WVPASS echo twisty-maze > src/5
-WVPASS bup index src
-WVPASS bup save -n src-5 src
-WVPASS "$top"/dev/sync-tree bup/ bup-baseline/
-WVPASS bup tick # Make sure we always get the timestamp changes below
-WVPASS bup rm --unsafe /src-2 /src-4
-observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
-wv_matches_rx "$observed" \
-"\*deleting[ ]+logs/refs/heads/src-2
-\*deleting[ ]+logs/refs/heads/src-4
-\*deleting[ ]+refs/heads/src-2
-\*deleting[ ]+refs/heads/src-4(
-\.d\.\.t\.\.\.[.]*[ ]+\./)?
-\.d\.\.t\.\.\.[.]*[ ]+logs/refs/heads/
-\.d\.\.t\.\.\.[.]*[ ]+refs/heads/(
->f\+\+\+\+\+\+\+\+\+[ ]+packed-refs)?"
-
-
-WVSTART "rm /foo /bar (all)"
-WVPASS rm -rf bup
-WVPASS mv bup-baseline bup
-WVPASS "$top"/dev/sync-tree bup/ bup-baseline/
-WVPASS bup tick # Make sure we always get the timestamp changes below
-WVPASS bup rm --unsafe /src /src-2 /src-3 /src-4 /src-5
-observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
-wv_matches_rx "$observed" \
-"\*deleting[ ]+logs/refs/heads/src
-\*deleting[ ]+logs/refs/heads/src-2
-\*deleting[ ]+logs/refs/heads/src-3
-\*deleting[ ]+logs/refs/heads/src-4
-\*deleting[ ]+logs/refs/heads/src-5
-\*deleting[ ]+refs/heads/src
-\*deleting[ ]+refs/heads/src-2
-\*deleting[ ]+refs/heads/src-3
-\*deleting[ ]+refs/heads/src-4
-\*deleting[ ]+refs/heads/src-5(
-\.d\.\.t\.\.\.[.]*[ ]+\./)?
-\.d\.\.t\.\.\.[.]*[ ]+logs/refs/heads/
-\.d\.\.t\.\.\.[.]*[ ]+refs/heads/(
->f\+\+\+\+\+\+\+\+\+[ ]+packed-refs)?"
-
-
-WVSTART "rm /foo/bar (lone save - equivalent to rm /foo)"
-WVPASS rm -rf bup bup-baseline src
-WVPASS bup init
-WVPASS mkdir src
-WVPASS echo twisty-maze > src/1
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS bup ls src > tmp-ls
-save1="$(WVPASS head -n 1 tmp-ls)" || exit $?
-WVPASS "$top"/dev/sync-tree bup/ bup-baseline/
-WVPASS bup tick # Make sure we always get the timestamp changes below
-WVFAIL bup rm --unsafe /src/latest
-WVPASS bup rm --unsafe /src/"$save1"
-observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
-wv_matches_rx "$observed" \
-"\*deleting[ ]+logs/refs/heads/src
-\*deleting[ ]+refs/heads/src(
-\.d\.\.t\.\.\.[.]*[ ]+\./)?
-\.d\.\.t\.\.\.[.]*[ ]+logs/refs/heads/
-\.d\.\.t\.\.\.[.]*[ ]+refs/heads/(
->f\+\+\+\+\+\+\+\+\+[ ]+packed-refs)?"
-
-
-verify-changes-caused-by-rewriting-save()
-{
-    local before="$1" after="$2" tmpdir
-    tmpdir="$(WVPASS wvmktempdir)" || exit $?
-    (WVPASS cd "$before" && WVPASS find . | WVPASS sort) \
-        > "$tmpdir/before" || exit $?
-    (WVPASS cd "$after" && WVPASS find . | WVPASS sort) \
-        > "$tmpdir/after" || exit $?
-    local new_paths new_idx new_pack observed
-    new_paths="$(WVPASS comm -13 "$tmpdir/before" "$tmpdir/after")" || exit $?
-    new_idx="$(echo "$new_paths" | WVPASS grep -E '^\./objects/pack/pack-.*\.idx$' | cut -b 3-)" || exit $?
-    new_pack="$(echo "$new_paths" | WVPASS grep -E '^\./objects/pack/pack-.*\.pack$' | cut -b 3-)" || exit $?
-    wv_matches_rx "$(compare-trees "$after/" "$before/")" \
-">fcst\.\.\.[.]*[ ]+logs/refs/heads/src
-\.d\.\.t\.\.\.[.]*[ ]+objects/
-\.d\.\.t\.\.\.[.]*[ ]+objects/pack/
->fcst\.\.\.[.]*[ ]+objects/pack/bup\.bloom
->f\+\+\+\+\+\+\+[+]*[ ]+$new_idx
->f\+\+\+\+\+\+\+[+]*[ ]+$new_pack
-\.d\.\.t\.\.\.[.]*[ ]+refs/heads/
->fc\.t\.\.\.[.]*[ ]+refs/heads/src"
-    WVPASS rm -rf "$tmpdir"
-}
-
-commit-hash-n()
-{
-    local n="$1" repo="$2" branch="$3"
-    GIT_DIR="$repo" WVPASS git rev-list --reverse "$branch" \
-        | WVPASS awk "FNR == $n"
-}
-
-rm-safe-cinfo()
-{
-    local n="$1" repo="$2" branch="$3" hash
-    hash="$(commit-hash-n "$n" "$repo" "$branch")" || exit $?
-    local fmt='Tree: %T%n'
-    fmt="${fmt}Author: %an <%ae> %ai%n"
-    fmt="${fmt}Committer: %cn <%ce> %ci%n"
-    fmt="${fmt}%n%s%n%b"
-    GIT_DIR="$repo" WVPASS git log -n1 --pretty=format:"$fmt" "$hash"
-}
-
-
-WVSTART 'rm /foo/BAR (setup)'
-WVPASS rm -rf bup bup-baseline src
-WVPASS bup init
-WVPASS mkdir src
-WVPASS echo twisty-maze > src/1
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS echo twisty-maze > src/2
-WVPASS bup index src
-WVPASS bup tick
-WVPASS bup save -n src src
-WVPASS echo twisty-maze > src/3
-WVPASS bup index src
-WVPASS bup tick
-WVPASS bup save -n src src
-WVPASS mv bup bup-baseline
-WVPASS bup tick # Make sure we always get the timestamp changes below
-
-
-WVSTART "rm /foo/BAR (first of many)"
-WVPASS "$top"/dev/sync-tree bup-baseline/ bup/
-WVPASS bup ls src > tmp-ls
-victim="$(WVPASS head -n 1 tmp-ls)" || exit $?
-WVPASS bup rm --unsafe /src/"$victim"
-verify-changes-caused-by-rewriting-save bup-baseline bup
-observed=$(WVPASS git rev-list src | WVPASS wc -l) || exit $?
-WVPASSEQ 2 $observed
-WVPASSEQ "$(rm-safe-cinfo 1 bup src)" "$(rm-safe-cinfo 2 bup-baseline src)"
-WVPASSEQ "$(rm-safe-cinfo 2 bup src)" "$(rm-safe-cinfo 3 bup-baseline src)"
-
-
-WVSTART "rm /foo/BAR (one of many)"
-WVPASS "$top"/dev/sync-tree bup-baseline/ bup/
-victim="$(WVPASS bup ls src | tail -n +2 | head -n 1)" || exit $?
-WVPASS bup rm --unsafe /src/"$victim"
-verify-changes-caused-by-rewriting-save bup-baseline bup
-observed=$(git rev-list src | wc -l) || exit $?
-WVPASSEQ 2 $observed
-WVPASSEQ "$(commit-hash-n 1 bup src)" "$(commit-hash-n 1 bup-baseline src)"
-WVPASSEQ "$(rm-safe-cinfo 2 bup src)" "$(rm-safe-cinfo 3 bup-baseline src)"
-
-
-WVSTART "rm /foo/BAR (last of many)"
-WVPASS "$top"/dev/sync-tree bup-baseline/ bup/
-victim="$(WVPASS bup ls src | tail -n 2 | head -n 1)" || exit $?
-WVPASS bup rm --unsafe -vv /src/"$victim"
-observed="$(compare-trees bup/ bup-baseline/ | LC_ALL=C sort)" || exit $?
-wv_matches_rx "$observed" \
-"\.d\.\.t\.\.\.[.]*[ ]+refs/heads/
->fc\.t\.\.\.[.]*[ ]+refs/heads/src
->fcst\.\.\.[.]*[ ]+logs/refs/heads/src"
-observed=$(git rev-list src | wc -l) || exit $?
-WVPASSEQ 2 $observed
-WVPASSEQ "$(commit-hash-n 1 bup src)" "$(commit-hash-n 1 bup-baseline src)"
-WVPASSEQ "$(commit-hash-n 2 bup src)" "$(commit-hash-n 2 bup-baseline src)"
-
-
-# FIXME: test that committer changes when rewriting, when appropriate
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-save-creates-no-unrefs b/test/ext/test-save-creates-no-unrefs
new file mode 100755 (executable)
index 0000000..38d9064
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+
+WVSTART 'all'
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$BUP_DIR"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS mkdir -p "$tmpdir/src"
+WVPASS touch "$tmpdir/src/foo"
+WVPASS bup init
+WVPASS bup index "$tmpdir/src"
+WVPASS bup save -n src "$tmpdir/src"
+WVPASSEQ "$(git fsck --unreachable)" ""
+WVPASS bup save -n src "$tmpdir/src"
+WVPASSEQ "$(git fsck --unreachable)" ""
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-save-creates-no-unrefs.sh b/test/ext/test-save-creates-no-unrefs.sh
deleted file mode 100755 (executable)
index 38d9064..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-
-WVSTART 'all'
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$BUP_DIR"
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS mkdir -p "$tmpdir/src"
-WVPASS touch "$tmpdir/src/foo"
-WVPASS bup init
-WVPASS bup index "$tmpdir/src"
-WVPASS bup save -n src "$tmpdir/src"
-WVPASSEQ "$(git fsck --unreachable)" ""
-WVPASS bup save -n src "$tmpdir/src"
-WVPASSEQ "$(git fsck --unreachable)" ""
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-save-restore-excludes b/test/ext/test-save-restore-excludes
new file mode 100755 (executable)
index 0000000..39ce969
--- /dev/null
@@ -0,0 +1,303 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS cd "$tmpdir"
+
+
+WVSTART "index excludes bupdir"
+WVPASS force-delete src "$BUP_DIR"
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS bup random 128k >src/b
+WVPASS mkdir src/d src/d/e
+WVPASS bup random 512 >src/f
+WVPASS bup index -ux src
+WVPASS bup save -n exclude-bupdir src
+WVPASSEQ "$(bup ls -AF "exclude-bupdir/latest/$tmpdir/src/")" "a
+b
+d/
+f"
+
+
+WVSTART "index --exclude"
+WVPASS force-delete src "$BUP_DIR"
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS bup random 128k >src/b
+WVPASS mkdir src/d src/d/e
+WVPASS bup random 512 >src/f
+WVPASS bup random 512 >src/j
+WVPASS bup index -ux --exclude src/d --exclude src/j src
+WVPASS bup save -n exclude src
+WVPASSEQ "$(bup ls "exclude/latest/$tmpdir/src/")" "a
+b
+f"
+WVPASS mkdir src/g src/h
+WVPASS bup index -ux --exclude src/d --exclude $tmpdir/src/g --exclude src/h \
+    --exclude "$tmpdir/src/j" src
+WVPASS bup save -n exclude src
+WVPASSEQ "$(bup ls "exclude/latest/$tmpdir/src/")" "a
+b
+f"
+
+
+WVSTART "index --exclude-from"
+WVPASS force-delete src "$BUP_DIR"
+WVPASS bup init
+WVPASS mkdir src
+WVPASS echo "src/d
+ $tmpdir/src/g
+src/h
+src/i" > exclude-list
+WVPASS touch src/a
+WVPASS bup random 128k >src/b
+WVPASS mkdir src/d src/d/e
+WVPASS bup random 512 >src/f
+WVPASS mkdir src/g src/h
+WVPASS bup random 128k > src/i
+WVPASS bup index -ux --exclude-from exclude-list src
+WVPASS bup save -n exclude-from src
+WVPASSEQ "$(bup ls "exclude-from/latest/$tmpdir/src/")" "a
+b
+f"
+WVPASS rm exclude-list
+
+
+# bup index --exclude-rx ...
+# ==========================
+
+WVSTART "index --exclude-rx '^/foo' (root anchor)"
+WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS touch src/b
+WVPASS mkdir src/sub1
+WVPASS mkdir src/sub2
+WVPASS touch src/sub1/a
+WVPASS touch src/sub2/b
+WVPASS bup index -u src --exclude-rx "^$(pwd)/src/sub1/"
+WVPASS bup save --strip -n bupdir src
+WVPASS bup restore -C buprestore.tmp /bupdir/latest/
+actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
+WVPASSEQ "$actual" ".
+./a
+./b
+./sub2
+./sub2/b"
+
+WVSTART "index --exclude-rx '/foo$' (non-dir, tail anchor)"
+WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS touch src/b
+WVPASS touch src/foo
+WVPASS mkdir src/sub
+WVPASS mkdir src/sub/foo
+WVPASS touch src/sub/foo/a
+WVPASS bup index -u src --exclude-rx '/foo$'
+WVPASS bup save --strip -n bupdir src
+WVPASS bup restore -C buprestore.tmp /bupdir/latest/
+actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
+WVPASSEQ "$actual" ".
+./a
+./b
+./sub
+./sub/foo
+./sub/foo/a"
+
+WVSTART "index --exclude-rx '/foo/$' (dir, tail anchor)"
+WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS touch src/b
+WVPASS touch src/foo
+WVPASS mkdir src/sub
+WVPASS mkdir src/sub/foo
+WVPASS touch src/sub/foo/a
+WVPASS bup index -u src --exclude-rx '/foo/$'
+WVPASS bup save --strip -n bupdir src
+WVPASS bup restore -C buprestore.tmp /bupdir/latest/
+actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
+WVPASSEQ "$actual" ".
+./a
+./b
+./foo
+./sub"
+
+WVSTART "index --exclude-rx '/foo/.' (dir content)"
+WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS touch src/b
+WVPASS touch src/foo
+WVPASS mkdir src/sub
+WVPASS mkdir src/sub/foo
+WVPASS touch src/sub/foo/a
+WVPASS bup index -u src --exclude-rx '/foo/.'
+WVPASS bup save --strip -n bupdir src
+WVPASS bup restore -C buprestore.tmp /bupdir/latest/
+actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
+WVPASSEQ "$actual" ".
+./a
+./b
+./foo
+./sub
+./sub/foo"
+
+
+# bup index --exclude-rx-from ...
+# ===============================
+WVSTART "index --exclude-rx-from"
+WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS touch src/b
+WVPASS mkdir src/sub1
+WVPASS mkdir src/sub2
+WVPASS touch src/sub1/a
+WVPASS touch src/sub2/b
+# exclude-rx-file includes blank lines to check that we ignore them.
+WVPASS echo "^$(pwd)/src/sub1/
+
+" > exclude-rx-file
+WVPASS bup index -u src --exclude-rx-from exclude-rx-file
+WVPASS bup save --strip -n bupdir src
+WVPASS bup restore -C buprestore.tmp /bupdir/latest/
+actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
+WVPASSEQ "$actual" ".
+./a
+./b
+./sub2
+./sub2/b"
+
+
+# bup restore --exclude-rx ...
+# ============================
+
+WVSTART "restore --exclude-rx '^/foo' (root anchor)"
+WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS touch src/b
+WVPASS mkdir src/sub1
+WVPASS mkdir src/sub2
+WVPASS touch src/sub1/a
+WVPASS touch src/sub2/b
+WVPASS bup index -u src
+WVPASS bup save --strip -n bupdir src
+WVPASS bup restore -C buprestore.tmp --exclude-rx "^/sub1/" /bupdir/latest/
+actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
+WVPASSEQ "$actual" ".
+./a
+./b
+./sub2
+./sub2/b"
+
+WVSTART "restore --exclude-rx '/foo$' (non-dir, tail anchor)"
+WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS touch src/b
+WVPASS touch src/foo
+WVPASS mkdir src/sub
+WVPASS mkdir src/sub/foo
+WVPASS touch src/sub/foo/a
+WVPASS bup index -u src
+WVPASS bup save --strip -n bupdir src
+WVPASS bup restore -C buprestore.tmp --exclude-rx '/foo$' /bupdir/latest/
+actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
+WVPASSEQ "$actual" ".
+./a
+./b
+./sub
+./sub/foo
+./sub/foo/a"
+
+WVSTART "restore --exclude-rx '/foo/$' (dir, tail anchor)"
+WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS touch src/b
+WVPASS touch src/foo
+WVPASS mkdir src/sub
+WVPASS mkdir src/sub/foo
+WVPASS touch src/sub/foo/a
+WVPASS bup index -u src
+WVPASS bup save --strip -n bupdir src
+WVPASS bup restore -C buprestore.tmp --exclude-rx '/foo/$' /bupdir/latest/
+actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
+WVPASSEQ "$actual" ".
+./a
+./b
+./foo
+./sub"
+
+WVSTART "restore --exclude-rx '/foo/.' (dir content)"
+WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS touch src/b
+WVPASS touch src/foo
+WVPASS mkdir src/sub
+WVPASS mkdir src/sub/foo
+WVPASS touch src/sub/foo/a
+WVPASS bup index -u src
+WVPASS bup save --strip -n bupdir src
+WVPASS bup restore -C buprestore.tmp --exclude-rx '/foo/.' /bupdir/latest/
+actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
+WVPASSEQ "$actual" ".
+./a
+./b
+./foo
+./sub
+./sub/foo"
+
+
+# bup restore --exclude-rx-from ...
+# =================================
+
+WVSTART "restore --exclude-rx-from"
+WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
+WVPASS bup init
+WVPASS mkdir src
+WVPASS touch src/a
+WVPASS touch src/b
+WVPASS mkdir src/sub1
+WVPASS mkdir src/sub2
+WVPASS touch src/sub1/a
+WVPASS touch src/sub2/b
+WVPASS bup index -u src
+WVPASS bup save --strip -n bupdir src
+WVPASS echo "^/sub1/" > exclude-rx-file
+WVPASS bup restore -C buprestore.tmp \
+    --exclude-rx-from exclude-rx-file /bupdir/latest/
+actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
+WVPASSEQ "$actual" ".
+./a
+./b
+./sub2
+./sub2/b"
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-save-restore-excludes.sh b/test/ext/test-save-restore-excludes.sh
deleted file mode 100755 (executable)
index 39ce969..0000000
+++ /dev/null
@@ -1,303 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS cd "$tmpdir"
-
-
-WVSTART "index excludes bupdir"
-WVPASS force-delete src "$BUP_DIR"
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS bup random 128k >src/b
-WVPASS mkdir src/d src/d/e
-WVPASS bup random 512 >src/f
-WVPASS bup index -ux src
-WVPASS bup save -n exclude-bupdir src
-WVPASSEQ "$(bup ls -AF "exclude-bupdir/latest/$tmpdir/src/")" "a
-b
-d/
-f"
-
-
-WVSTART "index --exclude"
-WVPASS force-delete src "$BUP_DIR"
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS bup random 128k >src/b
-WVPASS mkdir src/d src/d/e
-WVPASS bup random 512 >src/f
-WVPASS bup random 512 >src/j
-WVPASS bup index -ux --exclude src/d --exclude src/j src
-WVPASS bup save -n exclude src
-WVPASSEQ "$(bup ls "exclude/latest/$tmpdir/src/")" "a
-b
-f"
-WVPASS mkdir src/g src/h
-WVPASS bup index -ux --exclude src/d --exclude $tmpdir/src/g --exclude src/h \
-    --exclude "$tmpdir/src/j" src
-WVPASS bup save -n exclude src
-WVPASSEQ "$(bup ls "exclude/latest/$tmpdir/src/")" "a
-b
-f"
-
-
-WVSTART "index --exclude-from"
-WVPASS force-delete src "$BUP_DIR"
-WVPASS bup init
-WVPASS mkdir src
-WVPASS echo "src/d
- $tmpdir/src/g
-src/h
-src/i" > exclude-list
-WVPASS touch src/a
-WVPASS bup random 128k >src/b
-WVPASS mkdir src/d src/d/e
-WVPASS bup random 512 >src/f
-WVPASS mkdir src/g src/h
-WVPASS bup random 128k > src/i
-WVPASS bup index -ux --exclude-from exclude-list src
-WVPASS bup save -n exclude-from src
-WVPASSEQ "$(bup ls "exclude-from/latest/$tmpdir/src/")" "a
-b
-f"
-WVPASS rm exclude-list
-
-
-# bup index --exclude-rx ...
-# ==========================
-
-WVSTART "index --exclude-rx '^/foo' (root anchor)"
-WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS touch src/b
-WVPASS mkdir src/sub1
-WVPASS mkdir src/sub2
-WVPASS touch src/sub1/a
-WVPASS touch src/sub2/b
-WVPASS bup index -u src --exclude-rx "^$(pwd)/src/sub1/"
-WVPASS bup save --strip -n bupdir src
-WVPASS bup restore -C buprestore.tmp /bupdir/latest/
-actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
-WVPASSEQ "$actual" ".
-./a
-./b
-./sub2
-./sub2/b"
-
-WVSTART "index --exclude-rx '/foo$' (non-dir, tail anchor)"
-WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS touch src/b
-WVPASS touch src/foo
-WVPASS mkdir src/sub
-WVPASS mkdir src/sub/foo
-WVPASS touch src/sub/foo/a
-WVPASS bup index -u src --exclude-rx '/foo$'
-WVPASS bup save --strip -n bupdir src
-WVPASS bup restore -C buprestore.tmp /bupdir/latest/
-actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
-WVPASSEQ "$actual" ".
-./a
-./b
-./sub
-./sub/foo
-./sub/foo/a"
-
-WVSTART "index --exclude-rx '/foo/$' (dir, tail anchor)"
-WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS touch src/b
-WVPASS touch src/foo
-WVPASS mkdir src/sub
-WVPASS mkdir src/sub/foo
-WVPASS touch src/sub/foo/a
-WVPASS bup index -u src --exclude-rx '/foo/$'
-WVPASS bup save --strip -n bupdir src
-WVPASS bup restore -C buprestore.tmp /bupdir/latest/
-actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
-WVPASSEQ "$actual" ".
-./a
-./b
-./foo
-./sub"
-
-WVSTART "index --exclude-rx '/foo/.' (dir content)"
-WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS touch src/b
-WVPASS touch src/foo
-WVPASS mkdir src/sub
-WVPASS mkdir src/sub/foo
-WVPASS touch src/sub/foo/a
-WVPASS bup index -u src --exclude-rx '/foo/.'
-WVPASS bup save --strip -n bupdir src
-WVPASS bup restore -C buprestore.tmp /bupdir/latest/
-actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
-WVPASSEQ "$actual" ".
-./a
-./b
-./foo
-./sub
-./sub/foo"
-
-
-# bup index --exclude-rx-from ...
-# ===============================
-WVSTART "index --exclude-rx-from"
-WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS touch src/b
-WVPASS mkdir src/sub1
-WVPASS mkdir src/sub2
-WVPASS touch src/sub1/a
-WVPASS touch src/sub2/b
-# exclude-rx-file includes blank lines to check that we ignore them.
-WVPASS echo "^$(pwd)/src/sub1/
-
-" > exclude-rx-file
-WVPASS bup index -u src --exclude-rx-from exclude-rx-file
-WVPASS bup save --strip -n bupdir src
-WVPASS bup restore -C buprestore.tmp /bupdir/latest/
-actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
-WVPASSEQ "$actual" ".
-./a
-./b
-./sub2
-./sub2/b"
-
-
-# bup restore --exclude-rx ...
-# ============================
-
-WVSTART "restore --exclude-rx '^/foo' (root anchor)"
-WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS touch src/b
-WVPASS mkdir src/sub1
-WVPASS mkdir src/sub2
-WVPASS touch src/sub1/a
-WVPASS touch src/sub2/b
-WVPASS bup index -u src
-WVPASS bup save --strip -n bupdir src
-WVPASS bup restore -C buprestore.tmp --exclude-rx "^/sub1/" /bupdir/latest/
-actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
-WVPASSEQ "$actual" ".
-./a
-./b
-./sub2
-./sub2/b"
-
-WVSTART "restore --exclude-rx '/foo$' (non-dir, tail anchor)"
-WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS touch src/b
-WVPASS touch src/foo
-WVPASS mkdir src/sub
-WVPASS mkdir src/sub/foo
-WVPASS touch src/sub/foo/a
-WVPASS bup index -u src
-WVPASS bup save --strip -n bupdir src
-WVPASS bup restore -C buprestore.tmp --exclude-rx '/foo$' /bupdir/latest/
-actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
-WVPASSEQ "$actual" ".
-./a
-./b
-./sub
-./sub/foo
-./sub/foo/a"
-
-WVSTART "restore --exclude-rx '/foo/$' (dir, tail anchor)"
-WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS touch src/b
-WVPASS touch src/foo
-WVPASS mkdir src/sub
-WVPASS mkdir src/sub/foo
-WVPASS touch src/sub/foo/a
-WVPASS bup index -u src
-WVPASS bup save --strip -n bupdir src
-WVPASS bup restore -C buprestore.tmp --exclude-rx '/foo/$' /bupdir/latest/
-actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
-WVPASSEQ "$actual" ".
-./a
-./b
-./foo
-./sub"
-
-WVSTART "restore --exclude-rx '/foo/.' (dir content)"
-WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS touch src/b
-WVPASS touch src/foo
-WVPASS mkdir src/sub
-WVPASS mkdir src/sub/foo
-WVPASS touch src/sub/foo/a
-WVPASS bup index -u src
-WVPASS bup save --strip -n bupdir src
-WVPASS bup restore -C buprestore.tmp --exclude-rx '/foo/.' /bupdir/latest/
-actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
-WVPASSEQ "$actual" ".
-./a
-./b
-./foo
-./sub
-./sub/foo"
-
-
-# bup restore --exclude-rx-from ...
-# =================================
-
-WVSTART "restore --exclude-rx-from"
-WVPASS rm -rf src "$BUP_DIR" buprestore.tmp
-WVPASS bup init
-WVPASS mkdir src
-WVPASS touch src/a
-WVPASS touch src/b
-WVPASS mkdir src/sub1
-WVPASS mkdir src/sub2
-WVPASS touch src/sub1/a
-WVPASS touch src/sub2/b
-WVPASS bup index -u src
-WVPASS bup save --strip -n bupdir src
-WVPASS echo "^/sub1/" > exclude-rx-file
-WVPASS bup restore -C buprestore.tmp \
-    --exclude-rx-from exclude-rx-file /bupdir/latest/
-actual="$(WVPASS cd buprestore.tmp; WVPASS find . | WVPASS sort)" || exit $?
-WVPASSEQ "$actual" ".
-./a
-./b
-./sub2
-./sub2/b"
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-save-strip-graft b/test/ext/test-save-strip-graft
new file mode 100755 (executable)
index 0000000..78b79fe
--- /dev/null
@@ -0,0 +1,161 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+compare-trees() { "$top/dev/compare-trees" "$@"; }
+
+WVPASS cd "$tmpdir"
+
+
+WVSTART "save --strip"
+WVPASS force-delete "$BUP_DIR" src restore
+WVPASS bup init
+WVPASS mkdir -p src/x/y/z
+WVPASS bup random 8k > src/x/y/random-1
+WVPASS bup random 8k > src/x/y/z/random-2
+WVPASS bup index -u src
+WVPASS bup save --strip -n foo src/x/y
+WVPASS bup restore -C restore /foo/latest
+WVPASS compare-trees src/x/y/ restore/latest/
+
+
+WVSTART "save --strip-path (relative)"
+WVPASS force-delete "$BUP_DIR" src restore
+WVPASS bup init
+WVPASS mkdir -p src/x/y/z
+WVPASS bup random 8k > src/x/y/random-1
+WVPASS bup random 8k > src/x/y/z/random-2
+WVPASS bup index -u src
+WVPASS bup save --strip-path src -n foo src/x
+WVPASS bup restore -C restore /foo/latest
+WVPASS compare-trees src/ restore/latest/
+
+
+WVSTART "save --strip-path (absolute)"
+WVPASS force-delete "$BUP_DIR" src restore
+WVPASS bup init
+WVPASS mkdir -p src/x/y/z
+WVPASS bup random 8k > src/x/y/random-1
+WVPASS bup random 8k > src/x/y/z/random-2
+WVPASS bup index -u src
+WVPASS bup save --strip-path "$tmpdir" -n foo src
+WVPASS bup restore -C restore /foo/latest
+WVPASS compare-trees src/ "restore/latest/src/"
+
+
+WVSTART "save --strip-path (no match)"
+if test $(WVPASS path-filesystems . | WVPASS sort -u | WVPASS wc -l) -ne 1
+then
+    # Skip the test because the attempt to restore parent dirs to the
+    # current filesystem may fail -- i.e. running from
+    # /foo/ext4/bar/btrfs will fail when bup tries to restore linux
+    # attrs above btrfs to the restore tree *inside* btrfs.
+    # FIXME: add WVSKIP
+    echo "(running from tree with mixed filesystems; skipping test)" 1>&2
+    exit 0
+else
+    WVPASS force-delete "$BUP_DIR" src restore
+    WVPASS bup init
+    WVPASS mkdir -p src/x/y/z
+    WVPASS bup random 8k > src/x/y/random-1
+    WVPASS bup random 8k > src/x/y/z/random-2
+    WVPASS bup index -u src
+    WVPASS bup save --strip-path foo -n foo src/x
+    WVPASS bup restore -C restore /foo/latest
+    WVPASS compare-trees src/ "restore/latest/$tmpdir/src/"
+fi
+
+
+WVSTART "save --graft (empty graft points disallowed)"
+WVPASS force-delete "$BUP_DIR" src restore
+WVPASS bup init
+WVPASS mkdir src
+WVFAIL bup save --graft =/grafted -n graft-point-absolute src 2>&1 \
+    | WVPASS grep 'error: a graft point cannot be empty'
+WVFAIL bup save --graft $top/$tmp= -n graft-point-absolute src 2>&1 \
+    | WVPASS grep 'error: a graft point cannot be empty'
+
+
+WVSTART "save --graft /x/y=/a/b (relative paths)"
+WVPASS force-delete "$BUP_DIR" src restore
+WVPASS bup init
+WVPASS mkdir -p src/x/y/z
+WVPASS bup random 8k > src/x/y/random-1
+WVPASS bup random 8k > src/x/y/z/random-2
+WVPASS bup index -u src
+WVPASS bup save --graft src=x -n foo src
+WVPASS bup restore -C restore /foo/latest
+WVPASS compare-trees src/ "restore/latest/$tmpdir/x/"
+
+
+WVSTART "save --graft /x/y=/a/b (matching structure)"
+WVPASS force-delete "$BUP_DIR" src restore
+WVPASS bup init
+WVPASS mkdir -p src/x/y/z
+WVPASS bup random 8k > src/x/y/random-1
+WVPASS bup random 8k > src/x/y/z/random-2
+WVPASS bup index -u src
+WVPASS bup save -v --graft "$tmpdir/src/x/y=$tmpdir/src/a/b" -n foo src/x/y
+WVPASS bup restore -C restore /foo/latest
+WVPASS compare-trees src/x/y/ "restore/latest/$tmpdir/src/a/b/"
+
+
+WVSTART "save --graft /x/y=/a (shorter target)"
+WVPASS force-delete "$BUP_DIR" src restore
+WVPASS bup init
+WVPASS mkdir -p src/x/y/z
+WVPASS bup random 8k > src/x/y/random-1
+WVPASS bup random 8k > src/x/y/z/random-2
+WVPASS bup index -u src
+WVPASS bup save -v --graft "$tmpdir/src/x/y=/a" -n foo src/x/y
+WVPASS bup restore -C restore /foo/latest
+WVPASS compare-trees src/x/y/ "restore/latest/a/"
+
+
+WVSTART "save --graft /x=/a/b (longer target)"
+WVPASS force-delete "$BUP_DIR" src restore
+WVPASS bup init
+WVPASS mkdir -p src/x/y/z
+WVPASS bup random 8k > src/x/y/random-1
+WVPASS bup random 8k > src/x/y/z/random-2
+WVPASS bup index -u src
+WVPASS bup save -v --graft "$tmpdir/src=$tmpdir/src/a/b/c" -n foo src
+WVPASS bup restore -C restore /foo/latest
+WVPASS compare-trees src/ "restore/latest/$tmpdir/src/a/b/c/"
+
+
+WVSTART "save --graft /x=/ (root target)"
+WVPASS force-delete "$BUP_DIR" src restore
+WVPASS bup init
+WVPASS mkdir -p src/x/y/z
+WVPASS bup random 8k > src/x/y/random-1
+WVPASS bup random 8k > src/x/y/z/random-2
+WVPASS bup index -u src
+WVPASS bup save -v --graft "$tmpdir/src/x=/" -n foo src/x
+WVPASS bup restore -C restore /foo/latest
+WVPASS compare-trees src/x/ "restore/latest/"
+
+
+#WVSTART "save --graft /=/x/ (root source)"
+# FIXME: Not tested for now -- will require cleverness, or caution as root.
+
+
+WVSTART "save collision"
+WVPASS force-delete "$BUP_DIR" src restore
+WVPASS bup init
+WVPASS mkdir -p src/x/1 src/y/1
+WVPASS bup index -u src
+WVFAIL bup save --strip -n foo src/x src/y 2> tmp-err.log
+WVPASS grep -F "error: ignoring duplicate path 1 in /" tmp-err.log
+
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-save-strip-graft.sh b/test/ext/test-save-strip-graft.sh
deleted file mode 100755 (executable)
index 78b79fe..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-compare-trees() { "$top/dev/compare-trees" "$@"; }
-
-WVPASS cd "$tmpdir"
-
-
-WVSTART "save --strip"
-WVPASS force-delete "$BUP_DIR" src restore
-WVPASS bup init
-WVPASS mkdir -p src/x/y/z
-WVPASS bup random 8k > src/x/y/random-1
-WVPASS bup random 8k > src/x/y/z/random-2
-WVPASS bup index -u src
-WVPASS bup save --strip -n foo src/x/y
-WVPASS bup restore -C restore /foo/latest
-WVPASS compare-trees src/x/y/ restore/latest/
-
-
-WVSTART "save --strip-path (relative)"
-WVPASS force-delete "$BUP_DIR" src restore
-WVPASS bup init
-WVPASS mkdir -p src/x/y/z
-WVPASS bup random 8k > src/x/y/random-1
-WVPASS bup random 8k > src/x/y/z/random-2
-WVPASS bup index -u src
-WVPASS bup save --strip-path src -n foo src/x
-WVPASS bup restore -C restore /foo/latest
-WVPASS compare-trees src/ restore/latest/
-
-
-WVSTART "save --strip-path (absolute)"
-WVPASS force-delete "$BUP_DIR" src restore
-WVPASS bup init
-WVPASS mkdir -p src/x/y/z
-WVPASS bup random 8k > src/x/y/random-1
-WVPASS bup random 8k > src/x/y/z/random-2
-WVPASS bup index -u src
-WVPASS bup save --strip-path "$tmpdir" -n foo src
-WVPASS bup restore -C restore /foo/latest
-WVPASS compare-trees src/ "restore/latest/src/"
-
-
-WVSTART "save --strip-path (no match)"
-if test $(WVPASS path-filesystems . | WVPASS sort -u | WVPASS wc -l) -ne 1
-then
-    # Skip the test because the attempt to restore parent dirs to the
-    # current filesystem may fail -- i.e. running from
-    # /foo/ext4/bar/btrfs will fail when bup tries to restore linux
-    # attrs above btrfs to the restore tree *inside* btrfs.
-    # FIXME: add WVSKIP
-    echo "(running from tree with mixed filesystems; skipping test)" 1>&2
-    exit 0
-else
-    WVPASS force-delete "$BUP_DIR" src restore
-    WVPASS bup init
-    WVPASS mkdir -p src/x/y/z
-    WVPASS bup random 8k > src/x/y/random-1
-    WVPASS bup random 8k > src/x/y/z/random-2
-    WVPASS bup index -u src
-    WVPASS bup save --strip-path foo -n foo src/x
-    WVPASS bup restore -C restore /foo/latest
-    WVPASS compare-trees src/ "restore/latest/$tmpdir/src/"
-fi
-
-
-WVSTART "save --graft (empty graft points disallowed)"
-WVPASS force-delete "$BUP_DIR" src restore
-WVPASS bup init
-WVPASS mkdir src
-WVFAIL bup save --graft =/grafted -n graft-point-absolute src 2>&1 \
-    | WVPASS grep 'error: a graft point cannot be empty'
-WVFAIL bup save --graft $top/$tmp= -n graft-point-absolute src 2>&1 \
-    | WVPASS grep 'error: a graft point cannot be empty'
-
-
-WVSTART "save --graft /x/y=/a/b (relative paths)"
-WVPASS force-delete "$BUP_DIR" src restore
-WVPASS bup init
-WVPASS mkdir -p src/x/y/z
-WVPASS bup random 8k > src/x/y/random-1
-WVPASS bup random 8k > src/x/y/z/random-2
-WVPASS bup index -u src
-WVPASS bup save --graft src=x -n foo src
-WVPASS bup restore -C restore /foo/latest
-WVPASS compare-trees src/ "restore/latest/$tmpdir/x/"
-
-
-WVSTART "save --graft /x/y=/a/b (matching structure)"
-WVPASS force-delete "$BUP_DIR" src restore
-WVPASS bup init
-WVPASS mkdir -p src/x/y/z
-WVPASS bup random 8k > src/x/y/random-1
-WVPASS bup random 8k > src/x/y/z/random-2
-WVPASS bup index -u src
-WVPASS bup save -v --graft "$tmpdir/src/x/y=$tmpdir/src/a/b" -n foo src/x/y
-WVPASS bup restore -C restore /foo/latest
-WVPASS compare-trees src/x/y/ "restore/latest/$tmpdir/src/a/b/"
-
-
-WVSTART "save --graft /x/y=/a (shorter target)"
-WVPASS force-delete "$BUP_DIR" src restore
-WVPASS bup init
-WVPASS mkdir -p src/x/y/z
-WVPASS bup random 8k > src/x/y/random-1
-WVPASS bup random 8k > src/x/y/z/random-2
-WVPASS bup index -u src
-WVPASS bup save -v --graft "$tmpdir/src/x/y=/a" -n foo src/x/y
-WVPASS bup restore -C restore /foo/latest
-WVPASS compare-trees src/x/y/ "restore/latest/a/"
-
-
-WVSTART "save --graft /x=/a/b (longer target)"
-WVPASS force-delete "$BUP_DIR" src restore
-WVPASS bup init
-WVPASS mkdir -p src/x/y/z
-WVPASS bup random 8k > src/x/y/random-1
-WVPASS bup random 8k > src/x/y/z/random-2
-WVPASS bup index -u src
-WVPASS bup save -v --graft "$tmpdir/src=$tmpdir/src/a/b/c" -n foo src
-WVPASS bup restore -C restore /foo/latest
-WVPASS compare-trees src/ "restore/latest/$tmpdir/src/a/b/c/"
-
-
-WVSTART "save --graft /x=/ (root target)"
-WVPASS force-delete "$BUP_DIR" src restore
-WVPASS bup init
-WVPASS mkdir -p src/x/y/z
-WVPASS bup random 8k > src/x/y/random-1
-WVPASS bup random 8k > src/x/y/z/random-2
-WVPASS bup index -u src
-WVPASS bup save -v --graft "$tmpdir/src/x=/" -n foo src/x
-WVPASS bup restore -C restore /foo/latest
-WVPASS compare-trees src/x/ "restore/latest/"
-
-
-#WVSTART "save --graft /=/x/ (root source)"
-# FIXME: Not tested for now -- will require cleverness, or caution as root.
-
-
-WVSTART "save collision"
-WVPASS force-delete "$BUP_DIR" src restore
-WVPASS bup init
-WVPASS mkdir -p src/x/1 src/y/1
-WVPASS bup index -u src
-WVFAIL bup save --strip -n foo src/x src/y 2> tmp-err.log
-WVPASS grep -F "error: ignoring duplicate path 1 in /" tmp-err.log
-
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-save-with-valid-parent b/test/ext/test-save-with-valid-parent
new file mode 100755 (executable)
index 0000000..53c7e1b
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+compare-trees() { "$top/dev/compare-trees" "$@"; }
+
+WVPASS cd "$tmpdir"
+
+# Make sure that we can explicitly save a path whose parent is up to
+# date.
+
+WVSTART "save path with up to date parent"
+WVPASS bup init
+
+WVPASS mkdir -p src/a src/b
+WVPASS touch src/a/1 src/b/2
+WVPASS bup index -u src
+WVPASS bup save -n src src
+
+WVPASS bup save -n src src/b
+WVPASS bup restore -C restore "src/latest/$(pwd)/"
+WVPASS test ! -e restore/src/a
+WVPASS "$top/dev/compare-trees" -c src/b/ restore/src/b/
+
+WVPASS bup save -n src src/a/1
+WVPASS rm -r restore
+WVPASS bup restore -C restore "src/latest/$(pwd)/"
+WVPASS test ! -e restore/src/b
+WVPASS "$top/dev/compare-trees" -c src/a/ restore/src/a/
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-save-with-valid-parent.sh b/test/ext/test-save-with-valid-parent.sh
deleted file mode 100755 (executable)
index 53c7e1b..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-compare-trees() { "$top/dev/compare-trees" "$@"; }
-
-WVPASS cd "$tmpdir"
-
-# Make sure that we can explicitly save a path whose parent is up to
-# date.
-
-WVSTART "save path with up to date parent"
-WVPASS bup init
-
-WVPASS mkdir -p src/a src/b
-WVPASS touch src/a/1 src/b/2
-WVPASS bup index -u src
-WVPASS bup save -n src src
-
-WVPASS bup save -n src src/b
-WVPASS bup restore -C restore "src/latest/$(pwd)/"
-WVPASS test ! -e restore/src/a
-WVPASS "$top/dev/compare-trees" -c src/b/ restore/src/b/
-
-WVPASS bup save -n src src/a/1
-WVPASS rm -r restore
-WVPASS bup restore -C restore "src/latest/$(pwd)/"
-WVPASS test ! -e restore/src/b
-WVPASS "$top/dev/compare-trees" -c src/a/ restore/src/a/
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-sparse-files b/test/ext/test-sparse-files
new file mode 100755 (executable)
index 0000000..c000784
--- /dev/null
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+mb=1048576
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+readonly mb top tmpdir
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS cd "$tmpdir"
+
+# The 3MB guess is semi-arbitrary, but we've been informed that
+# Lustre, for example, uses 1MB, so guess higher than that, at least.
+block_size=$(bup-cfg-py -c \
+  "import os; print(getattr(os.stat('.'), 'st_blksize', 0)) or $mb * 3") \
+    || exit $?
+data_size=$((block_size * 10))
+readonly block_size data_size
+
+WVPASS dd if=/dev/zero of=test-sparse-probe seek="$data_size" bs=1 count=1
+probe_size=$(WVPASS du -k -s test-sparse-probe | WVPASS cut -f1) || exit $?
+if [ "$probe_size" -ge "$((data_size / 1024))" ]; then
+    WVSTART "no sparse support detected -- skipping tests"
+    exit 0
+fi
+
+WVSTART "sparse restore on $(current-filesystem), assuming ${block_size}B blocks"
+
+WVPASS bup init
+WVPASS mkdir src
+
+WVPASS dd if=/dev/zero of=src/foo seek="$data_size" bs=1 count=1
+WVPASS bup index src
+WVPASS bup save -n src src
+
+WVSTART "sparse file restore (all sparse)"
+WVPASS bup restore -C restore "src/latest/$(pwd)/"
+restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
+WVPASS [ "$restore_size" -ge "$((data_size / 1024))" ]
+WVPASS "$top/dev/compare-trees" -c src/ restore/src/
+
+WVSTART "sparse file restore --no-sparse (all sparse)"
+WVPASS rm -r restore
+WVPASS bup restore --no-sparse -C restore "src/latest/$(pwd)/"
+restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
+WVPASS [ "$restore_size" -ge "$((data_size / 1024))" ]
+WVPASS "$top/dev/compare-trees" -c src/ restore/src/
+
+WVSTART "sparse file restore --sparse (all sparse)"
+WVPASS rm -r restore
+WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
+restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
+WVPASS [ "$restore_size" -le "$((3 * (block_size / 1024)))" ]
+WVPASS "$top/dev/compare-trees" -c src/ restore/src/
+
+WVSTART "sparse file restore --sparse (sparse end)"
+WVPASS echo "start" > src/foo
+WVPASS dd if=/dev/zero of=src/foo seek="$data_size" bs=1 count=1 conv=notrunc
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS rm -r restore
+WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
+restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
+WVPASS [ "$restore_size" -le "$((3 * (block_size / 1024)))" ]
+WVPASS "$top/dev/compare-trees" -c src/ restore/src/
+
+WVSTART "sparse file restore --sparse (sparse middle)"
+WVPASS echo "end" >> src/foo
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS rm -r restore
+WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
+restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
+WVPASS [ "$restore_size" -le "$((5 * (block_size / 1024)))" ]
+WVPASS "$top/dev/compare-trees" -c src/ restore/src/
+
+WVSTART "sparse file restore --sparse (bracketed zero run in buf)"
+WVPASS echo 'x' > src/foo
+WVPASS dd if=/dev/zero bs=1 count=512 >> src/foo
+WVPASS echo 'y' >> src/foo
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS rm -r restore
+WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
+WVPASS "$top/dev/compare-trees" -c src/ restore/src/
+
+WVSTART "sparse file restore --sparse (sparse start)"
+WVPASS dd if=/dev/zero of=src/foo seek="$data_size" bs=1 count=1
+WVPASS echo "end" >> src/foo
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS rm -r restore
+WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
+restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
+WVPASS [ "$restore_size" -le "$((5 * (block_size / 1024)))" ]
+WVPASS "$top/dev/compare-trees" -c src/ restore/src/
+
+WVSTART "sparse file restore --sparse (sparse start and end)"
+WVPASS dd if=/dev/zero of=src/foo seek="$data_size" bs=1 count=1
+WVPASS echo "middle" >> src/foo
+WVPASS dd if=/dev/zero of=src/foo seek=$((2 * data_size)) bs=1 count=1 conv=notrunc
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS rm -r restore
+WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
+restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
+WVPASS [ "$restore_size" -le "$((5 * (block_size / 1024)))" ]
+WVPASS "$top/dev/compare-trees" -c src/ restore/src/
+
+if test "$block_size" -gt $mb; then
+    random_size="$block_size"
+else
+    random_size=1M
+fi
+WVSTART "sparse file restore --sparse (random $random_size)"
+WVPASS bup random --seed "$RANDOM" 1M > src/foo
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS rm -r restore
+WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
+WVPASS "$top/dev/compare-trees" -c src/ restore/src/
+
+WVSTART "sparse file restore --sparse (random sparse regions)"
+WVPASS rm -rf "$BUP_DIR" src
+WVPASS bup init
+WVPASS mkdir src
+for sparse_dataset in 0 1 2 3 4 5 6 7 8 9
+do
+    WVPASS "$top/dev/sparse-test-data" "src/foo-$sparse_dataset"
+done
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS rm -r restore
+WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
+WVPASS "$top/dev/compare-trees" -c src/ restore/src/
+
+WVSTART "sparse file restore --sparse (short zero runs around boundary)"
+WVPASS bup-cfg-py > src/foo <<EOF
+from sys import stdout
+stdout.write("x" * 65535 + "\0")
+stdout.write("\0" + "x" * 65535)
+stdout.write("\0" + "x" * 65534 + "\0")
+stdout.write("x" * 65536)
+stdout.write("\0")
+EOF
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS rm -r restore
+WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
+WVPASS "$top/dev/compare-trees" -c src/ restore/src/
+
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-sparse-files.sh b/test/ext/test-sparse-files.sh
deleted file mode 100755 (executable)
index c000784..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-mb=1048576
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-readonly mb top tmpdir
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS cd "$tmpdir"
-
-# The 3MB guess is semi-arbitrary, but we've been informed that
-# Lustre, for example, uses 1MB, so guess higher than that, at least.
-block_size=$(bup-cfg-py -c \
-  "import os; print(getattr(os.stat('.'), 'st_blksize', 0)) or $mb * 3") \
-    || exit $?
-data_size=$((block_size * 10))
-readonly block_size data_size
-
-WVPASS dd if=/dev/zero of=test-sparse-probe seek="$data_size" bs=1 count=1
-probe_size=$(WVPASS du -k -s test-sparse-probe | WVPASS cut -f1) || exit $?
-if [ "$probe_size" -ge "$((data_size / 1024))" ]; then
-    WVSTART "no sparse support detected -- skipping tests"
-    exit 0
-fi
-
-WVSTART "sparse restore on $(current-filesystem), assuming ${block_size}B blocks"
-
-WVPASS bup init
-WVPASS mkdir src
-
-WVPASS dd if=/dev/zero of=src/foo seek="$data_size" bs=1 count=1
-WVPASS bup index src
-WVPASS bup save -n src src
-
-WVSTART "sparse file restore (all sparse)"
-WVPASS bup restore -C restore "src/latest/$(pwd)/"
-restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
-WVPASS [ "$restore_size" -ge "$((data_size / 1024))" ]
-WVPASS "$top/dev/compare-trees" -c src/ restore/src/
-
-WVSTART "sparse file restore --no-sparse (all sparse)"
-WVPASS rm -r restore
-WVPASS bup restore --no-sparse -C restore "src/latest/$(pwd)/"
-restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
-WVPASS [ "$restore_size" -ge "$((data_size / 1024))" ]
-WVPASS "$top/dev/compare-trees" -c src/ restore/src/
-
-WVSTART "sparse file restore --sparse (all sparse)"
-WVPASS rm -r restore
-WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
-restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
-WVPASS [ "$restore_size" -le "$((3 * (block_size / 1024)))" ]
-WVPASS "$top/dev/compare-trees" -c src/ restore/src/
-
-WVSTART "sparse file restore --sparse (sparse end)"
-WVPASS echo "start" > src/foo
-WVPASS dd if=/dev/zero of=src/foo seek="$data_size" bs=1 count=1 conv=notrunc
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS rm -r restore
-WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
-restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
-WVPASS [ "$restore_size" -le "$((3 * (block_size / 1024)))" ]
-WVPASS "$top/dev/compare-trees" -c src/ restore/src/
-
-WVSTART "sparse file restore --sparse (sparse middle)"
-WVPASS echo "end" >> src/foo
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS rm -r restore
-WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
-restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
-WVPASS [ "$restore_size" -le "$((5 * (block_size / 1024)))" ]
-WVPASS "$top/dev/compare-trees" -c src/ restore/src/
-
-WVSTART "sparse file restore --sparse (bracketed zero run in buf)"
-WVPASS echo 'x' > src/foo
-WVPASS dd if=/dev/zero bs=1 count=512 >> src/foo
-WVPASS echo 'y' >> src/foo
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS rm -r restore
-WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
-WVPASS "$top/dev/compare-trees" -c src/ restore/src/
-
-WVSTART "sparse file restore --sparse (sparse start)"
-WVPASS dd if=/dev/zero of=src/foo seek="$data_size" bs=1 count=1
-WVPASS echo "end" >> src/foo
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS rm -r restore
-WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
-restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
-WVPASS [ "$restore_size" -le "$((5 * (block_size / 1024)))" ]
-WVPASS "$top/dev/compare-trees" -c src/ restore/src/
-
-WVSTART "sparse file restore --sparse (sparse start and end)"
-WVPASS dd if=/dev/zero of=src/foo seek="$data_size" bs=1 count=1
-WVPASS echo "middle" >> src/foo
-WVPASS dd if=/dev/zero of=src/foo seek=$((2 * data_size)) bs=1 count=1 conv=notrunc
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS rm -r restore
-WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
-restore_size=$(WVPASS du -k -s restore/src/foo | WVPASS cut -f1) || exit $?
-WVPASS [ "$restore_size" -le "$((5 * (block_size / 1024)))" ]
-WVPASS "$top/dev/compare-trees" -c src/ restore/src/
-
-if test "$block_size" -gt $mb; then
-    random_size="$block_size"
-else
-    random_size=1M
-fi
-WVSTART "sparse file restore --sparse (random $random_size)"
-WVPASS bup random --seed "$RANDOM" 1M > src/foo
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS rm -r restore
-WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
-WVPASS "$top/dev/compare-trees" -c src/ restore/src/
-
-WVSTART "sparse file restore --sparse (random sparse regions)"
-WVPASS rm -rf "$BUP_DIR" src
-WVPASS bup init
-WVPASS mkdir src
-for sparse_dataset in 0 1 2 3 4 5 6 7 8 9
-do
-    WVPASS "$top/dev/sparse-test-data" "src/foo-$sparse_dataset"
-done
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS rm -r restore
-WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
-WVPASS "$top/dev/compare-trees" -c src/ restore/src/
-
-WVSTART "sparse file restore --sparse (short zero runs around boundary)"
-WVPASS bup-cfg-py > src/foo <<EOF
-from sys import stdout
-stdout.write("x" * 65535 + "\0")
-stdout.write("\0" + "x" * 65535)
-stdout.write("\0" + "x" * 65534 + "\0")
-stdout.write("x" * 65536)
-stdout.write("\0")
-EOF
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS rm -r restore
-WVPASS bup restore --sparse -C restore "src/latest/$(pwd)/"
-WVPASS "$top/dev/compare-trees" -c src/ restore/src/
-
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-split-join b/test/ext/test-split-join
new file mode 100755 (executable)
index 0000000..548f110
--- /dev/null
@@ -0,0 +1,96 @@
+#!/usr/bin/env bash
+. wvtest.sh
+. wvtest-bup.sh
+. dev/lib.sh
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+export BUP_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS cd "$tmpdir"
+
+WVPASS bup init
+
+WVSTART "split --noop"
+WVPASS bup split --noop <"$top/test/testfile1" >noop.tmp
+WVPASSEQ '' "$(<noop.tmp)"
+WVPASS bup split --noop -b <"$top/test/testfile1" >tags1n.tmp
+WVPASS bup split --noop -t <"$top/test/testfile2" >tags2tn.tmp
+WVPASSEQ $(find "$BUP_DIR/objects/pack" -name '*.pack' | wc -l) 0
+
+WVSTART "split"
+WVPASS echo a >a.tmp
+WVPASS echo b >b.tmp
+WVPASS bup split -b a.tmp >taga.tmp
+WVPASS bup split -b b.tmp >tagb.tmp
+WVPASS cat a.tmp b.tmp | WVPASS bup split -b >tagab.tmp
+WVPASSEQ $(cat taga.tmp | wc -l) 1
+WVPASSEQ $(cat tagb.tmp | wc -l) 1
+WVPASSEQ $(cat tagab.tmp | wc -l) 1
+WVPASSEQ $(cat tag[ab].tmp | wc -l) 2
+WVPASSEQ "$(bup split -b a.tmp b.tmp)" "$(cat tagab.tmp)"
+WVPASSEQ "$(bup split -b --keep-boundaries a.tmp b.tmp)" "$(cat tag[ab].tmp)"
+WVPASSEQ "$(cat tag[ab].tmp | bup split -b --keep-boundaries --git-ids)" \
+         "$(cat tag[ab].tmp)"
+WVPASSEQ "$(cat tag[ab].tmp | bup split -b --git-ids)" \
+         "$(cat tagab.tmp)"
+WVPASS bup split --bench -b <"$top/test/testfile1" >tags1.tmp
+WVPASS bup split -vvvv -b "$top/test/testfile2" >tags2.tmp
+WVPASS echo -n "" | WVPASS bup split -n split_empty_string.tmp
+WVPASS bup margin
+WVPASS bup midx -f
+WVPASS bup midx --check -a
+WVPASS bup midx -o "$BUP_DIR/objects/pack/test1.midx" \
+       "$BUP_DIR"/objects/pack/*.idx
+WVPASS bup midx --check -a
+WVPASS bup midx -o "$BUP_DIR"/objects/pack/test1.midx \
+       "$BUP_DIR"/objects/pack/*.idx \
+       "$BUP_DIR"/objects/pack/*.idx
+WVPASS bup midx --check -a
+all=$(echo "$BUP_DIR"/objects/pack/*.idx "$BUP_DIR"/objects/pack/*.midx)
+WVPASS bup midx -o "$BUP_DIR"/objects/pack/zzz.midx $all
+WVPASS bup tick
+WVPASS bup midx -o "$BUP_DIR"/objects/pack/yyy.midx $all
+WVPASS bup midx -a
+WVPASSEQ "$(echo "$BUP_DIR"/objects/pack/*.midx)" \
+       ""$BUP_DIR"/objects/pack/yyy.midx"
+WVPASS bup margin
+WVPASS bup split -t "$top/test/testfile2" >tags2t.tmp
+WVPASS bup split -t "$top/test/testfile2" --fanout 3 >tags2tf.tmp
+WVPASS bup split -r "$BUP_DIR" -c "$top/test/testfile2" >tags2c.tmp
+WVPASS bup split -r ":$BUP_DIR" -c "$top/test/testfile2" >tags2c.tmp
+WVPASS ls -lR \
+    | WVPASS bup split -r ":$BUP_DIR" -c --fanout 3 --max-pack-objects 3 -n lslr \
+    || exit $?
+WVPASS bup ls
+WVFAIL bup ls /does-not-exist
+WVPASS bup ls /lslr
+WVPASS bup ls /lslr/latest
+WVPASS bup ls /lslr/latest/
+#WVPASS bup ls /lslr/1971-01-01   # all dates always exist
+WVFAIL diff -u tags1.tmp tags2.tmp
+WVPASS diff -u tags1.tmp tags1n.tmp
+WVPASS diff -u tags2t.tmp tags2tn.tmp
+
+# fanout must be different from non-fanout
+WVFAIL diff tags2t.tmp tags2tf.tmp
+WVPASS wc -c "$top/test/testfile1" "$top/test/testfile2"
+WVPASS wc -l tags1.tmp tags2.tmp
+
+WVSTART "join"
+WVPASS bup join $(cat tags1.tmp) >out1.tmp
+WVPASS bup join <tags2.tmp >out2.tmp
+WVPASS bup join <tags2t.tmp -o out2t.tmp
+WVPASS bup join -r "$BUP_DIR" <tags2c.tmp >out2c.tmp
+WVPASS bup join -r ":$BUP_DIR" <tags2c.tmp >out2c.tmp
+WVPASS diff -u "$top/test/testfile1" out1.tmp
+WVPASS diff -u "$top/test/testfile2" out2.tmp
+WVPASS diff -u "$top/test/testfile2" out2t.tmp
+WVPASS diff -u "$top/test/testfile2" out2c.tmp
+WVPASSEQ "$(bup join split_empty_string.tmp)" ""
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-split-join.sh b/test/ext/test-split-join.sh
deleted file mode 100755 (executable)
index 548f110..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/usr/bin/env bash
-. wvtest.sh
-. wvtest-bup.sh
-. dev/lib.sh
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-export BUP_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS cd "$tmpdir"
-
-WVPASS bup init
-
-WVSTART "split --noop"
-WVPASS bup split --noop <"$top/test/testfile1" >noop.tmp
-WVPASSEQ '' "$(<noop.tmp)"
-WVPASS bup split --noop -b <"$top/test/testfile1" >tags1n.tmp
-WVPASS bup split --noop -t <"$top/test/testfile2" >tags2tn.tmp
-WVPASSEQ $(find "$BUP_DIR/objects/pack" -name '*.pack' | wc -l) 0
-
-WVSTART "split"
-WVPASS echo a >a.tmp
-WVPASS echo b >b.tmp
-WVPASS bup split -b a.tmp >taga.tmp
-WVPASS bup split -b b.tmp >tagb.tmp
-WVPASS cat a.tmp b.tmp | WVPASS bup split -b >tagab.tmp
-WVPASSEQ $(cat taga.tmp | wc -l) 1
-WVPASSEQ $(cat tagb.tmp | wc -l) 1
-WVPASSEQ $(cat tagab.tmp | wc -l) 1
-WVPASSEQ $(cat tag[ab].tmp | wc -l) 2
-WVPASSEQ "$(bup split -b a.tmp b.tmp)" "$(cat tagab.tmp)"
-WVPASSEQ "$(bup split -b --keep-boundaries a.tmp b.tmp)" "$(cat tag[ab].tmp)"
-WVPASSEQ "$(cat tag[ab].tmp | bup split -b --keep-boundaries --git-ids)" \
-         "$(cat tag[ab].tmp)"
-WVPASSEQ "$(cat tag[ab].tmp | bup split -b --git-ids)" \
-         "$(cat tagab.tmp)"
-WVPASS bup split --bench -b <"$top/test/testfile1" >tags1.tmp
-WVPASS bup split -vvvv -b "$top/test/testfile2" >tags2.tmp
-WVPASS echo -n "" | WVPASS bup split -n split_empty_string.tmp
-WVPASS bup margin
-WVPASS bup midx -f
-WVPASS bup midx --check -a
-WVPASS bup midx -o "$BUP_DIR/objects/pack/test1.midx" \
-       "$BUP_DIR"/objects/pack/*.idx
-WVPASS bup midx --check -a
-WVPASS bup midx -o "$BUP_DIR"/objects/pack/test1.midx \
-       "$BUP_DIR"/objects/pack/*.idx \
-       "$BUP_DIR"/objects/pack/*.idx
-WVPASS bup midx --check -a
-all=$(echo "$BUP_DIR"/objects/pack/*.idx "$BUP_DIR"/objects/pack/*.midx)
-WVPASS bup midx -o "$BUP_DIR"/objects/pack/zzz.midx $all
-WVPASS bup tick
-WVPASS bup midx -o "$BUP_DIR"/objects/pack/yyy.midx $all
-WVPASS bup midx -a
-WVPASSEQ "$(echo "$BUP_DIR"/objects/pack/*.midx)" \
-       ""$BUP_DIR"/objects/pack/yyy.midx"
-WVPASS bup margin
-WVPASS bup split -t "$top/test/testfile2" >tags2t.tmp
-WVPASS bup split -t "$top/test/testfile2" --fanout 3 >tags2tf.tmp
-WVPASS bup split -r "$BUP_DIR" -c "$top/test/testfile2" >tags2c.tmp
-WVPASS bup split -r ":$BUP_DIR" -c "$top/test/testfile2" >tags2c.tmp
-WVPASS ls -lR \
-    | WVPASS bup split -r ":$BUP_DIR" -c --fanout 3 --max-pack-objects 3 -n lslr \
-    || exit $?
-WVPASS bup ls
-WVFAIL bup ls /does-not-exist
-WVPASS bup ls /lslr
-WVPASS bup ls /lslr/latest
-WVPASS bup ls /lslr/latest/
-#WVPASS bup ls /lslr/1971-01-01   # all dates always exist
-WVFAIL diff -u tags1.tmp tags2.tmp
-WVPASS diff -u tags1.tmp tags1n.tmp
-WVPASS diff -u tags2t.tmp tags2tn.tmp
-
-# fanout must be different from non-fanout
-WVFAIL diff tags2t.tmp tags2tf.tmp
-WVPASS wc -c "$top/test/testfile1" "$top/test/testfile2"
-WVPASS wc -l tags1.tmp tags2.tmp
-
-WVSTART "join"
-WVPASS bup join $(cat tags1.tmp) >out1.tmp
-WVPASS bup join <tags2.tmp >out2.tmp
-WVPASS bup join <tags2t.tmp -o out2t.tmp
-WVPASS bup join -r "$BUP_DIR" <tags2c.tmp >out2c.tmp
-WVPASS bup join -r ":$BUP_DIR" <tags2c.tmp >out2c.tmp
-WVPASS diff -u "$top/test/testfile1" out1.tmp
-WVPASS diff -u "$top/test/testfile2" out2.tmp
-WVPASS diff -u "$top/test/testfile2" out2t.tmp
-WVPASS diff -u "$top/test/testfile2" out2c.tmp
-WVPASSEQ "$(bup join split_empty_string.tmp)" ""
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-tz b/test/ext/test-tz
new file mode 100755 (executable)
index 0000000..4b566b1
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+WVSTART "half hour TZ"
+
+export TZ=ACDT-10:30
+
+WVPASS bup init
+WVPASS cd "$tmpdir"
+
+WVPASS mkdir src
+WVPASS bup index src
+WVPASS bup save -n src -d 1420164180 src
+
+WVPASSEQ "$(WVPASS git cat-file commit src | sed -ne 's/^author .*> //p')" \
+"1420164180 +1030"
+
+WVPASSEQ "$(WVPASS bup ls /src)" \
+"2015-01-02-123300
+latest"
+
+WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-tz.sh b/test/ext/test-tz.sh
deleted file mode 100755 (executable)
index 4b566b1..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-
-set -o pipefail
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-WVSTART "half hour TZ"
-
-export TZ=ACDT-10:30
-
-WVPASS bup init
-WVPASS cd "$tmpdir"
-
-WVPASS mkdir src
-WVPASS bup index src
-WVPASS bup save -n src -d 1420164180 src
-
-WVPASSEQ "$(WVPASS git cat-file commit src | sed -ne 's/^author .*> //p')" \
-"1420164180 +1030"
-
-WVPASSEQ "$(WVPASS bup ls /src)" \
-"2015-01-02-123300
-latest"
-
-WVPASS rm -rf "$tmpdir"
diff --git a/test/ext/test-web b/test/ext/test-web
new file mode 100755 (executable)
index 0000000..eab092c
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/env bash
+. wvtest-bup.sh || exit $?
+. dev/lib.sh || exit $?
+
+set -o pipefail
+
+TOP="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+export BUP_DIR="$tmpdir/bup"
+
+bup()
+{
+    "$TOP/bup" "$@"
+}
+
+wait-for-server-start()
+{
+    curl --unix-socket ./socket http://localhost/
+    curl_status=$?
+    while test $curl_status -eq 7; do
+        sleep 0.2
+        curl --unix-socket ./socket http://localhost/
+        curl_status=$?
+    done
+    WVPASSEQ $curl_status 0
+}
+
+WVPASS cd "$tmpdir"
+
+# FIXME: add WVSKIP
+if test -z "$(type -p curl)"; then
+    WVSTART 'curl does not appear to be installed; skipping  test'
+    exit 0
+fi
+    
+WVPASS bup-cfg-py -c "import socket as s; s.socket(s.AF_UNIX).bind('socket')"
+curl -s --unix-socket ./socket http://localhost/foo
+if test $? -ne 7; then
+    WVSTART 'curl does not appear to support --unix-socket; skipping test'
+    exit 0
+fi
+
+if ! bup-python -c 'import tornado' 2> /dev/null; then
+    WVSTART 'unable to import tornado; skipping test'
+    exit 0
+fi
+
+WVSTART 'web'
+WVPASS bup init
+WVPASS mkdir src
+WVPASS echo '¡excitement!' > src/data
+WVPASS echo -e 'whee \x80\x90\xff' > "$(echo -ne 'src/whee \x80\x90\xff')"
+WVPASS bup index src
+WVPASS bup save -n '¡excitement!' --strip src
+
+"$TOP/bup" web unix://socket &
+web_pid=$!
+wait-for-server-start
+
+WVPASS curl --unix-socket ./socket \
+       'http://localhost/%C2%A1excitement%21/latest/data' > result
+WVPASS curl --unix-socket ./socket \
+       'http://localhost/%C2%A1excitement%21/latest/whee%20%80%90%ff' > result2
+WVPASSEQ "$(curl --unix-socket ./socket http://localhost/static/styles.css)" \
+         "$(cat "$TOP/lib/web/static/styles.css")"
+
+WVPASSEQ '¡excitement!' "$(cat result)"
+WVPASS cmp "$(echo -ne 'src/whee \x80\x90\xff')" result2
+WVPASS kill -s TERM "$web_pid"
+WVPASS wait "$web_pid"
+
+WVPASS rm -r "$tmpdir"
diff --git a/test/ext/test-web.sh b/test/ext/test-web.sh
deleted file mode 100755 (executable)
index eab092c..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env bash
-. wvtest-bup.sh || exit $?
-. dev/lib.sh || exit $?
-
-set -o pipefail
-
-TOP="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-export BUP_DIR="$tmpdir/bup"
-
-bup()
-{
-    "$TOP/bup" "$@"
-}
-
-wait-for-server-start()
-{
-    curl --unix-socket ./socket http://localhost/
-    curl_status=$?
-    while test $curl_status -eq 7; do
-        sleep 0.2
-        curl --unix-socket ./socket http://localhost/
-        curl_status=$?
-    done
-    WVPASSEQ $curl_status 0
-}
-
-WVPASS cd "$tmpdir"
-
-# FIXME: add WVSKIP
-if test -z "$(type -p curl)"; then
-    WVSTART 'curl does not appear to be installed; skipping  test'
-    exit 0
-fi
-    
-WVPASS bup-cfg-py -c "import socket as s; s.socket(s.AF_UNIX).bind('socket')"
-curl -s --unix-socket ./socket http://localhost/foo
-if test $? -ne 7; then
-    WVSTART 'curl does not appear to support --unix-socket; skipping test'
-    exit 0
-fi
-
-if ! bup-python -c 'import tornado' 2> /dev/null; then
-    WVSTART 'unable to import tornado; skipping test'
-    exit 0
-fi
-
-WVSTART 'web'
-WVPASS bup init
-WVPASS mkdir src
-WVPASS echo '¡excitement!' > src/data
-WVPASS echo -e 'whee \x80\x90\xff' > "$(echo -ne 'src/whee \x80\x90\xff')"
-WVPASS bup index src
-WVPASS bup save -n '¡excitement!' --strip src
-
-"$TOP/bup" web unix://socket &
-web_pid=$!
-wait-for-server-start
-
-WVPASS curl --unix-socket ./socket \
-       'http://localhost/%C2%A1excitement%21/latest/data' > result
-WVPASS curl --unix-socket ./socket \
-       'http://localhost/%C2%A1excitement%21/latest/whee%20%80%90%ff' > result2
-WVPASSEQ "$(curl --unix-socket ./socket http://localhost/static/styles.css)" \
-         "$(cat "$TOP/lib/web/static/styles.css")"
-
-WVPASSEQ '¡excitement!' "$(cat result)"
-WVPASS cmp "$(echo -ne 'src/whee \x80\x90\xff')" result2
-WVPASS kill -s TERM "$web_pid"
-WVPASS wait "$web_pid"
-
-WVPASS rm -r "$tmpdir"
diff --git a/test/ext/test-xdev b/test/ext/test-xdev
new file mode 100755 (executable)
index 0000000..78861a9
--- /dev/null
@@ -0,0 +1,156 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+root_status="$(dev/root-status)" || exit $?
+
+if [ "$root_status" != root ]; then
+    WVSTART 'not root: skipping tests'
+    exit 0 # FIXME: add WVSKIP.
+fi
+
+if ! modprobe loop; then
+    WVSTART 'unable to load loopback module; skipping tests' 1>&2
+    exit 0
+fi
+
+# These tests are only likely to work under Linux for now
+# (patches welcome).
+if ! [[ $(uname) =~ Linux ]]; then
+    WVSTART 'not Linux: skipping tests'
+    exit 0 # FIXME: add WVSKIP.
+fi
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS bup init
+WVPASS pushd "$tmpdir"
+
+WVSTART 'drecurse'
+
+WVPASS dd if=/dev/zero of=testfs-1.img bs=1M count=32
+WVPASS dd if=/dev/zero of=testfs-2.img bs=1M count=32
+WVPASS mkfs -F testfs-1.img # Don't care what type (though must have symlinks)
+WVPASS mkfs -F testfs-2.img # Don't care what type (though must have symlinks)
+WVPASS mkdir -p src/mnt-1/hidden-1 src/mnt-2/hidden-2
+WVPASS mount -o loop testfs-1.img src/mnt-1
+WVPASS mount -o loop testfs-2.img src/mnt-2
+
+WVPASS touch src/1
+
+WVPASS mkdir -p src/mnt-1/x
+WVPASS touch src/mnt-1/2 src/mnt-1/x/3
+
+WVPASS touch src/mnt-2/4
+
+(WVPASS cd src && WVPASS ln -s mnt-2 mnt-link)
+(WVPASS cd src && WVPASS ln -s . top)
+
+WVPASSEQ "$(bup drecurse src | grep -vF lost+found)" "src/top
+src/mnt-link
+src/mnt-2/4
+src/mnt-2/
+src/mnt-1/x/3
+src/mnt-1/x/
+src/mnt-1/2
+src/mnt-1/
+src/1
+src/"
+
+WVPASSEQ "$(bup drecurse -x src)" "src/top
+src/mnt-link
+src/mnt-2/
+src/mnt-1/
+src/1
+src/"
+
+WVSTART 'index/save/restore'
+
+WVPASS bup index src
+WVPASS bup save -n src src
+WVPASS mkdir src-restore
+WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
+WVPASS test -d src-restore/src
+WVPASS "$top/dev/compare-trees" -c src/ src-restore/src/
+
+# Test -x when none of the mount points are explicitly indexed
+WVPASS rm -r "$BUP_DIR" src-restore
+WVPASS bup init
+WVPASS bup index -x src
+WVPASS bup save -n src src
+WVPASS mkdir src-restore
+WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
+WVPASS test -d src-restore/src
+WVPASSEQ "$(cd src-restore/src && find . -not -name lost+found | LC_ALL=C sort)" \
+".
+./1
+./mnt-1
+./mnt-2
+./mnt-link
+./top"
+
+# Test -x when a mount point is explicitly indexed.  This should
+# include the mount.
+WVPASS rm -r "$BUP_DIR" src-restore
+WVPASS bup init
+WVPASS bup index -x src src/mnt-2
+WVPASS bup save -n src src
+WVPASS mkdir src-restore
+WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
+WVPASS test -d src-restore/src
+WVPASSEQ "$(cd src-restore/src && find . -not -name lost+found | LC_ALL=C sort)" \
+".
+./1
+./mnt-1
+./mnt-2
+./mnt-2/4
+./mnt-link
+./top"
+
+# Test -x when a direct link to a mount point is explicitly indexed.
+# This should *not* include the mount.
+WVPASS rm -r "$BUP_DIR" src-restore
+WVPASS bup init
+WVPASS bup index -x src src/mnt-link
+WVPASS bup save -n src src
+WVPASS mkdir src-restore
+WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
+WVPASS test -d src-restore/src
+WVPASSEQ "$(cd src-restore/src && find . -not -name lost+found | LC_ALL=C sort)" \
+".
+./1
+./mnt-1
+./mnt-2
+./mnt-link
+./top"
+
+# Test -x when a path that resolves to a mount point is explicitly
+# indexed (i.e. dir symlnks that redirect the leaf to a mount point).
+# This should include the mount.
+WVPASS rm -r "$BUP_DIR" src-restore
+WVPASS bup init
+WVPASS bup index -x src src/top/top/mnt-2
+WVPASS bup save -n src src
+WVPASS mkdir src-restore
+WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
+WVPASS test -d src-restore/src
+WVPASSEQ "$(cd src-restore/src && find . -not -name lost+found | LC_ALL=C sort)" \
+".
+./1
+./mnt-1
+./mnt-2
+./mnt-2/4
+./mnt-link
+./top"
+
+WVPASS cd "$top"
+WVPASS umount "$tmpdir/src/mnt-1"
+WVPASS umount "$tmpdir/src/mnt-2"
+WVPASS rm -r "$tmpdir"
diff --git a/test/ext/test-xdev.sh b/test/ext/test-xdev.sh
deleted file mode 100755 (executable)
index 78861a9..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-#!/usr/bin/env bash
-. ./wvtest-bup.sh || exit $?
-
-set -o pipefail
-
-root_status="$(dev/root-status)" || exit $?
-
-if [ "$root_status" != root ]; then
-    WVSTART 'not root: skipping tests'
-    exit 0 # FIXME: add WVSKIP.
-fi
-
-if ! modprobe loop; then
-    WVSTART 'unable to load loopback module; skipping tests' 1>&2
-    exit 0
-fi
-
-# These tests are only likely to work under Linux for now
-# (patches welcome).
-if ! [[ $(uname) =~ Linux ]]; then
-    WVSTART 'not Linux: skipping tests'
-    exit 0 # FIXME: add WVSKIP.
-fi
-
-top="$(WVPASS pwd)" || exit $?
-tmpdir="$(WVPASS wvmktempdir)" || exit $?
-
-export BUP_DIR="$tmpdir/bup"
-export GIT_DIR="$tmpdir/bup"
-
-bup() { "$top/bup" "$@"; }
-
-WVPASS bup init
-WVPASS pushd "$tmpdir"
-
-WVSTART 'drecurse'
-
-WVPASS dd if=/dev/zero of=testfs-1.img bs=1M count=32
-WVPASS dd if=/dev/zero of=testfs-2.img bs=1M count=32
-WVPASS mkfs -F testfs-1.img # Don't care what type (though must have symlinks)
-WVPASS mkfs -F testfs-2.img # Don't care what type (though must have symlinks)
-WVPASS mkdir -p src/mnt-1/hidden-1 src/mnt-2/hidden-2
-WVPASS mount -o loop testfs-1.img src/mnt-1
-WVPASS mount -o loop testfs-2.img src/mnt-2
-
-WVPASS touch src/1
-
-WVPASS mkdir -p src/mnt-1/x
-WVPASS touch src/mnt-1/2 src/mnt-1/x/3
-
-WVPASS touch src/mnt-2/4
-
-(WVPASS cd src && WVPASS ln -s mnt-2 mnt-link)
-(WVPASS cd src && WVPASS ln -s . top)
-
-WVPASSEQ "$(bup drecurse src | grep -vF lost+found)" "src/top
-src/mnt-link
-src/mnt-2/4
-src/mnt-2/
-src/mnt-1/x/3
-src/mnt-1/x/
-src/mnt-1/2
-src/mnt-1/
-src/1
-src/"
-
-WVPASSEQ "$(bup drecurse -x src)" "src/top
-src/mnt-link
-src/mnt-2/
-src/mnt-1/
-src/1
-src/"
-
-WVSTART 'index/save/restore'
-
-WVPASS bup index src
-WVPASS bup save -n src src
-WVPASS mkdir src-restore
-WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
-WVPASS test -d src-restore/src
-WVPASS "$top/dev/compare-trees" -c src/ src-restore/src/
-
-# Test -x when none of the mount points are explicitly indexed
-WVPASS rm -r "$BUP_DIR" src-restore
-WVPASS bup init
-WVPASS bup index -x src
-WVPASS bup save -n src src
-WVPASS mkdir src-restore
-WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
-WVPASS test -d src-restore/src
-WVPASSEQ "$(cd src-restore/src && find . -not -name lost+found | LC_ALL=C sort)" \
-".
-./1
-./mnt-1
-./mnt-2
-./mnt-link
-./top"
-
-# Test -x when a mount point is explicitly indexed.  This should
-# include the mount.
-WVPASS rm -r "$BUP_DIR" src-restore
-WVPASS bup init
-WVPASS bup index -x src src/mnt-2
-WVPASS bup save -n src src
-WVPASS mkdir src-restore
-WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
-WVPASS test -d src-restore/src
-WVPASSEQ "$(cd src-restore/src && find . -not -name lost+found | LC_ALL=C sort)" \
-".
-./1
-./mnt-1
-./mnt-2
-./mnt-2/4
-./mnt-link
-./top"
-
-# Test -x when a direct link to a mount point is explicitly indexed.
-# This should *not* include the mount.
-WVPASS rm -r "$BUP_DIR" src-restore
-WVPASS bup init
-WVPASS bup index -x src src/mnt-link
-WVPASS bup save -n src src
-WVPASS mkdir src-restore
-WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
-WVPASS test -d src-restore/src
-WVPASSEQ "$(cd src-restore/src && find . -not -name lost+found | LC_ALL=C sort)" \
-".
-./1
-./mnt-1
-./mnt-2
-./mnt-link
-./top"
-
-# Test -x when a path that resolves to a mount point is explicitly
-# indexed (i.e. dir symlnks that redirect the leaf to a mount point).
-# This should include the mount.
-WVPASS rm -r "$BUP_DIR" src-restore
-WVPASS bup init
-WVPASS bup index -x src src/top/top/mnt-2
-WVPASS bup save -n src src
-WVPASS mkdir src-restore
-WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
-WVPASS test -d src-restore/src
-WVPASSEQ "$(cd src-restore/src && find . -not -name lost+found | LC_ALL=C sort)" \
-".
-./1
-./mnt-1
-./mnt-2
-./mnt-2/4
-./mnt-link
-./top"
-
-WVPASS cd "$top"
-WVPASS umount "$tmpdir/src/mnt-1"
-WVPASS umount "$tmpdir/src/mnt-2"
-WVPASS rm -r "$tmpdir"