mirror of
https://github.com/octoleo/syncthing.git
synced 2024-09-19 05:09:01 +00:00
lib/fs: Add case-insensitive fakefs (#6074)
This commit is contained in:
parent
c71116ee94
commit
1c277fc096
145
lib/fs/fakefs.go
145
lib/fs/fakefs.go
@ -47,12 +47,14 @@ const randomBlockShift = 14 // 128k
|
|||||||
// maxsize=n to generate files up to a total of n MiB (default 0)
|
// maxsize=n to generate files up to a total of n MiB (default 0)
|
||||||
// sizeavg=n to set the average size of random files, in bytes (default 1<<20)
|
// sizeavg=n to set the average size of random files, in bytes (default 1<<20)
|
||||||
// seed=n to set the initial random seed (default 0)
|
// seed=n to set the initial random seed (default 0)
|
||||||
|
// insens=b "true" makes filesystem case-insensitive Windows- or OSX-style (default false)
|
||||||
//
|
//
|
||||||
// - Two fakefs:s pointing at the same root path see the same files.
|
// - Two fakefs:s pointing at the same root path see the same files.
|
||||||
//
|
//
|
||||||
type fakefs struct {
|
type fakefs struct {
|
||||||
mut sync.Mutex
|
mut sync.Mutex
|
||||||
root *fakeEntry
|
root *fakeEntry
|
||||||
|
insens bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -90,6 +92,10 @@ func newFakeFilesystem(root string) *fakefs {
|
|||||||
maxsize, _ := strconv.Atoi(params.Get("maxsize"))
|
maxsize, _ := strconv.Atoi(params.Get("maxsize"))
|
||||||
sizeavg, _ := strconv.Atoi(params.Get("sizeavg"))
|
sizeavg, _ := strconv.Atoi(params.Get("sizeavg"))
|
||||||
seed, _ := strconv.Atoi(params.Get("seed"))
|
seed, _ := strconv.Atoi(params.Get("seed"))
|
||||||
|
|
||||||
|
if params.Get("insens") == "true" {
|
||||||
|
fs.insens = true
|
||||||
|
}
|
||||||
if sizeavg == 0 {
|
if sizeavg == 0 {
|
||||||
sizeavg = 1 << 20
|
sizeavg = 1 << 20
|
||||||
}
|
}
|
||||||
@ -149,6 +155,9 @@ type fakeEntry struct {
|
|||||||
|
|
||||||
func (fs *fakefs) entryForName(name string) *fakeEntry {
|
func (fs *fakefs) entryForName(name string) *fakeEntry {
|
||||||
// bug: lookup doesn't work through symlinks.
|
// bug: lookup doesn't work through symlinks.
|
||||||
|
if fs.insens {
|
||||||
|
name = UnicodeLowercase(name)
|
||||||
|
}
|
||||||
|
|
||||||
name = filepath.ToSlash(name)
|
name = filepath.ToSlash(name)
|
||||||
if name == "." || name == "/" {
|
if name == "." || name == "/" {
|
||||||
@ -232,6 +241,11 @@ func (fs *fakefs) create(name string) (*fakeEntry, error) {
|
|||||||
mode: 0666,
|
mode: 0666,
|
||||||
mtime: time.Now(),
|
mtime: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fs.insens {
|
||||||
|
base = UnicodeLowercase(base)
|
||||||
|
}
|
||||||
|
|
||||||
entry.children[base] = new
|
entry.children[base] = new
|
||||||
return new, nil
|
return new, nil
|
||||||
}
|
}
|
||||||
@ -241,6 +255,9 @@ func (fs *fakefs) Create(name string) (File, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if fs.insens {
|
||||||
|
return &fakeFile{fakeEntry: entry, presentedName: filepath.Base(name)}, nil
|
||||||
|
}
|
||||||
return &fakeFile{fakeEntry: entry}, nil
|
return &fakeFile{fakeEntry: entry}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,8 +281,8 @@ func (fs *fakefs) DirNames(name string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
names := make([]string, 0, len(entry.children))
|
names := make([]string, 0, len(entry.children))
|
||||||
for name := range entry.children {
|
for _, child := range entry.children {
|
||||||
names = append(names, name)
|
names = append(names, child.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return names, nil
|
return names, nil
|
||||||
@ -279,7 +296,13 @@ func (fs *fakefs) Lstat(name string) (FileInfo, error) {
|
|||||||
if entry == nil {
|
if entry == nil {
|
||||||
return nil, os.ErrNotExist
|
return nil, os.ErrNotExist
|
||||||
}
|
}
|
||||||
return &fakeFileInfo{*entry}, nil
|
|
||||||
|
info := &fakeFileInfo{*entry}
|
||||||
|
if fs.insens {
|
||||||
|
info.name = filepath.Base(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *fakefs) Mkdir(name string, perm FileMode) error {
|
func (fs *fakefs) Mkdir(name string, perm FileMode) error {
|
||||||
@ -289,17 +312,22 @@ func (fs *fakefs) Mkdir(name string, perm FileMode) error {
|
|||||||
dir := filepath.Dir(name)
|
dir := filepath.Dir(name)
|
||||||
base := filepath.Base(name)
|
base := filepath.Base(name)
|
||||||
entry := fs.entryForName(dir)
|
entry := fs.entryForName(dir)
|
||||||
|
key := base
|
||||||
|
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
return os.ErrNotExist
|
return os.ErrNotExist
|
||||||
}
|
}
|
||||||
if entry.entryType != fakeEntryTypeDir {
|
if entry.entryType != fakeEntryTypeDir {
|
||||||
return os.ErrExist
|
return os.ErrExist
|
||||||
}
|
}
|
||||||
if _, ok := entry.children[base]; ok {
|
if fs.insens {
|
||||||
|
key = UnicodeLowercase(key)
|
||||||
|
}
|
||||||
|
if _, ok := entry.children[key]; ok {
|
||||||
return os.ErrExist
|
return os.ErrExist
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.children[base] = &fakeEntry{
|
entry.children[key] = &fakeEntry{
|
||||||
name: base,
|
name: base,
|
||||||
entryType: fakeEntryTypeDir,
|
entryType: fakeEntryTypeDir,
|
||||||
mode: perm,
|
mode: perm,
|
||||||
@ -315,7 +343,12 @@ func (fs *fakefs) MkdirAll(name string, perm FileMode) error {
|
|||||||
comps := strings.Split(name, "/")
|
comps := strings.Split(name, "/")
|
||||||
entry := fs.root
|
entry := fs.root
|
||||||
for _, comp := range comps {
|
for _, comp := range comps {
|
||||||
next, ok := entry.children[comp]
|
key := comp
|
||||||
|
if fs.insens {
|
||||||
|
key = UnicodeLowercase(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
next, ok := entry.children[key]
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
new := &fakeEntry{
|
new := &fakeEntry{
|
||||||
@ -325,7 +358,7 @@ func (fs *fakefs) MkdirAll(name string, perm FileMode) error {
|
|||||||
mtime: time.Now(),
|
mtime: time.Now(),
|
||||||
children: make(map[string]*fakeEntry),
|
children: make(map[string]*fakeEntry),
|
||||||
}
|
}
|
||||||
entry.children[comp] = new
|
entry.children[key] = new
|
||||||
next = new
|
next = new
|
||||||
} else if next.entryType != fakeEntryTypeDir {
|
} else if next.entryType != fakeEntryTypeDir {
|
||||||
return errors.New("not a directory")
|
return errors.New("not a directory")
|
||||||
@ -344,28 +377,37 @@ func (fs *fakefs) Open(name string) (File, error) {
|
|||||||
if entry == nil || entry.entryType != fakeEntryTypeFile {
|
if entry == nil || entry.entryType != fakeEntryTypeFile {
|
||||||
return nil, os.ErrNotExist
|
return nil, os.ErrNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fs.insens {
|
||||||
|
return &fakeFile{fakeEntry: entry, presentedName: filepath.Base(name)}, nil
|
||||||
|
}
|
||||||
return &fakeFile{fakeEntry: entry}, nil
|
return &fakeFile{fakeEntry: entry}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error) {
|
func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error) {
|
||||||
fs.mut.Lock()
|
|
||||||
defer fs.mut.Unlock()
|
|
||||||
|
|
||||||
if flags&os.O_CREATE == 0 {
|
if flags&os.O_CREATE == 0 {
|
||||||
return fs.Open(name)
|
return fs.Open(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs.mut.Lock()
|
||||||
|
defer fs.mut.Unlock()
|
||||||
|
|
||||||
dir := filepath.Dir(name)
|
dir := filepath.Dir(name)
|
||||||
base := filepath.Base(name)
|
base := filepath.Base(name)
|
||||||
entry := fs.entryForName(dir)
|
entry := fs.entryForName(dir)
|
||||||
|
key := base
|
||||||
|
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
return nil, os.ErrNotExist
|
return nil, os.ErrNotExist
|
||||||
} else if entry.entryType != fakeEntryTypeDir {
|
} else if entry.entryType != fakeEntryTypeDir {
|
||||||
return nil, errors.New("not a directory")
|
return nil, errors.New("not a directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fs.insens {
|
||||||
|
key = UnicodeLowercase(key)
|
||||||
|
}
|
||||||
if flags&os.O_EXCL != 0 {
|
if flags&os.O_EXCL != 0 {
|
||||||
if _, ok := entry.children[base]; ok {
|
if _, ok := entry.children[key]; ok {
|
||||||
return nil, os.ErrExist
|
return nil, os.ErrExist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,7 +418,7 @@ func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error)
|
|||||||
mtime: time.Now(),
|
mtime: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.children[base] = newEntry
|
entry.children[key] = newEntry
|
||||||
return &fakeFile{fakeEntry: newEntry}, nil
|
return &fakeFile{fakeEntry: newEntry}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,6 +439,10 @@ func (fs *fakefs) Remove(name string) error {
|
|||||||
fs.mut.Lock()
|
fs.mut.Lock()
|
||||||
defer fs.mut.Unlock()
|
defer fs.mut.Unlock()
|
||||||
|
|
||||||
|
if fs.insens {
|
||||||
|
name = UnicodeLowercase(name)
|
||||||
|
}
|
||||||
|
|
||||||
entry := fs.entryForName(name)
|
entry := fs.entryForName(name)
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
return os.ErrNotExist
|
return os.ErrNotExist
|
||||||
@ -414,9 +460,13 @@ func (fs *fakefs) RemoveAll(name string) error {
|
|||||||
fs.mut.Lock()
|
fs.mut.Lock()
|
||||||
defer fs.mut.Unlock()
|
defer fs.mut.Unlock()
|
||||||
|
|
||||||
|
if fs.insens {
|
||||||
|
name = UnicodeLowercase(name)
|
||||||
|
}
|
||||||
|
|
||||||
entry := fs.entryForName(filepath.Dir(name))
|
entry := fs.entryForName(filepath.Dir(name))
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
return os.ErrNotExist
|
return nil // all tested real systems exibit this behaviour
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveAll is easy when the file system uses garbage collection under
|
// RemoveAll is easy when the file system uses garbage collection under
|
||||||
@ -429,12 +479,20 @@ func (fs *fakefs) Rename(oldname, newname string) error {
|
|||||||
fs.mut.Lock()
|
fs.mut.Lock()
|
||||||
defer fs.mut.Unlock()
|
defer fs.mut.Unlock()
|
||||||
|
|
||||||
|
oldKey := filepath.Base(oldname)
|
||||||
|
newKey := filepath.Base(newname)
|
||||||
|
|
||||||
|
if fs.insens {
|
||||||
|
oldKey = UnicodeLowercase(oldKey)
|
||||||
|
newKey = UnicodeLowercase(newKey)
|
||||||
|
}
|
||||||
|
|
||||||
p0 := fs.entryForName(filepath.Dir(oldname))
|
p0 := fs.entryForName(filepath.Dir(oldname))
|
||||||
if p0 == nil {
|
if p0 == nil {
|
||||||
return os.ErrNotExist
|
return os.ErrNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := p0.children[filepath.Base(oldname)]
|
entry := p0.children[oldKey]
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
return os.ErrNotExist
|
return os.ErrNotExist
|
||||||
}
|
}
|
||||||
@ -444,13 +502,24 @@ func (fs *fakefs) Rename(oldname, newname string) error {
|
|||||||
return os.ErrNotExist
|
return os.ErrNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
dst, ok := p1.children[filepath.Base(newname)]
|
dst, ok := p1.children[newKey]
|
||||||
if ok && dst.entryType == fakeEntryTypeDir {
|
if ok {
|
||||||
return errors.New("is a directory")
|
if fs.insens && newKey == oldKey {
|
||||||
|
// case-only in-place rename
|
||||||
|
entry.name = filepath.Base(newname)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst.entryType == fakeEntryTypeDir {
|
||||||
|
return errors.New("is a directory")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p1.children[filepath.Base(newname)] = entry
|
p1.children[newKey] = entry
|
||||||
delete(p0.children, filepath.Base(oldname))
|
entry.name = filepath.Base(newname)
|
||||||
|
|
||||||
|
delete(p0.children, oldKey)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,18 +569,30 @@ func (fs *fakefs) URI() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *fakefs) SameFile(fi1, fi2 FileInfo) bool {
|
func (fs *fakefs) SameFile(fi1, fi2 FileInfo) bool {
|
||||||
return fi1.Name() == fi2.Name()
|
// BUG: real systems base file sameness on path, inodes, etc
|
||||||
|
// we try our best, but FileInfo just doesn't have enough data
|
||||||
|
// so there be false positives, especially on Windows
|
||||||
|
// where ModTime is not that precise
|
||||||
|
var ok bool
|
||||||
|
if fs.insens {
|
||||||
|
ok = UnicodeLowercase(fi1.Name()) == UnicodeLowercase(fi2.Name())
|
||||||
|
} else {
|
||||||
|
ok = fi1.Name() == fi2.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok && fi1.ModTime().Equal(fi2.ModTime()) && fi1.Mode() == fi2.Mode() && fi1.IsDir() == fi2.IsDir() && fi1.IsRegular() == fi2.IsRegular() && fi1.IsSymlink() == fi2.IsSymlink() && fi1.Owner() == fi2.Owner() && fi1.Group() == fi2.Group()
|
||||||
}
|
}
|
||||||
|
|
||||||
// fakeFile is the representation of an open file. We don't care if it's
|
// fakeFile is the representation of an open file. We don't care if it's
|
||||||
// opened for reading or writing, it's all good.
|
// opened for reading or writing, it's all good.
|
||||||
type fakeFile struct {
|
type fakeFile struct {
|
||||||
*fakeEntry
|
*fakeEntry
|
||||||
mut sync.Mutex
|
mut sync.Mutex
|
||||||
rng io.Reader
|
rng io.Reader
|
||||||
seed int64
|
seed int64
|
||||||
offset int64
|
offset int64
|
||||||
seedOffs int64
|
seedOffs int64
|
||||||
|
presentedName string // present (i.e. != "") on insensitive fs only
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeFile) Close() error {
|
func (f *fakeFile) Close() error {
|
||||||
@ -674,6 +755,9 @@ func (f *fakeFile) WriteAt(p []byte, off int64) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeFile) Name() string {
|
func (f *fakeFile) Name() string {
|
||||||
|
if f.presentedName != "" {
|
||||||
|
return f.presentedName
|
||||||
|
}
|
||||||
return f.name
|
return f.name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,7 +774,12 @@ func (f *fakeFile) Truncate(size int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeFile) Stat() (FileInfo, error) {
|
func (f *fakeFile) Stat() (FileInfo, error) {
|
||||||
return &fakeFileInfo{*f.fakeEntry}, nil
|
info := &fakeFileInfo{*f.fakeEntry}
|
||||||
|
if f.presentedName != "" {
|
||||||
|
info.name = f.presentedName
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeFile) Sync() error {
|
func (f *fakeFile) Sync() error {
|
||||||
|
@ -8,9 +8,16 @@ package fs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFakeFS(t *testing.T) {
|
func TestFakeFS(t *testing.T) {
|
||||||
@ -123,13 +130,11 @@ func TestFakeFS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFakeFSRead(t *testing.T) {
|
func testFakeFSRead(t *testing.T, fs Filesystem) {
|
||||||
// Test some basic aspects of the fakefs
|
// Test some basic aspects of the fakefs
|
||||||
|
|
||||||
fs := newFakeFilesystem("/foo/bar/baz")
|
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
fd, _ := fs.Create("test")
|
fd, _ := fs.Create("test")
|
||||||
|
defer fd.Close()
|
||||||
fd.Truncate(3 * 1 << randomBlockShift)
|
fd.Truncate(3 * 1 << randomBlockShift)
|
||||||
|
|
||||||
// Read
|
// Read
|
||||||
@ -172,3 +177,734 @@ func TestFakeFSRead(t *testing.T) {
|
|||||||
t.Error("data mismatch")
|
t.Error("data mismatch")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testFS struct {
|
||||||
|
name string
|
||||||
|
fs Filesystem
|
||||||
|
}
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
name string
|
||||||
|
impl func(t *testing.T, fs Filesystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFakeFSCaseSensitive(t *testing.T) {
|
||||||
|
var tests = []test{
|
||||||
|
{"Read", testFakeFSRead},
|
||||||
|
{"OpenFile", testFakeFSOpenFile},
|
||||||
|
{"RemoveAll", testFakeFSRemoveAll},
|
||||||
|
{"Remove", testFakeFSRemove},
|
||||||
|
{"Rename", testFakeFSRename},
|
||||||
|
{"Mkdir", testFakeFSMkdir},
|
||||||
|
{"SameFile", testFakeFSSameFile},
|
||||||
|
{"DirNames", testDirNames},
|
||||||
|
{"FileName", testFakeFSFileName},
|
||||||
|
}
|
||||||
|
var filesystems = []testFS{
|
||||||
|
{"fakefs", newFakeFilesystem("/foo")},
|
||||||
|
}
|
||||||
|
|
||||||
|
testDir, sensitive := createTestDir(t)
|
||||||
|
defer removeTestDir(t, testDir)
|
||||||
|
if sensitive {
|
||||||
|
filesystems = append(filesystems, testFS{runtime.GOOS, newBasicFilesystem(testDir)})
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, tests, filesystems)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFakeFSCaseInsensitive(t *testing.T) {
|
||||||
|
var tests = []test{
|
||||||
|
{"Read", testFakeFSRead},
|
||||||
|
{"OpenFile", testFakeFSOpenFile},
|
||||||
|
{"RemoveAll", testFakeFSRemoveAll},
|
||||||
|
{"Remove", testFakeFSRemove},
|
||||||
|
{"Mkdir", testFakeFSMkdir},
|
||||||
|
{"SameFile", testFakeFSSameFile},
|
||||||
|
{"DirNames", testDirNames},
|
||||||
|
{"FileName", testFakeFSFileName},
|
||||||
|
{"GeneralInsens", testFakeFSCaseInsensitive},
|
||||||
|
{"MkdirAllInsens", testFakeFSCaseInsensitiveMkdirAll},
|
||||||
|
{"StatInsens", testFakeFSStatInsens},
|
||||||
|
{"RenameInsens", testFakeFSRenameInsensitive},
|
||||||
|
{"MkdirInsens", testFakeFSMkdirInsens},
|
||||||
|
{"OpenFileInsens", testFakeFSOpenFileInsens},
|
||||||
|
{"RemoveAllInsens", testFakeFSRemoveAllInsens},
|
||||||
|
{"RemoveInsens", testFakeFSRemoveInsens},
|
||||||
|
{"SameFileInsens", testFakeFSSameFileInsens},
|
||||||
|
{"CreateInsens", testFakeFSCreateInsens},
|
||||||
|
{"FileNameInsens", testFakeFSFileNameInsens},
|
||||||
|
}
|
||||||
|
|
||||||
|
var filesystems = []testFS{
|
||||||
|
{"fakefs", newFakeFilesystem("/foobar?insens=true")},
|
||||||
|
}
|
||||||
|
|
||||||
|
testDir, sensitive := createTestDir(t)
|
||||||
|
defer removeTestDir(t, testDir)
|
||||||
|
if !sensitive {
|
||||||
|
filesystems = append(filesystems, testFS{runtime.GOOS, newBasicFilesystem(testDir)})
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, tests, filesystems)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestDir(t *testing.T) (string, bool) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
testDir, err := ioutil.TempDir("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not create temporary dir for testing: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fd, err := os.Create(filepath.Join(testDir, ".stfolder")); err != nil {
|
||||||
|
t.Fatalf("could not create .stfolder: %s", err)
|
||||||
|
} else {
|
||||||
|
fd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
var sensitive bool
|
||||||
|
|
||||||
|
if f, err := os.Open(filepath.Join(testDir, ".STfolder")); err != nil {
|
||||||
|
sensitive = true
|
||||||
|
} else {
|
||||||
|
defer f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return testDir, sensitive
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeTestDir(t *testing.T, testDir string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
if err := os.RemoveAll(testDir); err != nil {
|
||||||
|
t.Fatalf("could not remove test directory: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTests(t *testing.T, tests []test, filesystems []testFS) {
|
||||||
|
for _, filesystem := range filesystems {
|
||||||
|
for _, test := range tests {
|
||||||
|
name := fmt.Sprintf("%s_%s", test.name, filesystem.name)
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
test.impl(t, filesystem.fs)
|
||||||
|
if err := cleanup(filesystem.fs); err != nil {
|
||||||
|
t.Errorf("cleanup failed: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSCaseInsensitive(t *testing.T, fs Filesystem) {
|
||||||
|
bs1 := []byte("test")
|
||||||
|
|
||||||
|
err := fs.Mkdir("/fUbar", 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd1, err := fs.Create("fuBAR/SISYPHOS")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not create file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd1.Close()
|
||||||
|
|
||||||
|
_, err = fd1.Write(bs1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try reading from the same file with different filenames
|
||||||
|
fd2, err := fs.Open("Fubar/Sisyphos")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not open file by its case-differing filename: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd2.Close()
|
||||||
|
|
||||||
|
if _, err := fd2.Seek(0, io.SeekStart); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bs2, err := ioutil.ReadAll(fd2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bs1) != len(bs2) {
|
||||||
|
t.Errorf("wrong number of bytes, expected %d, got %d", len(bs1), len(bs2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSCaseInsensitiveMkdirAll(t *testing.T, fs Filesystem) {
|
||||||
|
err := fs.MkdirAll("/fOO/Bar/bAz", 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := fs.OpenFile("/foo/BaR/BaZ/tESt", os.O_CREATE, 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = fd.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = fs.Rename("/FOO/BAR/baz/tesT", "/foo/baR/BAZ/Qux"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDirNames(t *testing.T, fs Filesystem) {
|
||||||
|
filenames := []string{"fOO", "Bar", "baz"}
|
||||||
|
for _, filename := range filenames {
|
||||||
|
if fd, err := fs.Create("/" + filename); err != nil {
|
||||||
|
t.Errorf("Could not create %s: %s", filename, err)
|
||||||
|
} else {
|
||||||
|
fd.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertDir(t, fs, "/", filenames)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertDir(t *testing.T, fs Filesystem, directory string, filenames []string) {
|
||||||
|
t.Helper()
|
||||||
|
got, err := fs.DirNames(directory)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if path.Clean(directory) == "/" {
|
||||||
|
filenames = append(filenames, ".stfolder")
|
||||||
|
}
|
||||||
|
sort.Strings(filenames)
|
||||||
|
sort.Strings(got)
|
||||||
|
|
||||||
|
if len(filenames) != len(got) {
|
||||||
|
t.Errorf("want %s, got %s", filenames, got)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range filenames {
|
||||||
|
if filenames[i] != got[i] {
|
||||||
|
t.Errorf("want %s, got %s", filenames, got)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSStatInsens(t *testing.T, fs Filesystem) {
|
||||||
|
// this is to test that neither fs.Stat nor fd.Stat change the filename
|
||||||
|
// both in directory and in previous Stat results
|
||||||
|
fd1, err := fs.Create("aAa")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer fd1.Close()
|
||||||
|
|
||||||
|
info1, err := fs.Stat("AAA")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = fs.Stat("AaA"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info2, err := fd1.Stat()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd2, err := fs.Open("aaa")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer fd2.Close()
|
||||||
|
|
||||||
|
if _, err = fd2.Stat(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if info1.Name() != "AAA" {
|
||||||
|
t.Errorf("want AAA, got %s", info1.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if info2.Name() != "aAa" {
|
||||||
|
t.Errorf("want aAa, got %s", info2.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
assertDir(t, fs, "/", []string{"aAa"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSFileName(t *testing.T, fs Filesystem) {
|
||||||
|
var testCases = []struct {
|
||||||
|
create string
|
||||||
|
open string
|
||||||
|
}{
|
||||||
|
{"bar", "bar"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
if fd, err := fs.Create(testCase.create); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
fd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := fs.Open(testCase.open)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
if got := fd.Name(); got != testCase.open {
|
||||||
|
t.Errorf("want %s, got %s", testCase.open, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSFileNameInsens(t *testing.T, fs Filesystem) {
|
||||||
|
var testCases = []struct {
|
||||||
|
create string
|
||||||
|
open string
|
||||||
|
}{
|
||||||
|
{"BaZ", "bAz"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
fd, err := fs.Create(testCase.create)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
fd, err = fs.Open(testCase.open)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
if got := fd.Name(); got != testCase.open {
|
||||||
|
t.Errorf("want %s, got %s", testCase.open, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSRename(t *testing.T, fs Filesystem) {
|
||||||
|
if err := fs.MkdirAll("/foo/bar/baz", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := fs.Create("/foo/bar/baz/qux")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
if err := fs.Rename("/foo/bar/baz/qux", "/foo/notthere/qux"); err == nil {
|
||||||
|
t.Errorf("rename to non-existent dir gave no error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.MkdirAll("/baz/bar/foo", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.Rename("/foo/bar/baz/qux", "/baz/bar/foo/qux"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dirs = []struct {
|
||||||
|
dir string
|
||||||
|
files []string
|
||||||
|
}{
|
||||||
|
{dir: "/", files: []string{"foo", "baz"}},
|
||||||
|
{dir: "/foo", files: []string{"bar"}},
|
||||||
|
{dir: "/foo/bar/baz", files: []string{}},
|
||||||
|
{dir: "/baz/bar/foo", files: []string{"qux"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dir := range dirs {
|
||||||
|
assertDir(t, fs, dir.dir, dir.files)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.Rename("/baz/bar/foo", "/baz/bar/FOO"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertDir(t, fs, "/baz/bar", []string{"FOO"})
|
||||||
|
assertDir(t, fs, "/baz/bar/FOO", []string{"qux"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSRenameInsensitive(t *testing.T, fs Filesystem) {
|
||||||
|
if err := fs.MkdirAll("/baz/bar/foo", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.MkdirAll("/foO/baR/baZ", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := fs.Create("/BAZ/BAR/FOO/QUX")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
if err := fs.Rename("/Baz/bAr/foO/QuX", "/Foo/Bar/Baz/qUUx"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dirs = []struct {
|
||||||
|
dir string
|
||||||
|
files []string
|
||||||
|
}{
|
||||||
|
{dir: "/", files: []string{"foO", "baz"}},
|
||||||
|
{dir: "/foo", files: []string{"baR"}},
|
||||||
|
{dir: "/foo/bar/baz", files: []string{"qUUx"}},
|
||||||
|
{dir: "/baz/bar/foo", files: []string{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dir := range dirs {
|
||||||
|
assertDir(t, fs, dir.dir, dir.files)
|
||||||
|
}
|
||||||
|
|
||||||
|
// not checking on darwin due to https://github.com/golang/go/issues/35222
|
||||||
|
if runtime.GOOS != "darwin" {
|
||||||
|
if err := fs.Rename("/foo/bar/BAZ", "/FOO/BAR/bAz"); err != nil {
|
||||||
|
t.Errorf("Could not perform in-place case-only directory rename: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertDir(t, fs, "/foo/bar", []string{"bAz"})
|
||||||
|
assertDir(t, fs, "/fOO/bAr/baz", []string{"qUUx"})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.Rename("foo/bar/baz/quux", "foo/bar/BaZ/Quux"); err != nil {
|
||||||
|
t.Errorf("File rename failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertDir(t, fs, "/FOO/BAR/BAZ", []string{"Quux"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSMkdir(t *testing.T, fs Filesystem) {
|
||||||
|
if err := fs.Mkdir("/foo", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := fs.Stat("/foo"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.Mkdir("/foo", 0755); err == nil {
|
||||||
|
t.Errorf("got no error while creating existing directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSMkdirInsens(t *testing.T, fs Filesystem) {
|
||||||
|
if err := fs.Mkdir("/foo", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := fs.Stat("/Foo"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.Mkdir("/FOO", 0755); err == nil {
|
||||||
|
t.Errorf("got no error while creating existing directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSOpenFile(t *testing.T, fs Filesystem) {
|
||||||
|
fd, err := fs.OpenFile("foobar", os.O_RDONLY, 0664)
|
||||||
|
if err == nil {
|
||||||
|
fd.Close()
|
||||||
|
t.Fatalf("got no error opening a non-existing file")
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err = fs.OpenFile("foobar", os.O_RDWR|os.O_CREATE, 0664)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
fd, err = fs.OpenFile("foobar", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0664)
|
||||||
|
if err == nil {
|
||||||
|
fd.Close()
|
||||||
|
t.Fatalf("created an existing file while told not to")
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err = fs.OpenFile("foobar", os.O_RDWR|os.O_CREATE, 0664)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
fd, err = fs.OpenFile("foobar", os.O_RDWR, 0664)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSOpenFileInsens(t *testing.T, fs Filesystem) {
|
||||||
|
fd, err := fs.OpenFile("FooBar", os.O_RDONLY, 0664)
|
||||||
|
if err == nil {
|
||||||
|
fd.Close()
|
||||||
|
t.Fatalf("got no error opening a non-existing file")
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err = fs.OpenFile("fOObar", os.O_RDWR|os.O_CREATE, 0664)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
fd, err = fs.OpenFile("fOoBaR", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0664)
|
||||||
|
if err == nil {
|
||||||
|
fd.Close()
|
||||||
|
t.Fatalf("created an existing file while told not to")
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err = fs.OpenFile("FoObAr", os.O_RDWR|os.O_CREATE, 0664)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
fd, err = fs.OpenFile("FOOBAR", os.O_RDWR, 0664)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSRemoveAll(t *testing.T, fs Filesystem) {
|
||||||
|
if err := fs.Mkdir("/foo", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filenames := []string{"bar", "baz", "qux"}
|
||||||
|
for _, filename := range filenames {
|
||||||
|
fd, err := fs.Create("/foo/" + filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not create %s: %s", filename, err)
|
||||||
|
} else {
|
||||||
|
fd.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.RemoveAll("/foo"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := fs.Stat("/foo"); err == nil {
|
||||||
|
t.Errorf("this should be an error, as file doesn not exist anymore")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.RemoveAll("/foo/bar"); err != nil {
|
||||||
|
t.Errorf("real systems don't return error here")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSRemoveAllInsens(t *testing.T, fs Filesystem) {
|
||||||
|
if err := fs.Mkdir("/Foo", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filenames := []string{"bar", "baz", "qux"}
|
||||||
|
for _, filename := range filenames {
|
||||||
|
fd, err := fs.Create("/FOO/" + filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not create %s: %s", filename, err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.RemoveAll("/fOo"); err != nil {
|
||||||
|
t.Errorf("Could not remove dir: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := fs.Stat("/foo"); err == nil {
|
||||||
|
t.Errorf("this should be an error, as file doesn not exist anymore")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.RemoveAll("/foO/bAr"); err != nil {
|
||||||
|
t.Errorf("real systems don't return error here")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSRemove(t *testing.T, fs Filesystem) {
|
||||||
|
if err := fs.Mkdir("/Foo", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := fs.Create("/Foo/Bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
fd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.Remove("/Foo"); err == nil {
|
||||||
|
t.Errorf("not empty, should give error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.Remove("/Foo/Bar"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.Remove("/Foo"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSRemoveInsens(t *testing.T, fs Filesystem) {
|
||||||
|
if err := fs.Mkdir("/Foo", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := fs.Create("/Foo/Bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
if err := fs.Remove("/FOO"); err == nil {
|
||||||
|
t.Errorf("not empty, should give error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.Remove("/Foo/BaR"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fs.Remove("/FoO"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSSameFile(t *testing.T, fs Filesystem) {
|
||||||
|
if err := fs.Mkdir("/Foo", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filenames := []string{"Bar", "Baz", "/Foo/Bar"}
|
||||||
|
for _, filename := range filenames {
|
||||||
|
if fd, err := fs.Create(filename); err != nil {
|
||||||
|
t.Fatalf("Could not create %s: %s", filename, err)
|
||||||
|
} else {
|
||||||
|
fd.Close()
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
f1 string
|
||||||
|
f2 string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"Bar", "Baz", false},
|
||||||
|
{"Bar", "/Foo/Bar", false},
|
||||||
|
{"Bar", "Bar", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
assertSameFile(t, fs, test.f1, test.f2, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSSameFileInsens(t *testing.T, fs Filesystem) {
|
||||||
|
if err := fs.Mkdir("/Foo", 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filenames := []string{"Bar", "Baz"}
|
||||||
|
for _, filename := range filenames {
|
||||||
|
fd, err := fs.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Could not create %s: %s", filename, err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
f1 string
|
||||||
|
f2 string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"bAr", "baZ", false},
|
||||||
|
{"baz", "BAZ", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
assertSameFile(t, fs, test.f1, test.f2, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertSameFile(t *testing.T, fs Filesystem, f1, f2 string, want bool) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
fi1, err := fs.Stat(f1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fi2, err := fs.Stat(f2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := fs.SameFile(fi1, fi2)
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("for \"%s\" and \"%s\" want SameFile %v, got %v", f1, f2, want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFakeFSCreateInsens(t *testing.T, fs Filesystem) {
|
||||||
|
fd1, err := fs.Create("FOO")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd1.Close()
|
||||||
|
|
||||||
|
fd2, err := fs.Create("fOo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd2.Close()
|
||||||
|
|
||||||
|
if fd1.Name() != "FOO" {
|
||||||
|
t.Errorf("name of the file created as \"FOO\" is %s", fd1.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if fd2.Name() != "fOo" {
|
||||||
|
t.Errorf("name of created file \"fOo\" is %s", fd2.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// one would expect DirNames to show the last variant, but in fact it shows
|
||||||
|
// the original one
|
||||||
|
assertDir(t, fs, "/", []string{"FOO"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanup(fs Filesystem) error {
|
||||||
|
filenames, _ := fs.DirNames("/")
|
||||||
|
for _, filename := range filenames {
|
||||||
|
if filename != ".stfolder" {
|
||||||
|
if err := fs.RemoveAll(filename); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -38,6 +38,8 @@ func TestUnicodeLowercase(t *testing.T) {
|
|||||||
{"汉语/漢語 or 中文", "汉语/漢語 or 中文"},
|
{"汉语/漢語 or 中文", "汉语/漢語 or 中文"},
|
||||||
// Niether katakana as far as I can tell.
|
// Niether katakana as far as I can tell.
|
||||||
{"チャーハン", "チャーハン"},
|
{"チャーハン", "チャーハン"},
|
||||||
|
// Some special unicode characters, however, are folded by OSes
|
||||||
|
{"\u212A", "k"},
|
||||||
}
|
}
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
res := UnicodeLowercase(tc[0])
|
res := UnicodeLowercase(tc[0])
|
||||||
|
Loading…
Reference in New Issue
Block a user