github.com/scaleoutsean/fusego@v0.0.0-20220224074057-4a6429e46bb8/samples/cachingfs/caching_fs_test.go (about)

     1  // Copyright 2015 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cachingfs_test
    16  
    17  import (
    18  	"bytes"
    19  	"io/ioutil"
    20  	"os"
    21  	"path"
    22  	"runtime"
    23  	"syscall"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/scaleoutsean/fusego/fuseutil"
    28  	"github.com/scaleoutsean/fusego/samples"
    29  	"github.com/scaleoutsean/fusego/samples/cachingfs"
    30  	. "github.com/jacobsa/oglematchers"
    31  	. "github.com/jacobsa/ogletest"
    32  	"github.com/jacobsa/timeutil"
    33  )
    34  
    35  func TestCachingFS(t *testing.T) { RunTests(t) }
    36  
    37  ////////////////////////////////////////////////////////////////////////
    38  // Boilerplate
    39  ////////////////////////////////////////////////////////////////////////
    40  
    41  type cachingFSTest struct {
    42  	samples.SampleTest
    43  
    44  	fs           cachingfs.CachingFS
    45  	initialMtime time.Time
    46  }
    47  
    48  var _ TearDownInterface = &cachingFSTest{}
    49  
    50  func (t *cachingFSTest) setUp(
    51  	ti *TestInfo,
    52  	lookupEntryTimeout time.Duration,
    53  	getattrTimeout time.Duration) {
    54  	var err error
    55  
    56  	// We assert things about whether or not mtimes are cached, but writeback
    57  	// caching causes them to always be cached. Turn it off.
    58  	t.MountConfig.DisableWritebackCaching = true
    59  
    60  	// Create the file system.
    61  	t.fs, err = cachingfs.NewCachingFS(lookupEntryTimeout, getattrTimeout)
    62  	AssertEq(nil, err)
    63  
    64  	t.Server = fuseutil.NewFileSystemServer(t.fs)
    65  
    66  	// Mount it.
    67  	t.SampleTest.SetUp(ti)
    68  
    69  	// Set up the mtime.
    70  	t.initialMtime = time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local)
    71  	t.fs.SetMtime(t.initialMtime)
    72  }
    73  
    74  func (t *cachingFSTest) statAll() (foo, dir, bar os.FileInfo) {
    75  	var err error
    76  
    77  	foo, err = os.Stat(path.Join(t.Dir, "foo"))
    78  	AssertEq(nil, err)
    79  
    80  	dir, err = os.Stat(path.Join(t.Dir, "dir"))
    81  	AssertEq(nil, err)
    82  
    83  	bar, err = os.Stat(path.Join(t.Dir, "dir/bar"))
    84  	AssertEq(nil, err)
    85  
    86  	return foo, dir, bar
    87  }
    88  
    89  func (t *cachingFSTest) openFiles() (foo, dir, bar *os.File) {
    90  	var err error
    91  
    92  	foo, err = os.Open(path.Join(t.Dir, "foo"))
    93  	AssertEq(nil, err)
    94  
    95  	dir, err = os.Open(path.Join(t.Dir, "dir"))
    96  	AssertEq(nil, err)
    97  
    98  	bar, err = os.Open(path.Join(t.Dir, "dir/bar"))
    99  	AssertEq(nil, err)
   100  
   101  	return foo, dir, bar
   102  }
   103  
   104  func (t *cachingFSTest) statFiles(
   105  	f, g, h *os.File) (foo, dir, bar os.FileInfo) {
   106  	var err error
   107  
   108  	foo, err = f.Stat()
   109  	AssertEq(nil, err)
   110  
   111  	dir, err = g.Stat()
   112  	AssertEq(nil, err)
   113  
   114  	bar, err = h.Stat()
   115  	AssertEq(nil, err)
   116  
   117  	return foo, dir, bar
   118  }
   119  
   120  func getInodeID(fi os.FileInfo) uint64 {
   121  	return fi.Sys().(*syscall.Stat_t).Ino
   122  }
   123  
   124  ////////////////////////////////////////////////////////////////////////
   125  // Basics
   126  ////////////////////////////////////////////////////////////////////////
   127  
   128  type BasicsTest struct {
   129  	cachingFSTest
   130  }
   131  
   132  var _ SetUpInterface = &BasicsTest{}
   133  
   134  func init() { RegisterTestSuite(&BasicsTest{}) }
   135  
   136  func (t *BasicsTest) SetUp(ti *TestInfo) {
   137  	const (
   138  		lookupEntryTimeout = 0
   139  		getattrTimeout     = 0
   140  	)
   141  
   142  	t.cachingFSTest.setUp(ti, lookupEntryTimeout, getattrTimeout)
   143  }
   144  
   145  func (t *BasicsTest) StatNonexistent() {
   146  	names := []string{
   147  		"blah",
   148  		"bar",
   149  		"dir/blah",
   150  		"dir/dir",
   151  		"dir/foo",
   152  	}
   153  
   154  	for _, n := range names {
   155  		_, err := os.Stat(path.Join(t.Dir, n))
   156  
   157  		AssertNe(nil, err)
   158  		ExpectTrue(os.IsNotExist(err), "n: %s, err: %v", n, err)
   159  	}
   160  }
   161  
   162  func (t *BasicsTest) StatFoo() {
   163  	fi, err := os.Stat(path.Join(t.Dir, "foo"))
   164  	AssertEq(nil, err)
   165  
   166  	ExpectEq("foo", fi.Name())
   167  	ExpectEq(cachingfs.FooSize, fi.Size())
   168  	ExpectEq(0777, fi.Mode())
   169  	ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime))
   170  	ExpectFalse(fi.IsDir())
   171  	ExpectEq(t.fs.FooID(), getInodeID(fi))
   172  	ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
   173  }
   174  
   175  func (t *BasicsTest) StatDir() {
   176  	fi, err := os.Stat(path.Join(t.Dir, "dir"))
   177  	AssertEq(nil, err)
   178  
   179  	ExpectEq("dir", fi.Name())
   180  	ExpectEq(os.ModeDir|0777, fi.Mode())
   181  	ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime))
   182  	ExpectTrue(fi.IsDir())
   183  	ExpectEq(t.fs.DirID(), getInodeID(fi))
   184  	ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
   185  }
   186  
   187  func (t *BasicsTest) StatBar() {
   188  	fi, err := os.Stat(path.Join(t.Dir, "dir/bar"))
   189  	AssertEq(nil, err)
   190  
   191  	ExpectEq("bar", fi.Name())
   192  	ExpectEq(cachingfs.BarSize, fi.Size())
   193  	ExpectEq(0777, fi.Mode())
   194  	ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime))
   195  	ExpectFalse(fi.IsDir())
   196  	ExpectEq(t.fs.BarID(), getInodeID(fi))
   197  	ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
   198  }
   199  
   200  ////////////////////////////////////////////////////////////////////////
   201  // No caching
   202  ////////////////////////////////////////////////////////////////////////
   203  
   204  type NoCachingTest struct {
   205  	cachingFSTest
   206  }
   207  
   208  var _ SetUpInterface = &NoCachingTest{}
   209  
   210  func init() { RegisterTestSuite(&NoCachingTest{}) }
   211  
   212  func (t *NoCachingTest) SetUp(ti *TestInfo) {
   213  	const (
   214  		lookupEntryTimeout = 0
   215  		getattrTimeout     = 0
   216  	)
   217  
   218  	t.cachingFSTest.setUp(ti, lookupEntryTimeout, getattrTimeout)
   219  }
   220  
   221  func (t *NoCachingTest) StatStat() {
   222  	fooBefore, dirBefore, barBefore := t.statAll()
   223  	fooAfter, dirAfter, barAfter := t.statAll()
   224  
   225  	// Make sure everything matches.
   226  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime()))
   227  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime()))
   228  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime()))
   229  
   230  	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
   231  	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
   232  	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
   233  }
   234  
   235  func (t *NoCachingTest) StatRenumberStat() {
   236  	t.statAll()
   237  	t.fs.RenumberInodes()
   238  	fooAfter, dirAfter, barAfter := t.statAll()
   239  
   240  	// We should see the new inode IDs, because the entries should not have been
   241  	// cached.
   242  	ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
   243  	ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
   244  	ExpectEq(t.fs.BarID(), getInodeID(barAfter))
   245  }
   246  
   247  func (t *NoCachingTest) StatMtimeStat() {
   248  	newMtime := t.initialMtime.Add(time.Second)
   249  
   250  	t.statAll()
   251  	t.fs.SetMtime(newMtime)
   252  	fooAfter, dirAfter, barAfter := t.statAll()
   253  
   254  	// We should see the new mtimes, because the attributes should not have been
   255  	// cached.
   256  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
   257  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
   258  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
   259  }
   260  
   261  func (t *NoCachingTest) StatRenumberMtimeStat() {
   262  	newMtime := t.initialMtime.Add(time.Second)
   263  
   264  	t.statAll()
   265  	t.fs.RenumberInodes()
   266  	t.fs.SetMtime(newMtime)
   267  	fooAfter, dirAfter, barAfter := t.statAll()
   268  
   269  	// We should see the new inode IDs and mtimes, because nothing should have
   270  	// been cached.
   271  	ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
   272  	ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
   273  	ExpectEq(t.fs.BarID(), getInodeID(barAfter))
   274  
   275  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
   276  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
   277  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
   278  }
   279  
   280  ////////////////////////////////////////////////////////////////////////
   281  // Entry caching
   282  ////////////////////////////////////////////////////////////////////////
   283  
   284  type EntryCachingTest struct {
   285  	cachingFSTest
   286  	lookupEntryTimeout time.Duration
   287  }
   288  
   289  var _ SetUpInterface = &EntryCachingTest{}
   290  
   291  func init() { RegisterTestSuite(&EntryCachingTest{}) }
   292  
   293  func (t *EntryCachingTest) SetUp(ti *TestInfo) {
   294  	t.lookupEntryTimeout = 250 * time.Millisecond
   295  	t.SampleTest.MountConfig.EnableVnodeCaching = true
   296  
   297  	t.cachingFSTest.setUp(ti, t.lookupEntryTimeout, 0)
   298  }
   299  
   300  func (t *EntryCachingTest) StatStat() {
   301  	fooBefore, dirBefore, barBefore := t.statAll()
   302  	fooAfter, dirAfter, barAfter := t.statAll()
   303  
   304  	// Make sure everything matches.
   305  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime()))
   306  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime()))
   307  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime()))
   308  
   309  	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
   310  	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
   311  	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
   312  }
   313  
   314  func (t *EntryCachingTest) StatRenumberStat() {
   315  	fooBefore, dirBefore, barBefore := t.statAll()
   316  	t.fs.RenumberInodes()
   317  	fooAfter, dirAfter, barAfter := t.statAll()
   318  
   319  	// We should still see the old inode IDs, because the inode entries should
   320  	// have been cached.
   321  	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
   322  	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
   323  	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
   324  
   325  	// But after waiting for the entry cache to expire, we should see the new
   326  	// IDs.
   327  	//
   328  	// Note that the cache is not guaranteed to expire on darwin. See notes on
   329  	// fuse.MountConfig.EnableVnodeCaching.
   330  	if runtime.GOOS != "darwin" {
   331  		time.Sleep(2 * t.lookupEntryTimeout)
   332  		fooAfter, dirAfter, barAfter = t.statAll()
   333  
   334  		ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
   335  		ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
   336  		ExpectEq(t.fs.BarID(), getInodeID(barAfter))
   337  	}
   338  }
   339  
   340  func (t *EntryCachingTest) StatMtimeStat() {
   341  	newMtime := t.initialMtime.Add(time.Second)
   342  
   343  	t.statAll()
   344  	t.fs.SetMtime(newMtime)
   345  	fooAfter, dirAfter, barAfter := t.statAll()
   346  
   347  	// We should see the new mtimes, because the attributes should not have been
   348  	// cached.
   349  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
   350  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
   351  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
   352  }
   353  
   354  func (t *EntryCachingTest) StatRenumberMtimeStat() {
   355  	newMtime := t.initialMtime.Add(time.Second)
   356  
   357  	fooBefore, dirBefore, barBefore := t.statAll()
   358  	t.fs.RenumberInodes()
   359  	t.fs.SetMtime(newMtime)
   360  	fooAfter, dirAfter, barAfter := t.statAll()
   361  
   362  	// We should still see the old inode IDs, because the inode entries should
   363  	// have been cached. But the attributes should not have been.
   364  	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
   365  	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
   366  	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
   367  
   368  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
   369  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
   370  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
   371  
   372  	// After waiting for the entry cache to expire, we should see fresh
   373  	// everything.
   374  	//
   375  	// Note that the cache is not guaranteed to expire on darwin. See notes on
   376  	// fuse.MountConfig.EnableVnodeCaching.
   377  	if runtime.GOOS != "darwin" {
   378  		time.Sleep(2 * t.lookupEntryTimeout)
   379  		fooAfter, dirAfter, barAfter = t.statAll()
   380  
   381  		ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
   382  		ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
   383  		ExpectEq(t.fs.BarID(), getInodeID(barAfter))
   384  
   385  		ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
   386  		ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
   387  		ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
   388  	}
   389  }
   390  
   391  ////////////////////////////////////////////////////////////////////////
   392  // Attribute caching
   393  ////////////////////////////////////////////////////////////////////////
   394  
   395  type AttributeCachingTest struct {
   396  	cachingFSTest
   397  	getattrTimeout time.Duration
   398  }
   399  
   400  var _ SetUpInterface = &AttributeCachingTest{}
   401  
   402  func init() { RegisterTestSuite(&AttributeCachingTest{}) }
   403  
   404  func (t *AttributeCachingTest) SetUp(ti *TestInfo) {
   405  	t.getattrTimeout = 250 * time.Millisecond
   406  	t.cachingFSTest.setUp(ti, 0, t.getattrTimeout)
   407  }
   408  
   409  func (t *AttributeCachingTest) StatStat() {
   410  	fooBefore, dirBefore, barBefore := t.statAll()
   411  	fooAfter, dirAfter, barAfter := t.statAll()
   412  
   413  	// Make sure everything matches.
   414  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime()))
   415  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime()))
   416  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime()))
   417  
   418  	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
   419  	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
   420  	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
   421  }
   422  
   423  func (t *AttributeCachingTest) StatRenumberStat() {
   424  	t.statAll()
   425  	t.fs.RenumberInodes()
   426  	fooAfter, dirAfter, barAfter := t.statAll()
   427  
   428  	// We should see the new inode IDs, because the entries should not have been
   429  	// cached.
   430  	ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
   431  	ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
   432  	ExpectEq(t.fs.BarID(), getInodeID(barAfter))
   433  }
   434  
   435  func (t *AttributeCachingTest) StatMtimeStat_ViaPath() {
   436  	newMtime := t.initialMtime.Add(time.Second)
   437  
   438  	t.statAll()
   439  	t.fs.SetMtime(newMtime)
   440  	fooAfter, dirAfter, barAfter := t.statAll()
   441  
   442  	// Since we don't have entry caching enabled, the call above had to look up
   443  	// the entry again. With the lookup we returned new attributes, so it's
   444  	// possible that the mtime will be fresh. On Linux it appears to be, and on
   445  	// OS X it appears to not be.
   446  	m := AnyOf(timeutil.TimeEq(newMtime), timeutil.TimeEq(t.initialMtime))
   447  	ExpectThat(fooAfter.ModTime(), m)
   448  	ExpectThat(dirAfter.ModTime(), m)
   449  	ExpectThat(barAfter.ModTime(), m)
   450  }
   451  
   452  func (t *AttributeCachingTest) StatMtimeStat_ViaFileDescriptor() {
   453  	newMtime := t.initialMtime.Add(time.Second)
   454  
   455  	// Open everything, fixing a particular inode number for each.
   456  	foo, dir, bar := t.openFiles()
   457  	defer func() {
   458  		foo.Close()
   459  		dir.Close()
   460  		bar.Close()
   461  	}()
   462  
   463  	fooBefore, dirBefore, barBefore := t.statFiles(foo, dir, bar)
   464  	t.fs.SetMtime(newMtime)
   465  	fooAfter, dirAfter, barAfter := t.statFiles(foo, dir, bar)
   466  
   467  	// We should still see the old cached mtime.
   468  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime()))
   469  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime()))
   470  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime()))
   471  
   472  	// After waiting for the attribute cache to expire, we should see the fresh
   473  	// mtime.
   474  	time.Sleep(2 * t.getattrTimeout)
   475  	fooAfter, dirAfter, barAfter = t.statFiles(foo, dir, bar)
   476  
   477  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
   478  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
   479  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
   480  }
   481  
   482  func (t *AttributeCachingTest) StatRenumberMtimeStat_ViaPath() {
   483  	newMtime := t.initialMtime.Add(time.Second)
   484  
   485  	t.statAll()
   486  	t.fs.RenumberInodes()
   487  	t.fs.SetMtime(newMtime)
   488  	fooAfter, dirAfter, barAfter := t.statAll()
   489  
   490  	// We should see new everything, because this is the first time the new
   491  	// inodes have been encountered. Entries for the old ones should not have
   492  	// been cached, because we have entry caching disabled.
   493  	ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
   494  	ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
   495  	ExpectEq(t.fs.BarID(), getInodeID(barAfter))
   496  
   497  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
   498  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
   499  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
   500  }
   501  
   502  func (t *AttributeCachingTest) StatRenumberMtimeStat_ViaFileDescriptor() {
   503  	newMtime := t.initialMtime.Add(time.Second)
   504  
   505  	// Open everything, fixing a particular inode number for each.
   506  	foo, dir, bar := t.openFiles()
   507  	defer func() {
   508  		foo.Close()
   509  		dir.Close()
   510  		bar.Close()
   511  	}()
   512  
   513  	fooBefore, dirBefore, barBefore := t.statFiles(foo, dir, bar)
   514  	t.fs.RenumberInodes()
   515  	t.fs.SetMtime(newMtime)
   516  	fooAfter, dirAfter, barAfter := t.statFiles(foo, dir, bar)
   517  
   518  	// We should still see the old cached mtime with the old inode ID.
   519  	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
   520  	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
   521  	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
   522  
   523  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime()))
   524  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime()))
   525  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime()))
   526  
   527  	// After waiting for the attribute cache to expire, we should see the fresh
   528  	// mtime, still with the old inode ID.
   529  	time.Sleep(2 * t.getattrTimeout)
   530  	fooAfter, dirAfter, barAfter = t.statFiles(foo, dir, bar)
   531  
   532  	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
   533  	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
   534  	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
   535  
   536  	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
   537  	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
   538  	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
   539  }
   540  
   541  ////////////////////////////////////////////////////////////////////////
   542  // Page cache
   543  ////////////////////////////////////////////////////////////////////////
   544  
   545  type PageCacheTest struct {
   546  	cachingFSTest
   547  }
   548  
   549  var _ SetUpInterface = &PageCacheTest{}
   550  
   551  func init() { RegisterTestSuite(&PageCacheTest{}) }
   552  
   553  func (t *PageCacheTest) SetUp(ti *TestInfo) {
   554  	const (
   555  		lookupEntryTimeout = 0
   556  		getattrTimeout     = 0
   557  	)
   558  
   559  	t.cachingFSTest.setUp(ti, lookupEntryTimeout, getattrTimeout)
   560  }
   561  
   562  func (t *PageCacheTest) SingleFileHandle_NoKeepCache() {
   563  	t.fs.SetKeepCache(false)
   564  
   565  	// Open the file.
   566  	f, err := os.Open(path.Join(t.Dir, "foo"))
   567  	AssertEq(nil, err)
   568  
   569  	defer f.Close()
   570  
   571  	// Read its contents once.
   572  	f.Seek(0, 0)
   573  	AssertEq(nil, err)
   574  
   575  	c1, err := ioutil.ReadAll(f)
   576  	AssertEq(nil, err)
   577  	AssertEq(cachingfs.FooSize, len(c1))
   578  
   579  	// And again.
   580  	f.Seek(0, 0)
   581  	AssertEq(nil, err)
   582  
   583  	c2, err := ioutil.ReadAll(f)
   584  	AssertEq(nil, err)
   585  	AssertEq(cachingfs.FooSize, len(c2))
   586  
   587  	// We should have seen the same contents each time.
   588  	ExpectTrue(bytes.Equal(c1, c2))
   589  }
   590  
   591  func (t *PageCacheTest) SingleFileHandle_KeepCache() {
   592  	t.fs.SetKeepCache(true)
   593  
   594  	// Open the file.
   595  	f, err := os.Open(path.Join(t.Dir, "foo"))
   596  	AssertEq(nil, err)
   597  
   598  	defer f.Close()
   599  
   600  	// Read its contents once.
   601  	f.Seek(0, 0)
   602  	AssertEq(nil, err)
   603  
   604  	c1, err := ioutil.ReadAll(f)
   605  	AssertEq(nil, err)
   606  	AssertEq(cachingfs.FooSize, len(c1))
   607  
   608  	// And again.
   609  	f.Seek(0, 0)
   610  	AssertEq(nil, err)
   611  
   612  	c2, err := ioutil.ReadAll(f)
   613  	AssertEq(nil, err)
   614  	AssertEq(cachingfs.FooSize, len(c2))
   615  
   616  	// We should have seen the same contents each time.
   617  	ExpectTrue(bytes.Equal(c1, c2))
   618  }
   619  
   620  func (t *PageCacheTest) TwoFileHandles_NoKeepCache() {
   621  	t.fs.SetKeepCache(false)
   622  
   623  	// SetKeepCache(false) doesn't work on OS X. See the notes on
   624  	// OpenFileOp.KeepPageCache.
   625  	if runtime.GOOS == "darwin" {
   626  		return
   627  	}
   628  
   629  	// Open the file.
   630  	f1, err := os.Open(path.Join(t.Dir, "foo"))
   631  	AssertEq(nil, err)
   632  
   633  	defer f1.Close()
   634  
   635  	// Read its contents once.
   636  	f1.Seek(0, 0)
   637  	AssertEq(nil, err)
   638  
   639  	c1, err := ioutil.ReadAll(f1)
   640  	AssertEq(nil, err)
   641  	AssertEq(cachingfs.FooSize, len(c1))
   642  
   643  	// Open a second handle.
   644  	f2, err := os.Open(path.Join(t.Dir, "foo"))
   645  	AssertEq(nil, err)
   646  
   647  	defer f2.Close()
   648  
   649  	// We should see different contents if we read from that handle, due to the
   650  	// cache being invalidated at the time of opening.
   651  	f2.Seek(0, 0)
   652  	AssertEq(nil, err)
   653  
   654  	c2, err := ioutil.ReadAll(f2)
   655  	AssertEq(nil, err)
   656  	AssertEq(cachingfs.FooSize, len(c2))
   657  
   658  	ExpectFalse(bytes.Equal(c1, c2))
   659  
   660  	// Another read from the second handle should give the same result as the
   661  	// first one from that handle.
   662  	f2.Seek(0, 0)
   663  	AssertEq(nil, err)
   664  
   665  	c3, err := ioutil.ReadAll(f2)
   666  	AssertEq(nil, err)
   667  	AssertEq(cachingfs.FooSize, len(c3))
   668  
   669  	ExpectTrue(bytes.Equal(c2, c3))
   670  
   671  	// And another read from the first handle should give the same result yet
   672  	// again.
   673  	f1.Seek(0, 0)
   674  	AssertEq(nil, err)
   675  
   676  	c4, err := ioutil.ReadAll(f1)
   677  	AssertEq(nil, err)
   678  	AssertEq(cachingfs.FooSize, len(c4))
   679  
   680  	ExpectTrue(bytes.Equal(c2, c4))
   681  }
   682  
   683  func (t *PageCacheTest) TwoFileHandles_KeepCache() {
   684  	t.fs.SetKeepCache(true)
   685  
   686  	// Open the file.
   687  	f1, err := os.Open(path.Join(t.Dir, "foo"))
   688  	AssertEq(nil, err)
   689  
   690  	defer f1.Close()
   691  
   692  	// Read its contents once.
   693  	f1.Seek(0, 0)
   694  	AssertEq(nil, err)
   695  
   696  	c1, err := ioutil.ReadAll(f1)
   697  	AssertEq(nil, err)
   698  	AssertEq(cachingfs.FooSize, len(c1))
   699  
   700  	// Open a second handle.
   701  	f2, err := os.Open(path.Join(t.Dir, "foo"))
   702  	AssertEq(nil, err)
   703  
   704  	defer f2.Close()
   705  
   706  	// We should see the same contents when we read via the second handle.
   707  	f2.Seek(0, 0)
   708  	AssertEq(nil, err)
   709  
   710  	c2, err := ioutil.ReadAll(f2)
   711  	AssertEq(nil, err)
   712  	AssertEq(cachingfs.FooSize, len(c2))
   713  
   714  	ExpectTrue(bytes.Equal(c1, c2))
   715  
   716  	// Ditto if we read again from the first.
   717  	f1.Seek(0, 0)
   718  	AssertEq(nil, err)
   719  
   720  	c3, err := ioutil.ReadAll(f1)
   721  	AssertEq(nil, err)
   722  	AssertEq(cachingfs.FooSize, len(c3))
   723  
   724  	ExpectTrue(bytes.Equal(c1, c3))
   725  }