github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/file/fsnodefuse/dir_test.go (about) 1 package fsnodefuse 2 3 import ( 4 "context" 5 "io/ioutil" 6 "os" 7 "path" 8 "strconv" 9 "testing" 10 "time" 11 12 "github.com/Schaudge/grailbase/file/fsnode" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 ) 16 17 // TestLookupCaching checks that Lookup doesn't return stale nodes (past their cache time). 18 // It's a regression test. 19 func TestLookupCaching(t *testing.T) { 20 const ( 21 childName = "time" 22 cacheFor = time.Millisecond 23 waitSlack = 500 * time.Millisecond 24 ) 25 // root is a directory with one child. Each time directory listing is initiated, the content 26 // of the child is fixed as the current time. 27 root := fsnode.NewParent( 28 fsnode.NewDirInfo(""), 29 fsnode.FuncChildren(func(ctx context.Context) ([]fsnode.T, error) { 30 nowUnixNanos := time.Now().UnixNano() 31 return []fsnode.T{ 32 fsnode.ConstLeaf( 33 fsnode.NewRegInfo(childName).WithCacheableFor(cacheFor), 34 []byte(strconv.FormatInt(nowUnixNanos, 10)), 35 ), 36 }, nil 37 }), 38 ) 39 withMounted(t, root, func(rootPath string) { 40 childPath := path.Join(rootPath, childName) 41 // Trigger a directory listing and read the event time from the file. 42 listingTime := readUnixNanosFile(t, childPath) 43 // Wait until that time has passed. 44 // Note: We have to use wall clock time here, not mock, because we're interested in kernel 45 // inode caching interactions. 46 // TODO: Is there a way to guarantee that entry cache time has elapsed, for robustness? 47 waitTime := listingTime.Add(waitSlack) 48 sleep := waitTime.Sub(time.Now()) 49 time.Sleep(sleep) 50 secondListingTime := readUnixNanosFile(t, childPath) 51 assert.NotEqual(t, 52 listingTime.UnixNano(), secondListingTime.UnixNano(), 53 "second listing should have different timestamp", 54 ) 55 }) 56 } 57 58 func readUnixNanosFile(t *testing.T, filePath string) time.Time { 59 child, err := os.Open(filePath) 60 require.NoError(t, err) 61 defer func() { assert.NoError(t, child.Close()) }() 62 content, err := ioutil.ReadAll(child) 63 require.NoError(t, err) 64 listingUnixNano, err := strconv.ParseInt(string(content), 10, 64) 65 require.NoError(t, err) 66 return time.Unix(0, listingUnixNano) 67 }