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  }