github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/client/logging/stat_test.go (about) 1 package logging_test 2 3 import ( 4 "errors" 5 "io" 6 "os" 7 "path/filepath" 8 "runtime" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/require" 13 14 "github.com/datawire/dlib/dlog" 15 "github.com/telepresenceio/telepresence/v2/pkg/client" 16 "github.com/telepresenceio/telepresence/v2/pkg/client/logging" 17 "github.com/telepresenceio/telepresence/v2/pkg/dos" 18 ) 19 20 var osHasBTime = true 21 22 func TestFStat(t *testing.T) { 23 btimeIsCTime := testFStat(t, runtime.GOOS == "linux") 24 if btimeIsCTime && osHasBTime { 25 // The kernel supports btime, but the filesystem doesn't. Set TMPDIR to be 26 // $HOME/tmp, on the assumption that $HOME is on a "big boy" filesystem and thus 27 // supports btime. 28 t.Run("tmpdirInHome", func(t *testing.T) { 29 os.Setenv("TMPDIR", filepath.Join(os.Getenv("HOME"), "tmp")) 30 err := os.Mkdir(os.Getenv("TMPDIR"), 0o777) 31 if err != nil && !errors.Is(err, os.ErrExist) { 32 t.Fatal(err) 33 } 34 testFStat(t, false) 35 }) 36 } 37 } 38 39 func testFStat(t *testing.T, okIfBTimeIsCTime bool) (btimeIsCTime bool) { 40 const ( 41 fsVsClockLeeway = 1 * time.Second // many filesystems only have second precision 42 minDelta = 2 * time.Second 43 ) 44 45 ctx := dlog.NewTestContext(t, false) 46 ctx = client.WithEnv(ctx, &client.Env{}) 47 filename := filepath.Join(t.TempDir(), "stamp.txt") 48 withFile := func(flags int, fn func(dos.File)) (time.Time, time.Time) { 49 before := time.Now() 50 time.Sleep(fsVsClockLeeway) 51 file, err := dos.OpenFile(ctx, filename, flags, 0o666) 52 require.NoError(t, err) 53 require.NotNil(t, file) 54 fn(file) 55 require.NoError(t, file.Close()) 56 time.Sleep(fsVsClockLeeway) 57 after := time.Now() 58 return before, after 59 } 60 61 // btime 62 bBefore, bAfter := withFile(os.O_CREATE|os.O_RDWR, func(file dos.File) {}) 63 64 time.Sleep(minDelta) 65 66 // mtime 67 mBefore, mAfter := withFile(os.O_RDWR, func(file dos.File) { 68 _, err := io.WriteString(file, "#!/bin/sh\n") 69 require.NoError(t, err) 70 }) 71 72 time.Sleep(minDelta) 73 74 // ctime 75 cBefore := time.Now() 76 time.Sleep(fsVsClockLeeway) 77 require.NoError(t, os.Chmod(filename, 0o777)) 78 time.Sleep(fsVsClockLeeway) 79 cAfter := time.Now() 80 81 // stat 82 var stat logging.SysInfo 83 withFile(os.O_RDWR, func(file dos.File) { 84 var err error 85 stat, err = logging.FStat(file) 86 require.NoError(t, err) 87 }) 88 89 // validate 90 assertInRange := func(before, after, x time.Time, msg string) { 91 if x.Before(before) || x.After(after) { 92 t.Errorf("%s: %v: not in range [%v, %v]", msg, x, before, after) 93 } else { 94 t.Logf("%s: %v", msg, x) 95 } 96 } 97 98 if okIfBTimeIsCTime && stat.BirthTime() == stat.ChangeTime() { 99 btimeIsCTime = true 100 t.Logf("btime: %v (spoofed with ctime)", stat.BirthTime()) 101 } else { 102 assertInRange(bBefore, bAfter, stat.BirthTime(), "btime") 103 } 104 assertInRange(mBefore, mAfter, stat.ModifyTime(), "mtime") 105 if runtime.GOOS == "windows" { 106 cBefore, cAfter = mBefore, mAfter 107 } 108 assertInRange(cBefore, cAfter, stat.ChangeTime(), "ctime") 109 return btimeIsCTime 110 }