2024-02-02 18:45:32 +00:00
|
|
|
// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
|
|
package webdavfs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2024-02-09 17:26:43 +00:00
|
|
|
"tailscale.com/tailfs/tailfsimpl/shared"
|
2024-02-02 18:45:32 +00:00
|
|
|
"tailscale.com/tstest"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestStatCache(t *testing.T) {
|
|
|
|
// Make sure we don't leak goroutines
|
|
|
|
tstest.ResourceCheck(t)
|
|
|
|
|
|
|
|
dir, err := os.MkdirTemp("", "")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create file of size 1
|
|
|
|
filename := filepath.Join(dir, "thefile")
|
|
|
|
err = os.WriteFile(filename, []byte("1"), 0644)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
stat := func(name string) (os.FileInfo, error) {
|
|
|
|
return os.Stat(name)
|
|
|
|
}
|
|
|
|
ttl := 1 * time.Second
|
|
|
|
c := newStatCache(ttl)
|
|
|
|
|
|
|
|
// fetch new stat
|
|
|
|
fi, err := c.getOrFetch(filename, stat)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if fi.Size() != 1 {
|
|
|
|
t.Errorf("got size %d, want 1", fi.Size())
|
|
|
|
}
|
|
|
|
// save original FileInfo as a StaticFileInfo so we can reuse it later
|
|
|
|
// without worrying about the underlying FileInfo changing.
|
|
|
|
originalFI := &shared.StaticFileInfo{
|
|
|
|
Named: fi.Name(),
|
|
|
|
Sized: fi.Size(),
|
|
|
|
Moded: fi.Mode(),
|
|
|
|
ModdedTime: fi.ModTime(),
|
|
|
|
Dir: fi.IsDir(),
|
|
|
|
}
|
|
|
|
|
|
|
|
// update file to size 2
|
|
|
|
err = os.WriteFile(filename, []byte("12"), 0644)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// fetch stat again, should still be cached
|
|
|
|
fi, err = c.getOrFetch(filename, stat)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if fi.Size() != 1 {
|
|
|
|
t.Errorf("got size %d, want 1", fi.Size())
|
|
|
|
}
|
|
|
|
|
|
|
|
// wait for cache to expire and refetch stat, size should reflect new size
|
|
|
|
time.Sleep(ttl * 2)
|
|
|
|
|
|
|
|
fi, err = c.getOrFetch(filename, stat)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if fi.Size() != 2 {
|
|
|
|
t.Errorf("got size %d, want 2", fi.Size())
|
|
|
|
}
|
|
|
|
|
|
|
|
// explicitly set the original FileInfo and make sure it's returned
|
|
|
|
c.set(dir, []fs.FileInfo{originalFI})
|
|
|
|
fi, err = c.getOrFetch(filename, stat)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if fi.Size() != 1 {
|
|
|
|
t.Errorf("got size %d, want 1", fi.Size())
|
|
|
|
}
|
|
|
|
|
|
|
|
// invalidate the cache and make sure the new size is returned
|
|
|
|
c.invalidate()
|
|
|
|
fi, err = c.getOrFetch(filename, stat)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if fi.Size() != 2 {
|
|
|
|
t.Errorf("got size %d, want 2", fi.Size())
|
|
|
|
}
|
|
|
|
|
|
|
|
c.stop()
|
|
|
|
}
|