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 }