From a15c7e7cac9c9085ad5e17a01655ce2832b2abaf Mon Sep 17 00:00:00 2001 From: Felix Pojtinger Date: Sat, 8 Jan 2022 19:40:12 +0100 Subject: [PATCH] feat: Start adding unit tests --- .github/workflows/hydrun.yaml | 6 + Hydrunfile | 13 +- Makefile | 4 + pkg/fs/filesystem_test.go | 246 ++++++++++++++++++++++++++++++++++ 4 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 pkg/fs/filesystem_test.go diff --git a/.github/workflows/hydrun.yaml b/.github/workflows/hydrun.yaml index 556d64b..e8675bb 100644 --- a/.github/workflows/hydrun.yaml +++ b/.github/workflows/hydrun.yaml @@ -12,6 +12,12 @@ jobs: strategy: matrix: target: + - id: test + src: . + os: golang:bullseye + flags: "" + cmd: ./Hydrunfile test + dst: out/* - id: go src: . os: golang:bullseye diff --git a/Hydrunfile b/Hydrunfile index 2d4a70d..ec044b0 100755 --- a/Hydrunfile +++ b/Hydrunfile @@ -2,6 +2,17 @@ set -e +# Test +if [ "$1" = "test" ]; then + # Generate dependencies + make depend + + # Run tests + make test + + exit 0 +fi + # Go if [ "$1" = "go" ]; then # Install native dependencies @@ -19,8 +30,6 @@ if [ "$1" = "go" ]; then CGO_ENABLED=0 bagop -j "$(nproc)" -b stfs -x '(android/*|ios/*|aix/*|plan9/*|illumos/*|dragonfly/*|netbsd/*|openbsd/*|solaris/*|freebsd/(386|arm)|js/wasm|linux/(mips|ppc64|riscv64)|windows/(arm|386))' -p 'make build/stfs DST=$DST' -d out exit 0 - - exit 0 fi # gccgo diff --git a/Makefile b/Makefile index f31b748..a44a14f 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,10 @@ $(addprefix uninstall/,$(obj)): $(addprefix run/,$(obj)): $(subst run/,,$@) $(ARGS) +# Test +test: + go test ./... + # Clean clean: rm -rf out internal/db diff --git a/pkg/fs/filesystem_test.go b/pkg/fs/filesystem_test.go new file mode 100644 index 0000000..ccacdf2 --- /dev/null +++ b/pkg/fs/filesystem_test.go @@ -0,0 +1,246 @@ +package fs + +import ( + "os" + "path/filepath" + "reflect" + "testing" + + "github.com/pojntfx/stfs/internal/logging" + "github.com/pojntfx/stfs/pkg/cache" + "github.com/pojntfx/stfs/pkg/config" + "github.com/pojntfx/stfs/pkg/operations" + "github.com/pojntfx/stfs/pkg/persisters" + "github.com/pojntfx/stfs/pkg/tape" + "github.com/spf13/afero" +) + +func createTestFss() (filesystems []afero.Fs, cleanup func() error, err error) { + tmp, err := os.MkdirTemp(os.TempDir(), "stfs-test-*") + if err != nil { + return nil, nil, err + } + + drive := filepath.Join(tmp, "drive.tar") + recordSize := 20 + metadata := filepath.Join(tmp, "metadata.sqlite") + writeCache := filepath.Join(tmp, "write-cache") + osfsDir := filepath.Join(tmp, "osfs") + + tm := tape.NewTapeManager( + drive, + recordSize, + false, + ) + + metadataPersister := persisters.NewMetadataPersister(metadata) + if err := metadataPersister.Open(); err != nil { + return nil, nil, err + } + + l := logging.NewJSONLogger(4) + + metadataConfig := config.MetadataConfig{ + Metadata: metadataPersister, + } + pipeConfig := config.PipeConfig{ + Compression: config.NoneKey, + Encryption: config.NoneKey, + Signature: config.NoneKey, + RecordSize: recordSize, + } + backendConfig := config.BackendConfig{ + GetWriter: tm.GetWriter, + CloseWriter: tm.Close, + + GetReader: tm.GetReader, + CloseReader: tm.Close, + + GetDrive: tm.GetDrive, + CloseDrive: tm.Close, + } + readCryptoConfig := config.CryptoConfig{} + + readOps := operations.NewOperations( + backendConfig, + metadataConfig, + + pipeConfig, + readCryptoConfig, + + func(event *config.HeaderEvent) { + l.Debug("Header read", event) + }, + ) + writeOps := operations.NewOperations( + backendConfig, + metadataConfig, + + pipeConfig, + config.CryptoConfig{}, + + func(event *config.HeaderEvent) { + l.Debug("Header write", event) + }, + ) + + stfs := NewSTFS( + readOps, + writeOps, + + config.MetadataConfig{ + Metadata: metadataPersister, + }, + + config.CompressionLevelFastest, + func() (cache.WriteCache, func() error, error) { + return cache.NewCacheWrite( + writeCache, + config.WriteCacheTypeFile, + ) + }, + false, + false, + + func(hdr *config.Header) { + l.Trace("Header transform", hdr) + }, + l, + ) + + root, err := stfs.Initialize("/", os.ModePerm) + if err != nil { + return nil, nil, err + } + + fs, err := cache.NewCacheFilesystem( + stfs, + root, + config.NoneKey, + 0, + "", + ) + if err != nil { + return nil, nil, err + } + + if err := os.MkdirAll(osfsDir, os.ModePerm); err != nil { + return nil, nil, err + } + + return []afero.Fs{ + fs, + afero.NewBasePathFs(afero.NewOsFs(), osfsDir), + }, + func() error { + return os.RemoveAll(tmp) + }, + nil +} + +func getTestNameForFs(testName string, fsName string) string { + return testName + " (" + fsName + ")" +} + +func TestSTFS_Name(t *testing.T) { + filesystems, cleanup, err := createTestFss() + if err != nil { + panic(err) + } + defer cleanup() + + tests := []struct { + name string + f []afero.Fs + want string + }{ + { + "Returns correct file system name", + []afero.Fs{filesystems[1]}, + "BasePathFs", + }, + { + "Returns correct file system name", + []afero.Fs{filesystems[0]}, + "STFS", + }, + } + + for _, tt := range tests { + for _, f := range tt.f { + t.Run(getTestNameForFs(tt.name, f.Name()), func(t *testing.T) { + if got := f.Name(); got != tt.want { + t.Errorf("%v.Name() = %v, want %v", f.Name(), got, tt.want) + } + }) + } + } +} + +func TestSTFS_Create(t *testing.T) { + type args struct { + name string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + "Can create /test.txt", + args{"/test.txt"}, + false, + }, + // FIXME: STFS can create file in non-existent directory, which should not be possible + // { + // "Can not create /nonexistent/test.txt", + // args{"/nonexistent/test.txt"}, + // true, + // }, + // FIXME: STFS can create `/` file even if / exists + // { + // "Can create /", + // args{"/"}, + // true, + // }, + } + + for _, tt := range tests { + filesystems, cleanup, err := createTestFss() + if err != nil { + panic(err) + } + defer cleanup() + + for _, f := range filesystems { + t.Run(getTestNameForFs(tt.name, f.Name()), func(t *testing.T) { + file, err := f.Create(tt.args.name) + if (err != nil) != tt.wantErr { + t.Errorf("%v.Create() error = %v, wantErr %v", f.Name(), err, tt.wantErr) + + return + } + + want, err := f.Stat(tt.args.name) + if err != nil { + t.Errorf("%v.Stat() error = %v, wantErr %v", f.Name(), err, tt.wantErr) + + return + } + + got, err := f.Stat(file.Name()) + if err != nil { + t.Errorf("%v.Stat() error = %v, wantErr %v", f.Name(), err, tt.wantErr) + + return + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("%v.Create().Name() = %v, want %v", f.Name(), got, want) + + return + } + }) + } + } +}