From 56610a8af8e79e2c0efdcceaf804cd2448156fdf Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Tue, 31 Dec 2024 17:22:28 +0000 Subject: [PATCH 1/2] 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..dd0b685 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() + _, 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 From 36eabfeefeb140c3fef4702057c56b2bb85500f7 Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Fri, 10 Jan 2025 16:18:56 +0000 Subject: [PATCH 2/2] memfs: Fix test which may error due to Windows timer resolution More information can be found on upstream documentation: https://github.com/golang/go/blob/7255b949202bb752b6525aa24cb636ceaf24e4d1/src/time/time.go#L85-L90 Signed-off-by: Paulo Gomes --- memfs/memory_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/memfs/memory_test.go b/memfs/memory_test.go index dd0b685..92a378a 100644 --- a/memfs/memory_test.go +++ b/memfs/memory_test.go @@ -6,6 +6,7 @@ import ( "os" "runtime" "testing" + "time" "github.com/go-git/go-billy/v5" "github.com/go-git/go-billy/v5/util" @@ -34,6 +35,10 @@ func TestModTime(t *testing.T) { _, err := fs.Create("/file1") require.NoError(t, err) + if runtime.GOOS == "windows" { + time.Sleep(20 * time.Millisecond) + } + _, err = fs.Create("/file2") require.NoError(t, err)