Files
wehub-resource-sync 5357c39144
Fuzzer / Run Fuzzer (push) Has been cancelled
Race tests / Go race tests (ubuntu-22.04) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:01:40 +08:00

374 lines
11 KiB
Go

// Copyright 2019 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package filesys
import (
"os"
"path/filepath"
"reflect"
"sort"
"testing"
"github.com/stretchr/testify/require"
"github.com/dolthub/dolt/go/libraries/utils/osutil"
"github.com/dolthub/dolt/go/libraries/utils/test"
)
const (
testFilename = "testfile.txt"
testSubdirFilename = "anothertest.txt"
movedFilename = "movedfile.txt"
testString = "this is a test"
testStringLen = int64(len(testString))
)
var filesysetmsToTest = map[string]Filesys{
"inmem": EmptyInMemFS("/"),
"local": LocalFS,
}
func TestFilesystems(t *testing.T) {
dir := test.TestDir("filesys_test")
newLocation := test.TestDir("newLocation")
subdir := filepath.Join(dir, "subdir")
subdirFile := filepath.Join(subdir, testSubdirFilename)
fp := filepath.Join(dir, testFilename)
movedFilePath := filepath.Join(dir, movedFilename)
for fsName, fs := range filesysetmsToTest {
t.Run(fsName, func(t *testing.T) {
// Test file doesn't exist before creation
exists, _ := fs.Exists(dir)
require.False(t, exists)
// Test creating directories
err := fs.MkDirs(dir)
require.NoError(t, err)
err = fs.MkDirs(subdir)
require.NoError(t, err)
// Test directories exists, and are in fact directories
exists, isDir := fs.Exists(dir)
require.True(t, exists)
require.True(t, isDir)
exists, isDir = fs.Exists(subdir)
require.True(t, exists)
require.True(t, isDir)
// Test failure to open a directory for read
_, err = fs.OpenForRead(dir)
require.Error(t, err)
// Test failure to open a directory for write
_, err = fs.OpenForWrite(dir, os.ModePerm)
require.Error(t, err)
// Test file doesn't exist before creation
exists, _ = fs.Exists(fp)
require.False(t, exists)
// Test can't open a file that doesn't exist for read
_, err = fs.OpenForRead(fp)
require.Error(t, err)
data := test.RandomData(256 * 1024)
// Test writing file with random data
err = fs.WriteFile(fp, data, os.ModePerm)
require.NoError(t, err)
// Test that the data can be read back and hasn't changed
dataRead, err := fs.ReadFile(fp)
require.NoError(t, err)
require.Equal(t, dataRead, data)
// Test moving the file
err = fs.MoveFile(fp, movedFilePath)
require.NoError(t, err)
// Test that there is no longer a file at the initial path
exists, _ = fs.Exists(fp)
require.False(t, exists)
// Test that a file exists at the new location
exists, isDir = fs.Exists(movedFilePath)
require.True(t, exists)
require.False(t, isDir)
// Test that the data can be read back and hasn't changed since being moved
dataRead, err = fs.ReadFile(movedFilePath)
require.NoError(t, err)
require.Equal(t, dataRead, data)
tmp := fs.TempDir()
require.NotEmpty(t, tmp)
fp2 := filepath.Join(tmp, "data.txt")
wrc, err := fs.OpenForWrite(fp2, os.ModePerm)
require.NoError(t, err)
require.NoError(t, wrc.Close())
// Test moving a directory
err = fs.WriteFile(subdirFile, []byte("helloworld"), os.ModePerm)
require.NoError(t, err)
err = fs.MkDirs(newLocation)
require.NoError(t, err)
err = fs.MoveDir(subdir, filepath.Join(newLocation, "subdir"))
require.NoError(t, err)
// Assert that nothing exists at the old path
exists, isDir = fs.Exists(subdir)
require.False(t, exists)
require.False(t, isDir)
// Assert that our directory exists at the new path
exists, isDir = fs.Exists(filepath.Join(newLocation, "subdir"))
require.True(t, exists)
require.True(t, isDir)
// Assert that the file in the sub-directory has been moved, too
exists, isDir = fs.Exists(subdirFile)
require.False(t, exists)
require.False(t, isDir)
exists, isDir = fs.Exists(filepath.Join(newLocation, "subdir", testSubdirFilename))
require.True(t, exists)
require.False(t, isDir)
// Test writing/reading random data to tmp file
err = fs.WriteFile(fp2, data, os.ModePerm)
require.NoError(t, err)
dataRead, err = fs.ReadFile(fp2)
require.NoError(t, err)
require.Equal(t, dataRead, data)
})
}
}
func TestNewInMemFS(t *testing.T) {
fs := NewInMemFS([]string{"/r1/c1", "r2/c1/gc1"}, map[string][]byte{
"/r1/c1/file1.txt": []byte(testString),
"/r3/file2.txt": []byte(testString),
}, "/")
expectedDirs := []string{
osutil.PathToNative("/r1"),
osutil.PathToNative("/r1/c1"),
osutil.PathToNative("/r2"),
osutil.PathToNative("/r2/c1"),
osutil.PathToNative("/r2/c1/gc1"),
osutil.PathToNative("/r3"),
}
expectedFiles := []string{
osutil.PathToNative("/r1/c1/file1.txt"),
osutil.PathToNative("/r3/file2.txt"),
}
actualDirs, actualFiles, err := iterate(fs, "/", true, t)
if err != nil {
t.Error("Error iterating")
}
validate(expectedDirs, expectedFiles, actualDirs, actualFiles, "inmem", t)
}
func TestRecursiveFSIteration(t *testing.T) {
dir := test.TestDir("TestRecursiveFSIteration")
for fsName, fs := range filesysetmsToTest {
var expectedDirs []string
var expectedFiles []string
expectedDirs = makeDirsAddExpected(expectedDirs, fs, dir, "child1")
expectedDirs = makeDirsAddExpected(expectedDirs, fs, dir, "child2", "grandchild1")
expectedDirs = makeDirsAddExpected(expectedDirs, fs, dir, "child3", "grandchild2")
expectedDirs = makeDirsAddExpected(expectedDirs, fs, filepath.Join(dir, "child3"), "grandchild3")
expectedFiles = writeFileAddToExp(expectedFiles, fs, dir, "child1", "File1.txt")
expectedFiles = writeFileAddToExp(expectedFiles, fs, dir, "child2", "grandchild1", "File1.txt")
expectedFiles = writeFileAddToExp(expectedFiles, fs, dir, "child3", "grandchild2", "File1.txt")
expectedFiles = writeFileAddToExp(expectedFiles, fs, dir, "child3", "grandchild2", "File2.txt")
expectedFiles = writeFileAddToExp(expectedFiles, fs, dir, "child3", "grandchild3", "File1.txt")
actualDirs, actualFiles, err := iterate(fs, dir, true, t)
if err != nil {
t.Error("fs:", fsName, "Failed to iterate.", err.Error())
continue
}
validate(expectedDirs, expectedFiles, actualDirs, actualFiles, fsName, t)
}
}
func TestFSIteration(t *testing.T) {
dir := test.TestDir("TestFSIteration")
for fsName, fs := range filesysetmsToTest {
var expectedDirs []string
var expectedFiles []string
var ignored []string
makeDirsAddExpected(ignored, fs, dir, "child1")
makeDirsAddExpected(ignored, fs, dir, "child2", "grandchild1")
makeDirsAddExpected(ignored, fs, dir, "child3")
child3path := filepath.Join(dir, "child3")
expectedDirs = makeDirsAddExpected(expectedDirs, fs, child3path, "grandchild2")
expectedDirs = makeDirsAddExpected(expectedDirs, fs, child3path, "grandchild3")
expectedFiles = writeFileAddToExp(expectedFiles, fs, child3path, "File1.txt")
writeFileAddToExp(ignored, fs, dir, "child1", "File1.txt")
writeFileAddToExp(ignored, fs, dir, "child2", "grandchild1", "File1.txt")
writeFileAddToExp(ignored, fs, dir, "child3", "grandchild2", "File1.txt")
writeFileAddToExp(ignored, fs, dir, "child3", "grandchild2", "File2.txt")
writeFileAddToExp(ignored, fs, dir, "child3", "grandchild3", "File1.txt")
actualDirs, actualFiles, err := iterate(fs, filepath.Join(dir, "child3"), false, t)
if err != nil {
t.Error("fs:", fsName, "Failed to iterate.", err.Error())
continue
}
validate(expectedDirs, expectedFiles, actualDirs, actualFiles, fsName, t)
}
}
func TestDeletes(t *testing.T) {
dir := test.TestDir("TestDeletes")
for fsName, fs := range filesysetmsToTest {
var ignored []string
makeDirsAddExpected(ignored, fs, dir, "child1")
makeDirsAddExpected(ignored, fs, dir, "child2", "grandchild1")
makeDirsAddExpected(ignored, fs, dir, "child3")
writeFileAddToExp(ignored, fs, dir, "child1", "File1.txt")
writeFileAddToExp(ignored, fs, dir, "child2", "grandchild1", "File1.txt")
writeFileAddToExp(ignored, fs, dir, "child3", "grandchild2", "File1.txt")
writeFileAddToExp(ignored, fs, dir, "child3", "grandchild2", "File2.txt")
writeFileAddToExp(ignored, fs, dir, "child3", "grandchild3", "File1.txt")
var err error
err = fs.Delete(filepath.Join(dir, "child1"), false)
if err == nil {
t.Error("fs:", fsName, "Should have failed to delete non empty directory without force flag")
}
err = fs.DeleteFile(filepath.Join(dir, "child1", "File1.txt"))
if err != nil {
t.Error("fs:", fsName, "Should have succeeded to delete file")
}
err = fs.DeleteFile(filepath.Join(dir, "child1"))
if err == nil {
t.Error("fs:", fsName, "DeleteFile should not delete directories")
}
err = fs.Delete(filepath.Join(dir, "child1"), false)
if err != nil {
t.Error("fs:", fsName, "Should have succeeded to delete empty directory")
}
err = fs.Delete(filepath.Join(dir, "child2"), true)
if err != nil {
t.Error("fs:", fsName, "Should have succeeded to force delete directory and it")
}
}
}
func makeDirsAddExpected(expected []string, fs Filesys, root string, descendants ...string) []string {
currDir := root
for _, descendant := range descendants {
currDir = filepath.Join(currDir, descendant)
expected = append(expected, currDir)
}
err := fs.MkDirs(currDir)
if err != nil {
panic("failed to make dir")
}
return expected
}
func writeFileAddToExp(expected []string, fs Filesys, root string, pathFromRoot ...string) []string {
pathElements := append([]string{root}, pathFromRoot...)
fp := filepath.Join(pathElements...)
fs.WriteFile(fp, []byte(testString), os.ModePerm)
return append(expected, fp)
}
func iterate(fs Filesys, dir string, recursive bool, t *testing.T) ([]string, []string, error) {
actualDirs := make([]string, 0, 10)
actualFiles := make([]string, 0, 10)
err := fs.Iter(dir, recursive, func(path string, size int64, isDir bool) (stop bool) {
if isDir {
actualDirs = append(actualDirs, path)
} else {
actualFiles = append(actualFiles, path)
if size != testStringLen {
t.Error(path, "is not of the expected size.")
}
}
return false
})
return actualDirs, actualFiles, err
}
func validate(expectedDirs, expectedFiles, actualDirs, actualFiles []string, fsName string, t *testing.T) {
sort.Strings(expectedDirs)
sort.Strings(expectedFiles)
sort.Strings(actualDirs)
sort.Strings(actualFiles)
if !reflect.DeepEqual(expectedDirs, actualDirs) {
t.Error("fs:", fsName, "Expected dirs does not match actual dirs.", "\n\tactual :", actualDirs, "\n\texpected:", expectedDirs)
}
if !reflect.DeepEqual(expectedFiles, actualFiles) {
t.Error("fs:", fsName, "Expected files does not match actual files.", "\n\tactual :", actualFiles, "\n\texpected:", expectedFiles)
}
}
func TestDeleteFileDurably(t *testing.T) {
fs, err := LocalFilesysWithWorkingDir(t.TempDir())
require.NoError(t, err)
require.NoError(t, fs.WriteFile("marker", nil, 0o644))
exists, _ := fs.Exists("marker")
require.True(t, exists)
require.NoError(t, fs.DeleteFileDurably("marker"))
exists, _ = fs.Exists("marker")
require.False(t, exists, "marker should be deleted")
require.ErrorIs(t, fs.DeleteFileDurably("marker"), os.ErrNotExist)
}