Skip to content

Commit

Permalink
Implement fs module with open method, File and FileInfo abstractions
Browse files Browse the repository at this point in the history
  • Loading branch information
oleiade committed Oct 10, 2023
1 parent 0a4f80b commit 835b48f
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
60 changes: 60 additions & 0 deletions js/modules/k6/experimental/fs/registry.go
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
}
52 changes: 52 additions & 0 deletions js/modules/k6/experimental/fs/registry_test.go
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 := &registry{}
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 := &registry{}
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)
})
}

0 comments on commit 835b48f

Please sign in to comment.