Skip to content

Commit ee801ff

Browse files
committed
Update embedfs implementation:
- Fix path normalization to support billy's absolute path convention ("/") - Implement missing Lstat method (was returning ErrNotSupported) - Add proper embed.FS to billy path conversion throughout all methods Additional test coverage: - Add dedicated test files for different functionality areas - Test File interface methods including ReadAt, Close, Lock/Unlock - Add path normalization and edge case testing - Test empty file handling and boundary conditions - Verify proper error handling for read-only operations Test infrastructure: - Create embedfs_testdata package for reusable test data - Resolve import cycles with clean provider pattern - Add test data including nested directories and various file types - Organize tests by functionality (fs, dir, file operations) The embedfs implementation now provides billy.Filesystem compliance for read-only embedded filesystems.
1 parent c1f149a commit ee801ff

File tree

11 files changed

+462
-61
lines changed

11 files changed

+462
-61
lines changed

embedfs/debug_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package embedfs
2+
3+
import (
4+
"embed"
5+
"testing"
6+
7+
"github.com/go-git/go-billy/v6/embedfs_testdata"
8+
)
9+
10+
var emptyEmbedFS embed.FS
11+
12+
func TestDebugErrors(t *testing.T) {
13+
// Test 1: Empty embed.FS with empty path
14+
emptyFS := New(&emptyEmbedFS)
15+
_, err1 := emptyFS.ReadDir("")
16+
t.Logf("Empty FS, empty path: %v", err1)
17+
18+
// Test 2: Empty embed.FS with root path
19+
_, err2 := emptyFS.ReadDir("/")
20+
t.Logf("Empty FS, root path: %v", err2)
21+
22+
// Test 3: Non-empty embed.FS with empty path
23+
richFS := New(embedfs_testdata.GetTestData())
24+
_, err3 := richFS.ReadDir("")
25+
t.Logf("Rich FS, empty path: %v", err3)
26+
27+
// Test 4: Non-empty embed.FS with root path
28+
entries, err4 := richFS.ReadDir("/")
29+
t.Logf("Rich FS, root path: %d entries, err: %v", len(entries), err4)
30+
}

embedfs/dir_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package embedfs
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/go-git/go-billy/v6/embedfs_testdata"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
// Directory operation tests adapted from test/dir_test.go for read-only embedfs
13+
14+
func TestDir_ReadDirNested(t *testing.T) {
15+
t.Parallel()
16+
17+
fs := New(embedfs_testdata.GetTestData())
18+
19+
// Test reading nested directories
20+
entries, err := fs.ReadDir("/testdata/subdir")
21+
require.NoError(t, err)
22+
assert.Len(t, entries, 1)
23+
assert.Equal(t, "nested.txt", entries[0].Name())
24+
assert.False(t, entries[0].IsDir())
25+
}
26+
27+
func TestDir_ReadDirFileInfo(t *testing.T) {
28+
t.Parallel()
29+
30+
fs := New(embedfs_testdata.GetTestData())
31+
32+
entries, err := fs.ReadDir("/testdata")
33+
require.NoError(t, err)
34+
35+
// Verify all entries have proper FileInfo
36+
for _, entry := range entries {
37+
assert.NotEmpty(t, entry.Name())
38+
assert.NotNil(t, entry.ModTime())
39+
assert.Greater(t, entry.Size(), int64(-1)) // Size can be 0 but not negative
40+
}
41+
}
42+
43+
func TestDir_ReadDirFileInfoDirs(t *testing.T) {
44+
t.Parallel()
45+
46+
fs := New(embedfs_testdata.GetTestData())
47+
48+
entries, err := fs.ReadDir("/testdata")
49+
require.NoError(t, err)
50+
51+
// Find the subdirectory entry
52+
var subdirEntry os.FileInfo
53+
for _, entry := range entries {
54+
if entry.Name() == "subdir" {
55+
subdirEntry = entry
56+
break
57+
}
58+
}
59+
60+
require.NotNil(t, subdirEntry, "subdir should be found")
61+
assert.True(t, subdirEntry.IsDir(), "subdir should be a directory")
62+
assert.Equal(t, "subdir", subdirEntry.Name())
63+
}

embedfs/embed.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,26 @@ func New(efs *embed.FS) billy.Filesystem {
3232
return fs
3333
}
3434

35+
// normalizePath converts billy's absolute paths to embed.FS relative paths
36+
func (fs *Embed) normalizePath(path string) string {
37+
// embed.FS uses "." for root directory, but billy uses "/"
38+
if path == "/" {
39+
return "."
40+
}
41+
// Remove leading slash for embed.FS
42+
if strings.HasPrefix(path, "/") {
43+
return path[1:]
44+
}
45+
return path
46+
}
47+
3548
func (fs *Embed) Root() string {
3649
return ""
3750
}
3851

3952
func (fs *Embed) Stat(filename string) (os.FileInfo, error) {
53+
filename = fs.normalizePath(filename)
54+
4055
f, err := fs.underlying.Open(filename)
4156
if err != nil {
4257
return nil, err
@@ -53,6 +68,7 @@ func (fs *Embed) OpenFile(filename string, flag int, _ os.FileMode) (billy.File,
5368
return nil, billy.ErrReadOnly
5469
}
5570

71+
filename = fs.normalizePath(filename)
5672
f, err := fs.underlying.Open(filename)
5773
if err != nil {
5874
return nil, err
@@ -91,6 +107,8 @@ func (fs *Embed) Join(elem ...string) string {
91107
}
92108

93109
func (fs *Embed) ReadDir(path string) ([]os.FileInfo, error) {
110+
path = fs.normalizePath(path)
111+
94112
e, err := fs.underlying.ReadDir(path)
95113
if err != nil {
96114
return nil, err
@@ -114,11 +132,9 @@ func (fs *Embed) Chroot(_ string) (billy.Filesystem, error) {
114132
return nil, billy.ErrNotSupported
115133
}
116134

117-
// Lstat is not supported.
118-
//
119-
// Calls will always return billy.ErrNotSupported.
120-
func (fs *Embed) Lstat(_ string) (os.FileInfo, error) {
121-
return nil, billy.ErrNotSupported
135+
// Lstat behaves the same as Stat for embedded filesystems since there are no symlinks.
136+
func (fs *Embed) Lstat(filename string) (os.FileInfo, error) {
137+
return fs.Stat(filename)
122138
}
123139

124140
// Readlink is not supported.

0 commit comments

Comments
 (0)