github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/utils/filesys/fs_test.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package filesys
    16  
    17  import (
    18  	"os"
    19  	"path/filepath"
    20  	"reflect"
    21  	"sort"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/require"
    25  
    26  	"github.com/dolthub/dolt/go/libraries/utils/osutil"
    27  	"github.com/dolthub/dolt/go/libraries/utils/test"
    28  )
    29  
    30  const (
    31  	testFilename       = "testfile.txt"
    32  	testSubdirFilename = "anothertest.txt"
    33  	movedFilename      = "movedfile.txt"
    34  	testString         = "this is a test"
    35  	testStringLen      = int64(len(testString))
    36  )
    37  
    38  var filesysetmsToTest = map[string]Filesys{
    39  	"inmem": EmptyInMemFS("/"),
    40  	"local": LocalFS,
    41  }
    42  
    43  func TestFilesystems(t *testing.T) {
    44  	dir := test.TestDir("filesys_test")
    45  	newLocation := test.TestDir("newLocation")
    46  	subdir := filepath.Join(dir, "subdir")
    47  	subdirFile := filepath.Join(subdir, testSubdirFilename)
    48  	fp := filepath.Join(dir, testFilename)
    49  	movedFilePath := filepath.Join(dir, movedFilename)
    50  
    51  	for fsName, fs := range filesysetmsToTest {
    52  		t.Run(fsName, func(t *testing.T) {
    53  			// Test file doesn't exist before creation
    54  			exists, _ := fs.Exists(dir)
    55  			require.False(t, exists)
    56  
    57  			// Test creating directories
    58  			err := fs.MkDirs(dir)
    59  			require.NoError(t, err)
    60  			err = fs.MkDirs(subdir)
    61  			require.NoError(t, err)
    62  
    63  			// Test directories exists, and are in fact directories
    64  			exists, isDir := fs.Exists(dir)
    65  			require.True(t, exists)
    66  			require.True(t, isDir)
    67  			exists, isDir = fs.Exists(subdir)
    68  			require.True(t, exists)
    69  			require.True(t, isDir)
    70  
    71  			// Test failure to open a directory for read
    72  			_, err = fs.OpenForRead(dir)
    73  			require.Error(t, err)
    74  
    75  			// Test failure to open a directory for write
    76  			_, err = fs.OpenForWrite(dir, os.ModePerm)
    77  			require.Error(t, err)
    78  
    79  			// Test file doesn't exist before creation
    80  			exists, _ = fs.Exists(fp)
    81  			require.False(t, exists)
    82  
    83  			// Test can't open a file that doesn't exist for read
    84  			_, err = fs.OpenForRead(fp)
    85  			require.Error(t, err)
    86  
    87  			data := test.RandomData(256 * 1024)
    88  
    89  			// Test writing file with random data
    90  			err = fs.WriteFile(fp, data, os.ModePerm)
    91  			require.NoError(t, err)
    92  
    93  			// Test that the data can be read back and hasn't changed
    94  			dataRead, err := fs.ReadFile(fp)
    95  			require.NoError(t, err)
    96  			require.Equal(t, dataRead, data)
    97  
    98  			// Test moving the file
    99  			err = fs.MoveFile(fp, movedFilePath)
   100  			require.NoError(t, err)
   101  
   102  			// Test that there is no longer a file at the initial path
   103  			exists, _ = fs.Exists(fp)
   104  			require.False(t, exists)
   105  
   106  			// Test that a file exists at the new location
   107  			exists, isDir = fs.Exists(movedFilePath)
   108  			require.True(t, exists)
   109  			require.False(t, isDir)
   110  
   111  			// Test that the data can be read back and hasn't changed since being moved
   112  			dataRead, err = fs.ReadFile(movedFilePath)
   113  			require.NoError(t, err)
   114  			require.Equal(t, dataRead, data)
   115  
   116  			tmp := fs.TempDir()
   117  			require.NotEmpty(t, tmp)
   118  			fp2 := filepath.Join(tmp, "data.txt")
   119  			wrc, err := fs.OpenForWrite(fp2, os.ModePerm)
   120  			require.NoError(t, err)
   121  			require.NoError(t, wrc.Close())
   122  
   123  			// Test moving a directory
   124  			err = fs.WriteFile(subdirFile, []byte("helloworld"), os.ModePerm)
   125  			require.NoError(t, err)
   126  			err = fs.MkDirs(newLocation)
   127  			require.NoError(t, err)
   128  			err = fs.MoveDir(subdir, filepath.Join(newLocation, "subdir"))
   129  			require.NoError(t, err)
   130  
   131  			// Assert that nothing exists at the old path
   132  			exists, isDir = fs.Exists(subdir)
   133  			require.False(t, exists)
   134  			require.False(t, isDir)
   135  
   136  			// Assert that our directory exists at the new path
   137  			exists, isDir = fs.Exists(filepath.Join(newLocation, "subdir"))
   138  			require.True(t, exists)
   139  			require.True(t, isDir)
   140  
   141  			// Assert that the file in the sub-directory has been moved, too
   142  			exists, isDir = fs.Exists(subdirFile)
   143  			require.False(t, exists)
   144  			require.False(t, isDir)
   145  			exists, isDir = fs.Exists(filepath.Join(newLocation, "subdir", testSubdirFilename))
   146  			require.True(t, exists)
   147  			require.False(t, isDir)
   148  
   149  			// Test writing/reading random data to tmp file
   150  			err = fs.WriteFile(fp2, data, os.ModePerm)
   151  			require.NoError(t, err)
   152  			dataRead, err = fs.ReadFile(fp2)
   153  			require.NoError(t, err)
   154  			require.Equal(t, dataRead, data)
   155  		})
   156  	}
   157  }
   158  
   159  func TestNewInMemFS(t *testing.T) {
   160  	fs := NewInMemFS([]string{"/r1/c1", "r2/c1/gc1"}, map[string][]byte{
   161  		"/r1/c1/file1.txt": []byte(testString),
   162  		"/r3/file2.txt":    []byte(testString),
   163  	}, "/")
   164  
   165  	expectedDirs := []string{
   166  		osutil.PathToNative("/r1"),
   167  		osutil.PathToNative("/r1/c1"),
   168  		osutil.PathToNative("/r2"),
   169  		osutil.PathToNative("/r2/c1"),
   170  		osutil.PathToNative("/r2/c1/gc1"),
   171  		osutil.PathToNative("/r3"),
   172  	}
   173  
   174  	expectedFiles := []string{
   175  		osutil.PathToNative("/r1/c1/file1.txt"),
   176  		osutil.PathToNative("/r3/file2.txt"),
   177  	}
   178  
   179  	actualDirs, actualFiles, err := iterate(fs, "/", true, t)
   180  
   181  	if err != nil {
   182  		t.Error("Error iterating")
   183  	}
   184  
   185  	validate(expectedDirs, expectedFiles, actualDirs, actualFiles, "inmem", t)
   186  }
   187  
   188  func TestRecursiveFSIteration(t *testing.T) {
   189  	dir := test.TestDir("TestRecursiveFSIteration")
   190  
   191  	for fsName, fs := range filesysetmsToTest {
   192  		var expectedDirs []string
   193  		var expectedFiles []string
   194  
   195  		expectedDirs = makeDirsAddExpected(expectedDirs, fs, dir, "child1")
   196  		expectedDirs = makeDirsAddExpected(expectedDirs, fs, dir, "child2", "grandchild1")
   197  		expectedDirs = makeDirsAddExpected(expectedDirs, fs, dir, "child3", "grandchild2")
   198  		expectedDirs = makeDirsAddExpected(expectedDirs, fs, filepath.Join(dir, "child3"), "grandchild3")
   199  
   200  		expectedFiles = writeFileAddToExp(expectedFiles, fs, dir, "child1", "File1.txt")
   201  		expectedFiles = writeFileAddToExp(expectedFiles, fs, dir, "child2", "grandchild1", "File1.txt")
   202  		expectedFiles = writeFileAddToExp(expectedFiles, fs, dir, "child3", "grandchild2", "File1.txt")
   203  		expectedFiles = writeFileAddToExp(expectedFiles, fs, dir, "child3", "grandchild2", "File2.txt")
   204  		expectedFiles = writeFileAddToExp(expectedFiles, fs, dir, "child3", "grandchild3", "File1.txt")
   205  
   206  		actualDirs, actualFiles, err := iterate(fs, dir, true, t)
   207  
   208  		if err != nil {
   209  			t.Error("fs:", fsName, "Failed to iterate.", err.Error())
   210  			continue
   211  		}
   212  
   213  		validate(expectedDirs, expectedFiles, actualDirs, actualFiles, fsName, t)
   214  	}
   215  }
   216  
   217  func TestFSIteration(t *testing.T) {
   218  	dir := test.TestDir("TestFSIteration")
   219  
   220  	for fsName, fs := range filesysetmsToTest {
   221  		var expectedDirs []string
   222  		var expectedFiles []string
   223  		var ignored []string
   224  
   225  		makeDirsAddExpected(ignored, fs, dir, "child1")
   226  		makeDirsAddExpected(ignored, fs, dir, "child2", "grandchild1")
   227  		makeDirsAddExpected(ignored, fs, dir, "child3")
   228  
   229  		child3path := filepath.Join(dir, "child3")
   230  		expectedDirs = makeDirsAddExpected(expectedDirs, fs, child3path, "grandchild2")
   231  		expectedDirs = makeDirsAddExpected(expectedDirs, fs, child3path, "grandchild3")
   232  		expectedFiles = writeFileAddToExp(expectedFiles, fs, child3path, "File1.txt")
   233  
   234  		writeFileAddToExp(ignored, fs, dir, "child1", "File1.txt")
   235  		writeFileAddToExp(ignored, fs, dir, "child2", "grandchild1", "File1.txt")
   236  		writeFileAddToExp(ignored, fs, dir, "child3", "grandchild2", "File1.txt")
   237  		writeFileAddToExp(ignored, fs, dir, "child3", "grandchild2", "File2.txt")
   238  		writeFileAddToExp(ignored, fs, dir, "child3", "grandchild3", "File1.txt")
   239  
   240  		actualDirs, actualFiles, err := iterate(fs, filepath.Join(dir, "child3"), false, t)
   241  
   242  		if err != nil {
   243  			t.Error("fs:", fsName, "Failed to iterate.", err.Error())
   244  			continue
   245  		}
   246  
   247  		validate(expectedDirs, expectedFiles, actualDirs, actualFiles, fsName, t)
   248  	}
   249  }
   250  
   251  func TestDeletes(t *testing.T) {
   252  	dir := test.TestDir("TestDeletes")
   253  
   254  	for fsName, fs := range filesysetmsToTest {
   255  		var ignored []string
   256  
   257  		makeDirsAddExpected(ignored, fs, dir, "child1")
   258  		makeDirsAddExpected(ignored, fs, dir, "child2", "grandchild1")
   259  		makeDirsAddExpected(ignored, fs, dir, "child3")
   260  
   261  		writeFileAddToExp(ignored, fs, dir, "child1", "File1.txt")
   262  		writeFileAddToExp(ignored, fs, dir, "child2", "grandchild1", "File1.txt")
   263  		writeFileAddToExp(ignored, fs, dir, "child3", "grandchild2", "File1.txt")
   264  		writeFileAddToExp(ignored, fs, dir, "child3", "grandchild2", "File2.txt")
   265  		writeFileAddToExp(ignored, fs, dir, "child3", "grandchild3", "File1.txt")
   266  
   267  		var err error
   268  		err = fs.Delete(filepath.Join(dir, "child1"), false)
   269  
   270  		if err == nil {
   271  			t.Error("fs:", fsName, "Should have failed to delete non empty directory without force flag")
   272  		}
   273  
   274  		err = fs.DeleteFile(filepath.Join(dir, "child1", "File1.txt"))
   275  
   276  		if err != nil {
   277  			t.Error("fs:", fsName, "Should have succeeded to delete file")
   278  		}
   279  
   280  		err = fs.DeleteFile(filepath.Join(dir, "child1"))
   281  
   282  		if err == nil {
   283  			t.Error("fs:", fsName, "DeleteFile should not delete directories")
   284  		}
   285  
   286  		err = fs.Delete(filepath.Join(dir, "child1"), false)
   287  
   288  		if err != nil {
   289  			t.Error("fs:", fsName, "Should have succeeded to delete empty directory")
   290  		}
   291  
   292  		err = fs.Delete(filepath.Join(dir, "child2"), true)
   293  
   294  		if err != nil {
   295  			t.Error("fs:", fsName, "Should have succeeded to force delete directory and it")
   296  		}
   297  	}
   298  
   299  }
   300  
   301  func makeDirsAddExpected(expected []string, fs Filesys, root string, descendants ...string) []string {
   302  	currDir := root
   303  	for _, descendant := range descendants {
   304  		currDir = filepath.Join(currDir, descendant)
   305  		expected = append(expected, currDir)
   306  	}
   307  
   308  	err := fs.MkDirs(currDir)
   309  
   310  	if err != nil {
   311  		panic("failed to make dir")
   312  	}
   313  
   314  	return expected
   315  }
   316  
   317  func writeFileAddToExp(expected []string, fs Filesys, root string, pathFromRoot ...string) []string {
   318  	pathElements := append([]string{root}, pathFromRoot...)
   319  
   320  	fp := filepath.Join(pathElements...)
   321  	fs.WriteFile(fp, []byte(testString), os.ModePerm)
   322  	return append(expected, fp)
   323  }
   324  
   325  func iterate(fs Filesys, dir string, recursive bool, t *testing.T) ([]string, []string, error) {
   326  	actualDirs := make([]string, 0, 10)
   327  	actualFiles := make([]string, 0, 10)
   328  	err := fs.Iter(dir, recursive, func(path string, size int64, isDir bool) (stop bool) {
   329  		if isDir {
   330  			actualDirs = append(actualDirs, path)
   331  		} else {
   332  			actualFiles = append(actualFiles, path)
   333  
   334  			if size != testStringLen {
   335  				t.Error(path, "is not of the expected size.")
   336  			}
   337  		}
   338  
   339  		return false
   340  	})
   341  
   342  	return actualDirs, actualFiles, err
   343  }
   344  
   345  func validate(expectedDirs, expectedFiles, actualDirs, actualFiles []string, fsName string, t *testing.T) {
   346  	sort.Strings(expectedDirs)
   347  	sort.Strings(expectedFiles)
   348  	sort.Strings(actualDirs)
   349  	sort.Strings(actualFiles)
   350  
   351  	if !reflect.DeepEqual(expectedDirs, actualDirs) {
   352  		t.Error("fs:", fsName, "Expected dirs does not match actual dirs.", "\n\tactual  :", actualDirs, "\n\texpected:", expectedDirs)
   353  	}
   354  
   355  	if !reflect.DeepEqual(expectedFiles, actualFiles) {
   356  		t.Error("fs:", fsName, "Expected files does not match actual files.", "\n\tactual  :", actualFiles, "\n\texpected:", expectedFiles)
   357  	}
   358  }