Files
restic/internal/fuse/tree_cache_test.go
T
Ricardo Sawir 55e335ec6c fuse: reset treeCache on snapshot reload to fix stale latest symlink (#21873)
The treeCache in SnapshotsDir was never cleared when snapshots were
reloaded. This caused the "latest" symlink to keep pointing to the
previous snapshot even after new snapshots were added.

Add a generation counter to SnapshotsDirStructure that is incremented
whenever the directory structure is rebuilt (in makeDirs). The
treeCache checks this generation on each lookup and resets itself
when the generation changes, ensuring cached nodes (including symlinks)
are refreshed after a snapshot reload.
2026-06-22 18:31:51 +00:00

83 lines
2.2 KiB
Go

//go:build darwin || freebsd || linux
package fuse
import (
"context"
"testing"
"github.com/anacrolix/fuse"
"github.com/anacrolix/fuse/fs"
"github.com/restic/restic/internal/test"
)
type cacheTestNode struct {
id int
}
func (n cacheTestNode) Attr(context.Context, *fuse.Attr) error {
return nil
}
func TestTreeCacheGeneration(t *testing.T) {
cache := newTreeCache()
created := 0
create := func(forgetFn) (fs.Node, error) {
created++
return cacheTestNode{id: created}, nil
}
node1, err := cache.lookupOrCreate("node", 1, create)
test.OK(t, err)
node2, err := cache.lookupOrCreate("node", 1, create)
test.OK(t, err)
test.Assert(t, node1 == node2, "lookup should reuse cached node")
test.Equals(t, 1, created)
node3, err := cache.lookupOrCreate("node", 2, create)
test.OK(t, err)
test.Assert(t, node1 != node3, "lookup should recreate node after generation change")
test.Equals(t, 2, created)
node4, err := cache.lookupOrCreate("node", -1, create)
test.OK(t, err)
test.Assert(t, node3 == node4, "negative generation should not reset cache")
test.Equals(t, 2, created)
node5, err := cache.lookupOrCreate("node", 3, create)
test.OK(t, err)
test.Assert(t, node4 != node5, "lookup should still track later generation changes")
test.Equals(t, 3, created)
}
func TestTreeCacheForgetOnlyRemovesSameGeneration(t *testing.T) {
cache := newTreeCache()
created := 0
var forgets []forgetFn
create := func(forget forgetFn) (fs.Node, error) {
created++
forgets = append(forgets, forget)
return cacheTestNode{id: created}, nil
}
node1, err := cache.lookupOrCreate("node", 1, create)
test.OK(t, err)
node2, err := cache.lookupOrCreate("node", 2, create)
test.OK(t, err)
test.Assert(t, node1 != node2, "lookup should recreate node after generation change")
test.Equals(t, 2, created)
forgets[0]()
node3, err := cache.lookupOrCreate("node", 2, create)
test.OK(t, err)
test.Assert(t, node2 == node3, "forget from an old generation must not remove the current node")
test.Equals(t, 2, created)
forgets[1]()
node4, err := cache.lookupOrCreate("node", 2, create)
test.OK(t, err)
test.Assert(t, node3 != node4, "forget from the current generation should remove the cached node")
test.Equals(t, 3, created)
}