Add tarfs implementation (reopen #265) (#266)
* Initial commit for tarfs
* tarfs: reword "open" status field
* tarfs: use TestMain for FS setup
We want to have the FS variable available through all the tests, so we
we use a common "setup" function to initialise it.
* tarfs: test: early exit for nonexisting files
* tarfs: create test for filesystem Open
* tarfs: implement File.Stat
* tarfs: implement Fs.Open
* tarfs: return error on non-supported methods
As tarfs is a read-only filesystem backend, we return EROFS (Read-only
file system) from any method that makes modifications.
* tarfs: implement File.data as bytes.Reader
Most of the operations that we want to implement for tarfs.File are
already defined in bytes.Reader.
We could use a plain slice and implement all the seeking manually, but I
think using this is more convenient.
* tarfs: short format for simple methods
* tarfs: add missing closing brace in tests
* tarfs: add test for File.ReadAt
* tarfs: test File.ReadAt
* tarfs: add tests for File.Read
* tarfs: implement File.Read
* tarfs: add tests for File.Seek
* tarfs: implement File.Seek
* tarfs: add tests for File.Name
* tarfs: implement File.Name
* tarfs: add tests for File.Close
* tarfs: implement File.Close
* tarfs: add tests for OpenFile
* tarfs: fix test for Fs.OpenFile
If the call fails, we don't have to close the file
* tarfs: remove code not needed after using filepath.Clean
* tarfs: Open: return a copy of the internal structure
As we modify the struct fields when closing, we don't want to lose the
internal representation of the file, in case we want to reopen it.
Return a copy of the File, although we keep using the same pointers to
tar.Header and buffer.Reader. Maybe we will need to change that in the
future.
* tarfs: implement Fs.OpenFile
* tarfs: use Fatalf for unexpected error in TestFsOpen
* tarfs: add tests for Fs.Stat
* tarfs: implement Fs.Stat
* tarfs: remove TestNewFs
That test depends too much on the internal imlementation, and it is
easier to break if we change it.
* tarfs: remove unused code
* tarfs: change internal implementation
To be able to handle directories (File.Readdir, File.Readdirnames), the
naive single-map implementation makes it a bit harder to implement.
Inspired by the zipfs backend, switch to an internal implementation of a
map of directories that contains a map of files, so the directory
methods are easier to implement.
Also, treat the "virtual" filesystem as absolute, just like zipfs does.
* tarfs: use Fatal errors to avoid panics
* tarfs: add pseudoroot
* tarfs: add tests for File.Readdir
* tarfs: add pointer Fs in the File structure
For directory-related operations we will need to access the internal
structure in the Fs.
As Readdir and Readdirnames are File methods, we need to access such
structure from the File.
* tarfs: fix error
* tarfs: use just the names for TestReaddir, easier than using fill os.FileInfo entries
* tarfs: create a copy of the original entry when opening a file
We added the fs field in the File struct to reference the underlying Fs
object, but in the Open cal we were not passing it, making all the
opened files to have a nil pointer in that field.
Change to make a copy of the original file, and returning that
* tarfs: implement File.Readdir
* tarfs: add tests for File.Readdirnames
* tarfs: implement Readdirnames
* tarfs: add test for File.Name
* tarfs: change tests to use the Afero interface instead
* tarfs: add tests for Glob from zipfs
* tarfs: update main repo references to tarfs
* tarfs: use OS-specific file separator for pseudoroot
* tarfs: fix path handling in Windows systems
2020-09-14 21:44:56 +03:00
|
|
|
// Most of the tests are stolen from the zipfs implementation
|
|
|
|
package tarfs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/tar"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"reflect"
|
2020-10-06 01:23:18 +03:00
|
|
|
"strings"
|
Add tarfs implementation (reopen #265) (#266)
* Initial commit for tarfs
* tarfs: reword "open" status field
* tarfs: use TestMain for FS setup
We want to have the FS variable available through all the tests, so we
we use a common "setup" function to initialise it.
* tarfs: test: early exit for nonexisting files
* tarfs: create test for filesystem Open
* tarfs: implement File.Stat
* tarfs: implement Fs.Open
* tarfs: return error on non-supported methods
As tarfs is a read-only filesystem backend, we return EROFS (Read-only
file system) from any method that makes modifications.
* tarfs: implement File.data as bytes.Reader
Most of the operations that we want to implement for tarfs.File are
already defined in bytes.Reader.
We could use a plain slice and implement all the seeking manually, but I
think using this is more convenient.
* tarfs: short format for simple methods
* tarfs: add missing closing brace in tests
* tarfs: add test for File.ReadAt
* tarfs: test File.ReadAt
* tarfs: add tests for File.Read
* tarfs: implement File.Read
* tarfs: add tests for File.Seek
* tarfs: implement File.Seek
* tarfs: add tests for File.Name
* tarfs: implement File.Name
* tarfs: add tests for File.Close
* tarfs: implement File.Close
* tarfs: add tests for OpenFile
* tarfs: fix test for Fs.OpenFile
If the call fails, we don't have to close the file
* tarfs: remove code not needed after using filepath.Clean
* tarfs: Open: return a copy of the internal structure
As we modify the struct fields when closing, we don't want to lose the
internal representation of the file, in case we want to reopen it.
Return a copy of the File, although we keep using the same pointers to
tar.Header and buffer.Reader. Maybe we will need to change that in the
future.
* tarfs: implement Fs.OpenFile
* tarfs: use Fatalf for unexpected error in TestFsOpen
* tarfs: add tests for Fs.Stat
* tarfs: implement Fs.Stat
* tarfs: remove TestNewFs
That test depends too much on the internal imlementation, and it is
easier to break if we change it.
* tarfs: remove unused code
* tarfs: change internal implementation
To be able to handle directories (File.Readdir, File.Readdirnames), the
naive single-map implementation makes it a bit harder to implement.
Inspired by the zipfs backend, switch to an internal implementation of a
map of directories that contains a map of files, so the directory
methods are easier to implement.
Also, treat the "virtual" filesystem as absolute, just like zipfs does.
* tarfs: use Fatal errors to avoid panics
* tarfs: add pseudoroot
* tarfs: add tests for File.Readdir
* tarfs: add pointer Fs in the File structure
For directory-related operations we will need to access the internal
structure in the Fs.
As Readdir and Readdirnames are File methods, we need to access such
structure from the File.
* tarfs: fix error
* tarfs: use just the names for TestReaddir, easier than using fill os.FileInfo entries
* tarfs: create a copy of the original entry when opening a file
We added the fs field in the File struct to reference the underlying Fs
object, but in the Open cal we were not passing it, making all the
opened files to have a nil pointer in that field.
Change to make a copy of the original file, and returning that
* tarfs: implement File.Readdir
* tarfs: add tests for File.Readdirnames
* tarfs: implement Readdirnames
* tarfs: add test for File.Name
* tarfs: change tests to use the Afero interface instead
* tarfs: add tests for Glob from zipfs
* tarfs: update main repo references to tarfs
* tarfs: use OS-specific file separator for pseudoroot
* tarfs: fix path handling in Windows systems
2020-09-14 21:44:56 +03:00
|
|
|
"syscall"
|
|
|
|
"testing"
|
|
|
|
|
2022-12-12 18:01:24 +03:00
|
|
|
"git.internal/re/afero"
|
Add tarfs implementation (reopen #265) (#266)
* Initial commit for tarfs
* tarfs: reword "open" status field
* tarfs: use TestMain for FS setup
We want to have the FS variable available through all the tests, so we
we use a common "setup" function to initialise it.
* tarfs: test: early exit for nonexisting files
* tarfs: create test for filesystem Open
* tarfs: implement File.Stat
* tarfs: implement Fs.Open
* tarfs: return error on non-supported methods
As tarfs is a read-only filesystem backend, we return EROFS (Read-only
file system) from any method that makes modifications.
* tarfs: implement File.data as bytes.Reader
Most of the operations that we want to implement for tarfs.File are
already defined in bytes.Reader.
We could use a plain slice and implement all the seeking manually, but I
think using this is more convenient.
* tarfs: short format for simple methods
* tarfs: add missing closing brace in tests
* tarfs: add test for File.ReadAt
* tarfs: test File.ReadAt
* tarfs: add tests for File.Read
* tarfs: implement File.Read
* tarfs: add tests for File.Seek
* tarfs: implement File.Seek
* tarfs: add tests for File.Name
* tarfs: implement File.Name
* tarfs: add tests for File.Close
* tarfs: implement File.Close
* tarfs: add tests for OpenFile
* tarfs: fix test for Fs.OpenFile
If the call fails, we don't have to close the file
* tarfs: remove code not needed after using filepath.Clean
* tarfs: Open: return a copy of the internal structure
As we modify the struct fields when closing, we don't want to lose the
internal representation of the file, in case we want to reopen it.
Return a copy of the File, although we keep using the same pointers to
tar.Header and buffer.Reader. Maybe we will need to change that in the
future.
* tarfs: implement Fs.OpenFile
* tarfs: use Fatalf for unexpected error in TestFsOpen
* tarfs: add tests for Fs.Stat
* tarfs: implement Fs.Stat
* tarfs: remove TestNewFs
That test depends too much on the internal imlementation, and it is
easier to break if we change it.
* tarfs: remove unused code
* tarfs: change internal implementation
To be able to handle directories (File.Readdir, File.Readdirnames), the
naive single-map implementation makes it a bit harder to implement.
Inspired by the zipfs backend, switch to an internal implementation of a
map of directories that contains a map of files, so the directory
methods are easier to implement.
Also, treat the "virtual" filesystem as absolute, just like zipfs does.
* tarfs: use Fatal errors to avoid panics
* tarfs: add pseudoroot
* tarfs: add tests for File.Readdir
* tarfs: add pointer Fs in the File structure
For directory-related operations we will need to access the internal
structure in the Fs.
As Readdir and Readdirnames are File methods, we need to access such
structure from the File.
* tarfs: fix error
* tarfs: use just the names for TestReaddir, easier than using fill os.FileInfo entries
* tarfs: create a copy of the original entry when opening a file
We added the fs field in the File struct to reference the underlying Fs
object, but in the Open cal we were not passing it, making all the
opened files to have a nil pointer in that field.
Change to make a copy of the original file, and returning that
* tarfs: implement File.Readdir
* tarfs: add tests for File.Readdirnames
* tarfs: implement Readdirnames
* tarfs: add test for File.Name
* tarfs: change tests to use the Afero interface instead
* tarfs: add tests for Glob from zipfs
* tarfs: update main repo references to tarfs
* tarfs: use OS-specific file separator for pseudoroot
* tarfs: fix path handling in Windows systems
2020-09-14 21:44:56 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var files = []struct {
|
|
|
|
name string
|
|
|
|
exists bool
|
|
|
|
isdir bool
|
|
|
|
size int64
|
|
|
|
content string
|
|
|
|
contentAt4k string
|
|
|
|
}{
|
|
|
|
{"/", true, true, 0, "", ""},
|
|
|
|
{"/sub", true, true, 0, "", ""},
|
|
|
|
{"/sub/testDir2", true, true, 0, "", ""},
|
|
|
|
{"/sub/testDir2/testFile", true, false, 8192, "cccccccc", "ccccdddd"},
|
|
|
|
{"/testFile", true, false, 8192, "aaaaaaaa", "aaaabbbb"},
|
|
|
|
{"/testDir1/testFile", true, false, 8192, "bbbbbbbb", "bbbbcccc"},
|
|
|
|
|
|
|
|
{"/nonExisting", false, false, 0, "", ""},
|
|
|
|
}
|
|
|
|
|
|
|
|
var dirs = []struct {
|
|
|
|
name string
|
|
|
|
children []string
|
|
|
|
}{
|
|
|
|
{"/", []string{"sub", "testDir1", "testFile"}},
|
|
|
|
{"/sub", []string{"testDir2"}},
|
|
|
|
{"/sub/testDir2", []string{"testFile"}},
|
|
|
|
{"/testDir1", []string{"testFile"}},
|
|
|
|
}
|
|
|
|
|
|
|
|
var afs *afero.Afero
|
|
|
|
|
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
tf, err := os.Open("testdata/t.tar")
|
|
|
|
if err != nil {
|
|
|
|
fmt.Print(err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
tfs := New(tar.NewReader(tf))
|
|
|
|
afs = &afero.Afero{Fs: tfs}
|
2020-10-06 01:23:18 +03:00
|
|
|
|
|
|
|
// Check that an empty reader does not panic.
|
|
|
|
_ = New(tar.NewReader(strings.NewReader("")))
|
Add tarfs implementation (reopen #265) (#266)
* Initial commit for tarfs
* tarfs: reword "open" status field
* tarfs: use TestMain for FS setup
We want to have the FS variable available through all the tests, so we
we use a common "setup" function to initialise it.
* tarfs: test: early exit for nonexisting files
* tarfs: create test for filesystem Open
* tarfs: implement File.Stat
* tarfs: implement Fs.Open
* tarfs: return error on non-supported methods
As tarfs is a read-only filesystem backend, we return EROFS (Read-only
file system) from any method that makes modifications.
* tarfs: implement File.data as bytes.Reader
Most of the operations that we want to implement for tarfs.File are
already defined in bytes.Reader.
We could use a plain slice and implement all the seeking manually, but I
think using this is more convenient.
* tarfs: short format for simple methods
* tarfs: add missing closing brace in tests
* tarfs: add test for File.ReadAt
* tarfs: test File.ReadAt
* tarfs: add tests for File.Read
* tarfs: implement File.Read
* tarfs: add tests for File.Seek
* tarfs: implement File.Seek
* tarfs: add tests for File.Name
* tarfs: implement File.Name
* tarfs: add tests for File.Close
* tarfs: implement File.Close
* tarfs: add tests for OpenFile
* tarfs: fix test for Fs.OpenFile
If the call fails, we don't have to close the file
* tarfs: remove code not needed after using filepath.Clean
* tarfs: Open: return a copy of the internal structure
As we modify the struct fields when closing, we don't want to lose the
internal representation of the file, in case we want to reopen it.
Return a copy of the File, although we keep using the same pointers to
tar.Header and buffer.Reader. Maybe we will need to change that in the
future.
* tarfs: implement Fs.OpenFile
* tarfs: use Fatalf for unexpected error in TestFsOpen
* tarfs: add tests for Fs.Stat
* tarfs: implement Fs.Stat
* tarfs: remove TestNewFs
That test depends too much on the internal imlementation, and it is
easier to break if we change it.
* tarfs: remove unused code
* tarfs: change internal implementation
To be able to handle directories (File.Readdir, File.Readdirnames), the
naive single-map implementation makes it a bit harder to implement.
Inspired by the zipfs backend, switch to an internal implementation of a
map of directories that contains a map of files, so the directory
methods are easier to implement.
Also, treat the "virtual" filesystem as absolute, just like zipfs does.
* tarfs: use Fatal errors to avoid panics
* tarfs: add pseudoroot
* tarfs: add tests for File.Readdir
* tarfs: add pointer Fs in the File structure
For directory-related operations we will need to access the internal
structure in the Fs.
As Readdir and Readdirnames are File methods, we need to access such
structure from the File.
* tarfs: fix error
* tarfs: use just the names for TestReaddir, easier than using fill os.FileInfo entries
* tarfs: create a copy of the original entry when opening a file
We added the fs field in the File struct to reference the underlying Fs
object, but in the Open cal we were not passing it, making all the
opened files to have a nil pointer in that field.
Change to make a copy of the original file, and returning that
* tarfs: implement File.Readdir
* tarfs: add tests for File.Readdirnames
* tarfs: implement Readdirnames
* tarfs: add test for File.Name
* tarfs: change tests to use the Afero interface instead
* tarfs: add tests for Glob from zipfs
* tarfs: update main repo references to tarfs
* tarfs: use OS-specific file separator for pseudoroot
* tarfs: fix path handling in Windows systems
2020-09-14 21:44:56 +03:00
|
|
|
os.Exit(m.Run())
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFsOpen(t *testing.T) {
|
|
|
|
for _, f := range files {
|
|
|
|
file, err := afs.Open(f.name)
|
|
|
|
if (err == nil) != f.exists {
|
|
|
|
t.Errorf("%v exists = %v, but got err = %v", f.name, f.exists, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !f.exists {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("%v: %v", f.name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if file.Name() != filepath.FromSlash(f.name) {
|
|
|
|
t.Errorf("Name(), got %v, expected %v", file.Name(), filepath.FromSlash(f.name))
|
|
|
|
}
|
|
|
|
|
|
|
|
s, err := file.Stat()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("stat %v: got error '%v'", file.Name(), err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if isdir := s.IsDir(); isdir != f.isdir {
|
|
|
|
t.Errorf("%v directory, got: %v, expected: %v", file.Name(), isdir, f.isdir)
|
|
|
|
}
|
|
|
|
|
|
|
|
if size := s.Size(); size != f.size {
|
|
|
|
t.Errorf("%v size, got: %v, expected: %v", file.Name(), size, f.size)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRead(t *testing.T) {
|
|
|
|
for _, f := range files {
|
|
|
|
if !f.exists {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := afs.Open(f.name)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("opening %v: %v", f.name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := make([]byte, 8)
|
|
|
|
n, err := file.Read(buf)
|
|
|
|
if err != nil {
|
|
|
|
if f.isdir && (err != syscall.EISDIR) {
|
|
|
|
t.Errorf("%v got error %v, expected EISDIR", f.name, err)
|
|
|
|
} else if !f.isdir {
|
|
|
|
t.Errorf("%v: %v", f.name, err)
|
|
|
|
}
|
|
|
|
} else if n != 8 {
|
|
|
|
t.Errorf("%v: got %d read bytes, expected 8", f.name, n)
|
|
|
|
} else if string(buf) != f.content {
|
|
|
|
t.Errorf("%v: got <%s>, expected <%s>", f.name, f.content, string(buf))
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestReadAt(t *testing.T) {
|
|
|
|
for _, f := range files {
|
|
|
|
if !f.exists {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := afs.Open(f.name)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("opening %v: %v", f.name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := make([]byte, 8)
|
|
|
|
n, err := file.ReadAt(buf, 4092)
|
|
|
|
if err != nil {
|
|
|
|
if f.isdir && (err != syscall.EISDIR) {
|
|
|
|
t.Errorf("%v got error %v, expected EISDIR", f.name, err)
|
|
|
|
} else if !f.isdir {
|
|
|
|
t.Errorf("%v: %v", f.name, err)
|
|
|
|
}
|
|
|
|
} else if n != 8 {
|
|
|
|
t.Errorf("%v: got %d read bytes, expected 8", f.name, n)
|
|
|
|
} else if string(buf) != f.contentAt4k {
|
|
|
|
t.Errorf("%v: got <%s>, expected <%s>", f.name, f.contentAt4k, string(buf))
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSeek(t *testing.T) {
|
|
|
|
for _, f := range files {
|
|
|
|
if !f.exists {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := afs.Open(f.name)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("opening %v: %v", f.name, err)
|
|
|
|
}
|
|
|
|
|
2022-12-12 18:01:24 +03:00
|
|
|
tests := []struct {
|
Add tarfs implementation (reopen #265) (#266)
* Initial commit for tarfs
* tarfs: reword "open" status field
* tarfs: use TestMain for FS setup
We want to have the FS variable available through all the tests, so we
we use a common "setup" function to initialise it.
* tarfs: test: early exit for nonexisting files
* tarfs: create test for filesystem Open
* tarfs: implement File.Stat
* tarfs: implement Fs.Open
* tarfs: return error on non-supported methods
As tarfs is a read-only filesystem backend, we return EROFS (Read-only
file system) from any method that makes modifications.
* tarfs: implement File.data as bytes.Reader
Most of the operations that we want to implement for tarfs.File are
already defined in bytes.Reader.
We could use a plain slice and implement all the seeking manually, but I
think using this is more convenient.
* tarfs: short format for simple methods
* tarfs: add missing closing brace in tests
* tarfs: add test for File.ReadAt
* tarfs: test File.ReadAt
* tarfs: add tests for File.Read
* tarfs: implement File.Read
* tarfs: add tests for File.Seek
* tarfs: implement File.Seek
* tarfs: add tests for File.Name
* tarfs: implement File.Name
* tarfs: add tests for File.Close
* tarfs: implement File.Close
* tarfs: add tests for OpenFile
* tarfs: fix test for Fs.OpenFile
If the call fails, we don't have to close the file
* tarfs: remove code not needed after using filepath.Clean
* tarfs: Open: return a copy of the internal structure
As we modify the struct fields when closing, we don't want to lose the
internal representation of the file, in case we want to reopen it.
Return a copy of the File, although we keep using the same pointers to
tar.Header and buffer.Reader. Maybe we will need to change that in the
future.
* tarfs: implement Fs.OpenFile
* tarfs: use Fatalf for unexpected error in TestFsOpen
* tarfs: add tests for Fs.Stat
* tarfs: implement Fs.Stat
* tarfs: remove TestNewFs
That test depends too much on the internal imlementation, and it is
easier to break if we change it.
* tarfs: remove unused code
* tarfs: change internal implementation
To be able to handle directories (File.Readdir, File.Readdirnames), the
naive single-map implementation makes it a bit harder to implement.
Inspired by the zipfs backend, switch to an internal implementation of a
map of directories that contains a map of files, so the directory
methods are easier to implement.
Also, treat the "virtual" filesystem as absolute, just like zipfs does.
* tarfs: use Fatal errors to avoid panics
* tarfs: add pseudoroot
* tarfs: add tests for File.Readdir
* tarfs: add pointer Fs in the File structure
For directory-related operations we will need to access the internal
structure in the Fs.
As Readdir and Readdirnames are File methods, we need to access such
structure from the File.
* tarfs: fix error
* tarfs: use just the names for TestReaddir, easier than using fill os.FileInfo entries
* tarfs: create a copy of the original entry when opening a file
We added the fs field in the File struct to reference the underlying Fs
object, but in the Open cal we were not passing it, making all the
opened files to have a nil pointer in that field.
Change to make a copy of the original file, and returning that
* tarfs: implement File.Readdir
* tarfs: add tests for File.Readdirnames
* tarfs: implement Readdirnames
* tarfs: add test for File.Name
* tarfs: change tests to use the Afero interface instead
* tarfs: add tests for Glob from zipfs
* tarfs: update main repo references to tarfs
* tarfs: use OS-specific file separator for pseudoroot
* tarfs: fix path handling in Windows systems
2020-09-14 21:44:56 +03:00
|
|
|
offin int64
|
|
|
|
whence int
|
|
|
|
offout int64
|
|
|
|
}{
|
|
|
|
{0, io.SeekStart, 0},
|
|
|
|
{10, io.SeekStart, 10},
|
|
|
|
{1, io.SeekCurrent, 11},
|
|
|
|
{10, io.SeekCurrent, 21},
|
|
|
|
{0, io.SeekEnd, f.size},
|
|
|
|
{-1, io.SeekEnd, f.size - 1},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, s := range tests {
|
|
|
|
n, err := file.Seek(s.offin, s.whence)
|
|
|
|
if err != nil {
|
|
|
|
if f.isdir && err == syscall.EISDIR {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Errorf("%v: %v", f.name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if n != s.offout {
|
|
|
|
t.Errorf("%v: (off: %v, whence: %v): got %v, expected %v", f.name, s.offin, s.whence, n, s.offout)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestName(t *testing.T) {
|
|
|
|
for _, f := range files {
|
|
|
|
if !f.exists {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := afs.Open(f.name)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("opening %v: %v", f.name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
n := file.Name()
|
|
|
|
if n != filepath.FromSlash(f.name) {
|
|
|
|
t.Errorf("got: %v, expected: %v", n, filepath.FromSlash(f.name))
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestClose(t *testing.T) {
|
|
|
|
for _, f := range files {
|
|
|
|
if !f.exists {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := afs.Open(f.name)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("opening %v: %v", f.name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = file.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("%v: %v", f.name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = file.Close()
|
|
|
|
if err == nil {
|
|
|
|
t.Errorf("%v: closing twice should return an error", f.name)
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := make([]byte, 8)
|
|
|
|
n, err := file.Read(buf)
|
|
|
|
if n != 0 || err == nil {
|
|
|
|
t.Errorf("%v: could read from a closed file", f.name)
|
|
|
|
}
|
|
|
|
|
|
|
|
n, err = file.ReadAt(buf, 256)
|
|
|
|
if n != 0 || err == nil {
|
|
|
|
t.Errorf("%v: could readAt from a closed file", f.name)
|
|
|
|
}
|
|
|
|
|
|
|
|
off, err := file.Seek(0, io.SeekStart)
|
|
|
|
if off != 0 || err == nil {
|
|
|
|
t.Errorf("%v: could seek from a closed file", f.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestOpenFile(t *testing.T) {
|
|
|
|
for _, f := range files {
|
2022-12-12 18:01:24 +03:00
|
|
|
file, err := afs.OpenFile(f.name, os.O_RDONLY, 0o400)
|
Add tarfs implementation (reopen #265) (#266)
* Initial commit for tarfs
* tarfs: reword "open" status field
* tarfs: use TestMain for FS setup
We want to have the FS variable available through all the tests, so we
we use a common "setup" function to initialise it.
* tarfs: test: early exit for nonexisting files
* tarfs: create test for filesystem Open
* tarfs: implement File.Stat
* tarfs: implement Fs.Open
* tarfs: return error on non-supported methods
As tarfs is a read-only filesystem backend, we return EROFS (Read-only
file system) from any method that makes modifications.
* tarfs: implement File.data as bytes.Reader
Most of the operations that we want to implement for tarfs.File are
already defined in bytes.Reader.
We could use a plain slice and implement all the seeking manually, but I
think using this is more convenient.
* tarfs: short format for simple methods
* tarfs: add missing closing brace in tests
* tarfs: add test for File.ReadAt
* tarfs: test File.ReadAt
* tarfs: add tests for File.Read
* tarfs: implement File.Read
* tarfs: add tests for File.Seek
* tarfs: implement File.Seek
* tarfs: add tests for File.Name
* tarfs: implement File.Name
* tarfs: add tests for File.Close
* tarfs: implement File.Close
* tarfs: add tests for OpenFile
* tarfs: fix test for Fs.OpenFile
If the call fails, we don't have to close the file
* tarfs: remove code not needed after using filepath.Clean
* tarfs: Open: return a copy of the internal structure
As we modify the struct fields when closing, we don't want to lose the
internal representation of the file, in case we want to reopen it.
Return a copy of the File, although we keep using the same pointers to
tar.Header and buffer.Reader. Maybe we will need to change that in the
future.
* tarfs: implement Fs.OpenFile
* tarfs: use Fatalf for unexpected error in TestFsOpen
* tarfs: add tests for Fs.Stat
* tarfs: implement Fs.Stat
* tarfs: remove TestNewFs
That test depends too much on the internal imlementation, and it is
easier to break if we change it.
* tarfs: remove unused code
* tarfs: change internal implementation
To be able to handle directories (File.Readdir, File.Readdirnames), the
naive single-map implementation makes it a bit harder to implement.
Inspired by the zipfs backend, switch to an internal implementation of a
map of directories that contains a map of files, so the directory
methods are easier to implement.
Also, treat the "virtual" filesystem as absolute, just like zipfs does.
* tarfs: use Fatal errors to avoid panics
* tarfs: add pseudoroot
* tarfs: add tests for File.Readdir
* tarfs: add pointer Fs in the File structure
For directory-related operations we will need to access the internal
structure in the Fs.
As Readdir and Readdirnames are File methods, we need to access such
structure from the File.
* tarfs: fix error
* tarfs: use just the names for TestReaddir, easier than using fill os.FileInfo entries
* tarfs: create a copy of the original entry when opening a file
We added the fs field in the File struct to reference the underlying Fs
object, but in the Open cal we were not passing it, making all the
opened files to have a nil pointer in that field.
Change to make a copy of the original file, and returning that
* tarfs: implement File.Readdir
* tarfs: add tests for File.Readdirnames
* tarfs: implement Readdirnames
* tarfs: add test for File.Name
* tarfs: change tests to use the Afero interface instead
* tarfs: add tests for Glob from zipfs
* tarfs: update main repo references to tarfs
* tarfs: use OS-specific file separator for pseudoroot
* tarfs: fix path handling in Windows systems
2020-09-14 21:44:56 +03:00
|
|
|
if !f.exists {
|
|
|
|
if !errors.Is(err, syscall.ENOENT) {
|
|
|
|
t.Errorf("%v: got %v, expected%v", f.name, err, syscall.ENOENT)
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("%v: %v", f.name, err)
|
|
|
|
}
|
|
|
|
file.Close()
|
|
|
|
|
2022-12-12 18:01:24 +03:00
|
|
|
_, err = afs.OpenFile(f.name, os.O_CREATE, 0o600)
|
Add tarfs implementation (reopen #265) (#266)
* Initial commit for tarfs
* tarfs: reword "open" status field
* tarfs: use TestMain for FS setup
We want to have the FS variable available through all the tests, so we
we use a common "setup" function to initialise it.
* tarfs: test: early exit for nonexisting files
* tarfs: create test for filesystem Open
* tarfs: implement File.Stat
* tarfs: implement Fs.Open
* tarfs: return error on non-supported methods
As tarfs is a read-only filesystem backend, we return EROFS (Read-only
file system) from any method that makes modifications.
* tarfs: implement File.data as bytes.Reader
Most of the operations that we want to implement for tarfs.File are
already defined in bytes.Reader.
We could use a plain slice and implement all the seeking manually, but I
think using this is more convenient.
* tarfs: short format for simple methods
* tarfs: add missing closing brace in tests
* tarfs: add test for File.ReadAt
* tarfs: test File.ReadAt
* tarfs: add tests for File.Read
* tarfs: implement File.Read
* tarfs: add tests for File.Seek
* tarfs: implement File.Seek
* tarfs: add tests for File.Name
* tarfs: implement File.Name
* tarfs: add tests for File.Close
* tarfs: implement File.Close
* tarfs: add tests for OpenFile
* tarfs: fix test for Fs.OpenFile
If the call fails, we don't have to close the file
* tarfs: remove code not needed after using filepath.Clean
* tarfs: Open: return a copy of the internal structure
As we modify the struct fields when closing, we don't want to lose the
internal representation of the file, in case we want to reopen it.
Return a copy of the File, although we keep using the same pointers to
tar.Header and buffer.Reader. Maybe we will need to change that in the
future.
* tarfs: implement Fs.OpenFile
* tarfs: use Fatalf for unexpected error in TestFsOpen
* tarfs: add tests for Fs.Stat
* tarfs: implement Fs.Stat
* tarfs: remove TestNewFs
That test depends too much on the internal imlementation, and it is
easier to break if we change it.
* tarfs: remove unused code
* tarfs: change internal implementation
To be able to handle directories (File.Readdir, File.Readdirnames), the
naive single-map implementation makes it a bit harder to implement.
Inspired by the zipfs backend, switch to an internal implementation of a
map of directories that contains a map of files, so the directory
methods are easier to implement.
Also, treat the "virtual" filesystem as absolute, just like zipfs does.
* tarfs: use Fatal errors to avoid panics
* tarfs: add pseudoroot
* tarfs: add tests for File.Readdir
* tarfs: add pointer Fs in the File structure
For directory-related operations we will need to access the internal
structure in the Fs.
As Readdir and Readdirnames are File methods, we need to access such
structure from the File.
* tarfs: fix error
* tarfs: use just the names for TestReaddir, easier than using fill os.FileInfo entries
* tarfs: create a copy of the original entry when opening a file
We added the fs field in the File struct to reference the underlying Fs
object, but in the Open cal we were not passing it, making all the
opened files to have a nil pointer in that field.
Change to make a copy of the original file, and returning that
* tarfs: implement File.Readdir
* tarfs: add tests for File.Readdirnames
* tarfs: implement Readdirnames
* tarfs: add test for File.Name
* tarfs: change tests to use the Afero interface instead
* tarfs: add tests for Glob from zipfs
* tarfs: update main repo references to tarfs
* tarfs: use OS-specific file separator for pseudoroot
* tarfs: fix path handling in Windows systems
2020-09-14 21:44:56 +03:00
|
|
|
if !errors.Is(err, syscall.EPERM) {
|
|
|
|
t.Errorf("%v: open for write: got %v, expected %v", f.name, err, syscall.EPERM)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFsStat(t *testing.T) {
|
|
|
|
for _, f := range files {
|
|
|
|
fi, err := afs.Stat(f.name)
|
|
|
|
if !f.exists {
|
|
|
|
if !errors.Is(err, syscall.ENOENT) {
|
|
|
|
t.Errorf("%v: got %v, expected%v", f.name, err, syscall.ENOENT)
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("stat %v: got error '%v'", f.name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if isdir := fi.IsDir(); isdir != f.isdir {
|
|
|
|
t.Errorf("%v directory, got: %v, expected: %v", f.name, isdir, f.isdir)
|
|
|
|
}
|
|
|
|
|
|
|
|
if size := fi.Size(); size != f.size {
|
|
|
|
t.Errorf("%v size, got: %v, expected: %v", f.name, size, f.size)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestReaddir(t *testing.T) {
|
|
|
|
for _, d := range dirs {
|
|
|
|
dir, err := afs.Open(d.name)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
fi, err := dir.Readdir(0)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
var names []string
|
|
|
|
for _, f := range fi {
|
|
|
|
names = append(names, f.Name())
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(names, d.children) {
|
|
|
|
t.Errorf("%v: children, got '%v', expected '%v'", d.name, names, d.children)
|
|
|
|
}
|
|
|
|
|
|
|
|
fi, err = dir.Readdir(1)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
names = []string{}
|
|
|
|
for _, f := range fi {
|
|
|
|
names = append(names, f.Name())
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(names, d.children[0:1]) {
|
|
|
|
t.Errorf("%v: children, got '%v', expected '%v'", d.name, names, d.children[0:1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dir, err := afs.Open("/testFile")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = dir.Readdir(-1)
|
|
|
|
if err != syscall.ENOTDIR {
|
|
|
|
t.Fatal("Expected error")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestReaddirnames(t *testing.T) {
|
|
|
|
for _, d := range dirs {
|
|
|
|
dir, err := afs.Open(d.name)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
names, err := dir.Readdirnames(0)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(names, d.children) {
|
|
|
|
t.Errorf("%v: children, got '%v', expected '%v'", d.name, names, d.children)
|
|
|
|
}
|
|
|
|
|
|
|
|
names, err = dir.Readdirnames(1)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(names, d.children[0:1]) {
|
|
|
|
t.Errorf("%v: children, got '%v', expected '%v'", d.name, names, d.children[0:1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dir, err := afs.Open("/testFile")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = dir.Readdir(-1)
|
|
|
|
if err != syscall.ENOTDIR {
|
|
|
|
t.Fatal("Expected error")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGlob(t *testing.T) {
|
|
|
|
for _, s := range []struct {
|
|
|
|
glob string
|
|
|
|
entries []string
|
|
|
|
}{
|
|
|
|
{filepath.FromSlash("/*"), []string{filepath.FromSlash("/sub"), filepath.FromSlash("/testDir1"), filepath.FromSlash("/testFile")}},
|
|
|
|
{filepath.FromSlash("*"), []string{filepath.FromSlash("sub"), filepath.FromSlash("testDir1"), filepath.FromSlash("testFile")}},
|
|
|
|
{filepath.FromSlash("sub/*"), []string{filepath.FromSlash("sub/testDir2")}},
|
|
|
|
{filepath.FromSlash("sub/testDir2/*"), []string{filepath.FromSlash("sub/testDir2/testFile")}},
|
|
|
|
{filepath.FromSlash("testDir1/*"), []string{filepath.FromSlash("testDir1/testFile")}},
|
|
|
|
} {
|
|
|
|
entries, err := afero.Glob(afs.Fs, s.glob)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
if reflect.DeepEqual(entries, s.entries) {
|
|
|
|
t.Logf("glob: %s: glob ok", s.glob)
|
|
|
|
} else {
|
|
|
|
t.Errorf("glob: %s: got %#v, expected %#v", s.glob, entries, s.entries)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|