Skip to content

Commit

Permalink
memfs: Add deterministic modification times
Browse files Browse the repository at this point in the history
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 go-git/go-git#1342, when using the
latest version of go-git-fixtures.

Signed-off-by: Paulo Gomes <[email protected]>
  • Loading branch information
pjbgf committed Dec 31, 2024
1 parent a0a6b08 commit 24ee98e
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 8 deletions.
21 changes: 13 additions & 8 deletions memfs/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ type file struct {
position int64
flag int
mode os.FileMode
modTime time.Time

isClosed bool
}
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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) {
Expand All @@ -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
}

Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
30 changes: 30 additions & 0 deletions memfs/memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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")

Check failure on line 34 in memfs/memory_test.go

View workflow job for this annotation

GitHub Actions / test (1.21.x, ubuntu-latest)

f1 declared and not used

Check failure on line 34 in memfs/memory_test.go

View workflow job for this annotation

GitHub Actions / test (1.22.x)

f1 declared and not used

Check failure on line 34 in memfs/memory_test.go

View workflow job for this annotation

GitHub Actions / test (1.21.x, macos-latest)

f1 declared and not used

Check failure on line 34 in memfs/memory_test.go

View workflow job for this annotation

GitHub Actions / test (1.23.x)

declared and not used: f1

Check failure on line 34 in memfs/memory_test.go

View workflow job for this annotation

GitHub Actions / test (1.22.x, ubuntu-latest)

f1 declared and not used

Check failure on line 34 in memfs/memory_test.go

View workflow job for this annotation

GitHub Actions / test (1.22.x, macos-latest)

f1 declared and not used

Check failure on line 34 in memfs/memory_test.go

View workflow job for this annotation

GitHub Actions / test (1.23.x, ubuntu-latest)

declared and not used: f1

Check failure on line 34 in memfs/memory_test.go

View workflow job for this annotation

GitHub Actions / test (1.23.x, macos-latest)

declared and not used: f1
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")
Expand Down
2 changes: 2 additions & 0 deletions memfs/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"path/filepath"
"strings"
"sync"
"time"
)

type storage struct {
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 24ee98e

Please sign in to comment.