From 24ee98e6d489005baad7cc6503aa669a99d61e00 Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Tue, 31 Dec 2024 17:19:03 +0000 Subject: [PATCH] memfs: Add deterministic modification times Memfs returns the latest time on every invocation of ModTime(), which causes issues in go-git when an accurate modification time is required. Relates to https://github.com/go-git/go-git/issues/1342, when using the latest version of go-git-fixtures. Signed-off-by: Paulo Gomes --- memfs/memory.go | 21 +++++++++++++-------- memfs/memory_test.go | 30 ++++++++++++++++++++++++++++++ memfs/storage.go | 2 ++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/memfs/memory.go b/memfs/memory.go index 6cbd7d0..be7623f 100644 --- a/memfs/memory.go +++ b/memfs/memory.go @@ -228,6 +228,7 @@ type file struct { position int64 flag int mode os.FileMode + modTime time.Time isClosed bool } @@ -291,6 +292,7 @@ func (f *file) WriteAt(p []byte, off int64) (int, error) { return 0, errors.New("write not supported") } + f.modTime = time.Now() n, err := f.content.WriteAt(p, off) f.position = off + int64(n) @@ -322,6 +324,7 @@ func (f *file) Duplicate(filename string, mode os.FileMode, flag int) billy.File content: f.content, mode: mode, flag: flag, + modTime: f.modTime, } if isTruncate(flag) { @@ -337,9 +340,10 @@ func (f *file) Duplicate(filename string, mode os.FileMode, flag int) billy.File func (f *file) Stat() (os.FileInfo, error) { return &fileInfo{ - name: f.Name(), - mode: f.mode, - size: f.content.Len(), + name: f.Name(), + mode: f.mode, + size: f.content.Len(), + modTime: f.modTime, }, nil } @@ -354,9 +358,10 @@ func (f *file) Unlock() error { } type fileInfo struct { - name string - size int - mode os.FileMode + name string + size int + mode os.FileMode + modTime time.Time } func (fi *fileInfo) Name() string { @@ -371,8 +376,8 @@ func (fi *fileInfo) Mode() os.FileMode { return fi.mode } -func (*fileInfo) ModTime() time.Time { - return time.Now() +func (fi *fileInfo) ModTime() time.Time { + return fi.modTime } func (fi *fileInfo) IsDir() bool { diff --git a/memfs/memory_test.go b/memfs/memory_test.go index 7a483cb..96c281c 100644 --- a/memfs/memory_test.go +++ b/memfs/memory_test.go @@ -10,6 +10,7 @@ import ( "github.com/go-git/go-billy/v5" "github.com/go-git/go-billy/v5/util" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestRootExists(t *testing.T) { @@ -28,6 +29,35 @@ func TestCapabilities(t *testing.T) { assert.Equal(t, billy.DefaultCapabilities&^billy.LockCapability, caps) } +func TestModTime(t *testing.T) { + fs := New() + f1, err := fs.Create("/file1") + require.NoError(t, err) + + _, err = fs.Create("/file2") + require.NoError(t, err) + + fi1a, err := fs.Stat("/file1") + require.NoError(t, err) + + fi2, err := fs.Stat("/file2") + require.NoError(t, err) + + fi1b, err := fs.Stat("/file1") + require.NoError(t, err) + + modtime := fi1a.ModTime() + + // file 1 and file 2 should have different mod times. + assert.NotEqual(t, modtime, fi2.ModTime()) + + // a new file info for the same unmodified file, should still match mod time. + assert.Equal(t, modtime, fi1b.ModTime()) + + // new calls to ModTime() retain existing mod time. + assert.Equal(t, modtime, fi1a.ModTime()) +} + func TestNegativeOffsets(t *testing.T) { fs := New() f, err := fs.Create("negative") diff --git a/memfs/storage.go b/memfs/storage.go index 16b48ce..c435f3a 100644 --- a/memfs/storage.go +++ b/memfs/storage.go @@ -8,6 +8,7 @@ import ( "path/filepath" "strings" "sync" + "time" ) type storage struct { @@ -46,6 +47,7 @@ func (s *storage) New(path string, mode os.FileMode, flag int) (*file, error) { content: &content{name: name}, mode: mode, flag: flag, + modTime: time.Now(), } s.files[path] = f