forked from mirror/afero
Fix sorting in IOFS.ReadDir
We recently added a check for fs.ReadDirFile in IOFS.ReadDir, but forgot to apply a sort to the result as defined in the spec. This fixes that and adds a test case for it.
This commit is contained in:
parent
b0a534a781
commit
0aa65edf44
7
iofs.go
7
iofs.go
|
@ -76,7 +76,12 @@ func (iofs IOFS) ReadDir(name string) ([]fs.DirEntry, error) {
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
if rdf, ok := f.(fs.ReadDirFile); ok {
|
if rdf, ok := f.(fs.ReadDirFile); ok {
|
||||||
return rdf.ReadDir(-1)
|
items, err := rdf.ReadDir(-1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, iofs.wrapError("readdir", name, err)
|
||||||
|
}
|
||||||
|
sort.Slice(items, func(i, j int) bool { return items[i].Name() < items[j].Name() })
|
||||||
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
items, err := f.Readdir(-1)
|
items, err := f.Readdir(-1)
|
||||||
|
|
36
iofs_test.go
36
iofs_test.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -76,7 +77,17 @@ func TestIOFSNativeDirEntryWhenPossible(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 1; i <= 2; i++ {
|
const numFiles = 10
|
||||||
|
|
||||||
|
var fileNumbers []int
|
||||||
|
for i := 0; i < numFiles; i++ {
|
||||||
|
fileNumbers = append(fileNumbers, i)
|
||||||
|
}
|
||||||
|
rand.Shuffle(len(fileNumbers), func(i, j int) {
|
||||||
|
fileNumbers[i], fileNumbers[j] = fileNumbers[j], fileNumbers[i]
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, i := range fileNumbers {
|
||||||
f, err := osfs.Create(fmt.Sprintf("dir1/dir2/test%d.txt", i))
|
f, err := osfs.Create(fmt.Sprintf("dir1/dir2/test%d.txt", i))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -89,14 +100,17 @@ func TestIOFSNativeDirEntryWhenPossible(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertDirEntries := func(entries []fs.DirEntry) {
|
assertDirEntries := func(entries []fs.DirEntry, ordered bool) {
|
||||||
if len(entries) != 2 {
|
if len(entries) != numFiles {
|
||||||
t.Fatalf("expected 2, got %d", len(entries))
|
t.Fatalf("expected %d, got %d", numFiles, len(entries))
|
||||||
}
|
}
|
||||||
for _, entry := range entries {
|
for i, entry := range entries {
|
||||||
if _, ok := entry.(dirEntry); ok {
|
if _, ok := entry.(dirEntry); ok {
|
||||||
t.Fatal("DirEntry not native")
|
t.Fatal("DirEntry not native")
|
||||||
}
|
}
|
||||||
|
if ordered && entry.Name() != fmt.Sprintf("test%d.txt", i) {
|
||||||
|
t.Fatalf("expected %s, got %s", fmt.Sprintf("test%d.txt", i), entry.Name())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,10 +118,16 @@ func TestIOFSNativeDirEntryWhenPossible(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
assertDirEntries(dirEntries)
|
assertDirEntries(dirEntries, false)
|
||||||
|
|
||||||
iofs := NewIOFS(osfs)
|
iofs := NewIOFS(osfs)
|
||||||
|
|
||||||
|
dirEntries, err = iofs.ReadDir("dir1/dir2")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assertDirEntries(dirEntries, true)
|
||||||
|
|
||||||
fileCount := 0
|
fileCount := 0
|
||||||
err = fs.WalkDir(iofs, "", func(path string, d fs.DirEntry, err error) error {
|
err = fs.WalkDir(iofs, "", func(path string, d fs.DirEntry, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -130,8 +150,8 @@ func TestIOFSNativeDirEntryWhenPossible(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileCount != 2 {
|
if fileCount != numFiles {
|
||||||
t.Fatalf("expected 2, got %d", fileCount)
|
t.Fatalf("expected %d, got %d", numFiles, fileCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
10
ioutil.go
10
ioutil.go
|
@ -141,7 +141,7 @@ func WriteFile(fs Fs, filename string, data []byte, perm os.FileMode) error {
|
||||||
// We generate random temporary file names so that there's a good
|
// We generate random temporary file names so that there's a good
|
||||||
// chance the file doesn't exist yet - keeps the number of tries in
|
// chance the file doesn't exist yet - keeps the number of tries in
|
||||||
// TempFile to a minimum.
|
// TempFile to a minimum.
|
||||||
var rand uint32
|
var randNum uint32
|
||||||
var randmu sync.Mutex
|
var randmu sync.Mutex
|
||||||
|
|
||||||
func reseed() uint32 {
|
func reseed() uint32 {
|
||||||
|
@ -150,12 +150,12 @@ func reseed() uint32 {
|
||||||
|
|
||||||
func nextRandom() string {
|
func nextRandom() string {
|
||||||
randmu.Lock()
|
randmu.Lock()
|
||||||
r := rand
|
r := randNum
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
r = reseed()
|
r = reseed()
|
||||||
}
|
}
|
||||||
r = r*1664525 + 1013904223 // constants from Numerical Recipes
|
r = r*1664525 + 1013904223 // constants from Numerical Recipes
|
||||||
rand = r
|
randNum = r
|
||||||
randmu.Unlock()
|
randmu.Unlock()
|
||||||
return strconv.Itoa(int(1e9 + r%1e9))[1:]
|
return strconv.Itoa(int(1e9 + r%1e9))[1:]
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,7 @@ func TempFile(fs Fs, dir, pattern string) (f File, err error) {
|
||||||
if os.IsExist(err) {
|
if os.IsExist(err) {
|
||||||
if nconflict++; nconflict > 10 {
|
if nconflict++; nconflict > 10 {
|
||||||
randmu.Lock()
|
randmu.Lock()
|
||||||
rand = reseed()
|
randNum = reseed()
|
||||||
randmu.Unlock()
|
randmu.Unlock()
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
@ -226,7 +226,7 @@ func TempDir(fs Fs, dir, prefix string) (name string, err error) {
|
||||||
if os.IsExist(err) {
|
if os.IsExist(err) {
|
||||||
if nconflict++; nconflict > 10 {
|
if nconflict++; nconflict > 10 {
|
||||||
randmu.Lock()
|
randmu.Lock()
|
||||||
rand = reseed()
|
randNum = reseed()
|
||||||
randmu.Unlock()
|
randmu.Unlock()
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
|
Loading…
Reference in New Issue