1 Commits
Author SHA1 Message Date
sneak 5f51a4da78 Stop the disk size tests failing at random on ZFS (closes #150)
check / check (push) Canceled after 0s
One ZFS commit can take a file through several sizes, and unix.Sync can
return before the blocks are counted. A test that expected the size on
disk to be settled therefore failed at random. The test that needs the
size to change now calls unix.Sync in a loop until it does, for at most
30 seconds. It then rescans until the size reads the same before and
after a rescan, and compares disk_size exactly, within the same 30
seconds. The other two tests fail only on a disk_size outside their two
reads. They call unix.Sync once first, so a wrong 512 usually fails them.

Model: opus-5-5
2026-11-03 02:55:48 +00:00
2 changed files with 38 additions and 16 deletions
+3 -2
View File
@@ -33,8 +33,9 @@ are known.
`unix.Sync` can return before ZFS has counted a file's blocks, and one ZFS
commit can take a file through several sizes. The test that needs the size to
change waits for it, for at most 30 seconds. Each test reads the size before
and after its scan. It fails only on a `disk_size` outside those two reads
(2026-11-02, https://git.eeqj.de/sneak/sfdupes/issues/150)
and after its scan. The rewrite test rescans until the two reads match, then
compares exactly. The other two fail only on a `disk_size` outside the two
reads (2026-11-02, https://git.eeqj.de/sneak/sfdupes/issues/150)
- `TODO.md` rewritten from scratch by the same rules as `README.md`, sentence by
sentence. Every completed step is kept, with its date and its issue or branch.
+35 -14
View File
@@ -50,8 +50,9 @@ func writeFile(t *testing.T, dir, name string, data []byte) string {
// but a unix.Sync call often takes long enough that the blocks are
// counted when it returns. A test that needs the value to change waits
// for the change. A test that compares it with a scan's disk_size reads
// it before and after the scan, and fails only on a disk_size outside
// those two reads.
// it before and after the scan. The rewrite test rescans until the two
// reads match, then compares exactly. The other two tests fail only on
// a disk_size outside the two reads.
func diskSizeOf(t *testing.T, path string) int64 {
t.Helper()
@@ -1514,22 +1515,42 @@ func TestSyncScanRewriteUpdatesDiskSize(t *testing.T) {
unix.Sync()
}
// ZFS may still be counting, so the size may change during the rescan.
// A rescan that keeps the old size still fails, because the wait made
// before differ from it.
before := diskSizeOf(t, p)
// ZFS may still be counting, so the size may change during a rescan.
// Only a rescan with the same size on both sides of it can be compared
// exactly. A rescan that keeps the old size still fails, because the
// wait made before differ from it.
for {
before := diskSizeOf(t, p)
st := syncTree(t, db, dir)
st := syncTree(t, db, dir)
after := diskSizeOf(t, p)
after := diskSizeOf(t, p)
if st != (scanStats{walked: 1, updated: 1}) {
t.Fatalf("rescan stats = %+v, want 1 updated", st)
}
if st != (scanStats{walked: 1, updated: 1}) {
t.Fatalf("rescan stats = %+v, want 1 updated", st)
}
got := recordByPath(t, dbRecords(t, db), p).diskSize
if got < before || got > after {
t.Errorf("disk_size = %d, want %d to %d", got, before, after)
if before == after {
got := recordByPath(t, dbRecords(t, db), p).diskSize
if got != after {
t.Errorf("disk_size = %d, want %d", got, after)
}
return
}
if time.Now().After(deadline) {
t.Fatalf("the size on disk was still changing 30 seconds after "+
"the write into its hole: %d, then %d", before, after)
}
// A later mtime makes the next rescan read the file again.
later = later.Add(time.Second)
err = os.Chtimes(p, later, later)
if err != nil {
t.Fatal(err)
}
}
}