-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement
fs
module with open method, File and FileInfo abstractions
- Loading branch information
Showing
2 changed files
with
112 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package fs | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"path/filepath" | ||
"sync" | ||
|
||
"github.com/spf13/afero" | ||
) | ||
|
||
// registry is a registry of opened files. | ||
type registry struct { | ||
// files holds a safe for concurrent use map of opened files. | ||
// | ||
// Keys are expected to be strings holding the files' path. | ||
// Values are expected to be byte slices holding the files' data. | ||
// | ||
// That way, we can cache the file's content and avoid opening too many | ||
// file descriptor, and re-reading its content every time the file is opened. | ||
// | ||
// Importantly, this also means that if the | ||
// file is modified from outside of k6, the changes will not be reflected in the file's data. | ||
// files map[string][]byte | ||
files sync.Map | ||
} | ||
|
||
// open opens the named file for reading. | ||
// | ||
// If the file was already opened, it returns a pointer to the cached file data. Otherwise, it | ||
// opens the file, reads its content, caches it, and returns a pointer to it. | ||
// | ||
// The file is always opened in read-only mode. The provided file path is cleaned before being | ||
// used. | ||
func (fr *registry) open(filename string, fromFs afero.Fs) ([]byte, error) { | ||
filename = filepath.Clean(filename) | ||
|
||
if f, ok := fr.files.Load(filename); ok { | ||
data, ok := f.([]byte) | ||
if !ok { | ||
panic(fmt.Errorf("registry's file %s is not stored as a byte slice", filename)) | ||
} | ||
|
||
return data, nil | ||
} | ||
|
||
f, err := fromFs.Open(filename) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
data, err := io.ReadAll(f) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
fr.files.Store(filename, data) | ||
|
||
return data, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package fs | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/spf13/afero" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestFileRegistryOpen(t *testing.T) { | ||
t.Parallel() | ||
|
||
t.Run("open succeeds", func(t *testing.T) { | ||
t.Parallel() | ||
|
||
registry := ®istry{} | ||
fs := newTestFs(t, func(fs afero.Fs) error { | ||
return afero.WriteFile(fs, "bonjour.txt", []byte("Bonjour, le monde"), 0o644) | ||
}) | ||
|
||
_, gotBeforeOk := registry.files.Load("bonjour.txt") | ||
gotData, gotErr := registry.open("bonjour.txt", fs) | ||
_, gotAfterOk := registry.files.Load("bonjour.txt") | ||
|
||
assert.False(t, gotBeforeOk) | ||
assert.NoError(t, gotErr) | ||
assert.Equal(t, []byte("Bonjour, le monde"), gotData) | ||
assert.True(t, gotAfterOk) | ||
}) | ||
|
||
t.Run("double open succeeds", func(t *testing.T) { | ||
t.Parallel() | ||
|
||
registry := ®istry{} | ||
fs := newTestFs(t, func(fs afero.Fs) error { | ||
return afero.WriteFile(fs, "bonjour.txt", []byte("Bonjour, le monde"), 0o644) | ||
}) | ||
|
||
firstData, firstErr := registry.open("bonjour.txt", fs) | ||
_, gotFirstOk := registry.files.Load("bonjour.txt") | ||
secondData, secondErr := registry.open("bonjour.txt", fs) | ||
_, gotSecondOk := registry.files.Load("bonjour.txt") | ||
|
||
assert.True(t, gotFirstOk) | ||
assert.NoError(t, firstErr) | ||
assert.Equal(t, []byte("Bonjour, le monde"), firstData) | ||
assert.True(t, gotSecondOk) | ||
assert.NoError(t, secondErr) | ||
assert.Equal(t, firstData, secondData) // same pointer | ||
assert.Equal(t, []byte("Bonjour, le monde"), secondData) | ||
}) | ||
} |