github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/fileutil/io_test.go (about) 1 package fileutil_test 2 3 import ( 4 "io/fs" 5 "os" 6 "path/filepath" 7 "strings" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 "github.com/treeverse/lakefs/pkg/fileutil" 12 ) 13 14 func TestFindInParents(t *testing.T) { 15 root := t.TempDir() 16 dirTree := filepath.Join(root, "foo", "bar", "baz", "taz") 17 require.NoError(t, os.MkdirAll(dirTree, fileutil.DefaultDirectoryMask)) 18 t.Run("dir does not exist", func(t *testing.T) { 19 found, err := fileutil.FindInParents(filepath.Join(root, "no_dir"), "file") 20 require.ErrorIs(t, err, fs.ErrNotExist) 21 if found != "" { 22 t.Errorf("expected found to be empty, got %v", found) 23 } 24 }) 25 26 tests := []struct { 27 name string 28 deep string 29 filename string 30 filepath string 31 find bool 32 }{ 33 { 34 name: "find_at_leaf", 35 deep: filepath.Join(root, "foo", "bar", "baz"), 36 filename: "some_file0", 37 filepath: filepath.Join(root, "foo", "bar", "baz", "some_file0"), 38 find: true, 39 }, 40 { 41 name: "find_at_root", 42 deep: filepath.Join(root, "foo", "bar", "baz"), 43 filename: "some_file1", 44 filepath: filepath.Join(root, "some_file1"), 45 find: true, 46 }, 47 { 48 name: "find_at_subpath", 49 deep: filepath.Join(root, "foo", "bar", "baz", "taz"), 50 filename: "some_file2", 51 filepath: filepath.Join(root, "foo", "some_file2"), 52 find: true, 53 }, 54 { 55 name: "not_found_above", 56 deep: filepath.Join(root, "foo", "bar", "baz"), 57 filename: "some_file3", 58 filepath: filepath.Join(root, "foo", "bar", "baz", "taz", "some_file3"), 59 find: false, 60 }, 61 { 62 name: "doesnt_exist", 63 deep: filepath.Join(root, "foo", "bar", "baz"), 64 filename: ".doesnotexist21348329043289", 65 filepath: filepath.Join(root, "foo", "bar", "some_file4"), 66 find: false, 67 }, 68 } 69 70 for _, tt := range tests { 71 t.Run(tt.name, func(t *testing.T) { 72 f, err := os.Create(tt.filepath) 73 require.NoError(t, err) 74 require.NoError(t, f.Close()) 75 76 found, err := fileutil.FindInParents(tt.deep, tt.filename) 77 require.NoError(t, err) 78 if tt.find { 79 require.Equal(t, tt.filepath, found) 80 } else { 81 require.Equal(t, "", found) 82 } 83 }) 84 } 85 } 86 87 func TestPruneEmptyDirectories(t *testing.T) { 88 root := t.TempDir() 89 90 cases := []struct { 91 name string 92 paths []string 93 expected []string 94 }{ 95 { 96 name: "prune_deep", 97 paths: []string{ 98 "a/b/", 99 "a/b/c.txt", 100 "a/d/", 101 "a/e/a/b/", 102 }, 103 expected: []string{ 104 "a/d", 105 "a/e", 106 "a/e/a", 107 "a/e/a/b", 108 }, 109 }, 110 { 111 name: "prune_deep_keep_neighbor", 112 paths: []string{ 113 "a/b/", 114 "a/b/c.txt", 115 "a/d/", 116 "a/e/a/b/", 117 "b.txt", 118 "c/", 119 "c/b.txt", 120 "d/a/b/", 121 }, 122 expected: []string{ 123 "a/d", 124 "a/e", 125 "a/e/a", 126 "a/e/a/b", 127 "d", 128 "d/a", 129 "d/a/b", 130 }, 131 }, 132 { 133 name: "prune_keep_root", 134 paths: []string{}, 135 expected: []string{}, 136 }, 137 { 138 name: "prune_all", 139 paths: []string{ 140 "a/", 141 "a/b/", 142 "a/b/c/", 143 "a/d/", 144 "a/e/", 145 "a/e/a/", 146 "a/e/a/b/", 147 }, 148 expected: []string{ 149 "a", 150 "a/b", 151 "a/b/c", 152 "a/d", 153 "a/e", 154 "a/e/a", 155 "a/e/a/b", 156 }, 157 }, 158 { 159 name: "nothing_to_prune", 160 paths: []string{ 161 "a/b/", 162 "a/b/c.txt", 163 "d.txt", 164 }, 165 expected: []string{}, 166 }, 167 } 168 169 for _, tt := range cases { 170 t.Run(tt.name, func(t *testing.T) { 171 var files []string 172 173 currentRoot := filepath.Join(root, tt.name) 174 require.NoError(t, os.Mkdir(currentRoot, fileutil.DefaultDirectoryMask)) 175 176 // create directory tree 177 for _, entry := range tt.paths { 178 fullPath := filepath.Join(currentRoot, entry) 179 if strings.HasSuffix(entry, string(os.PathSeparator)) { 180 // create dir 181 require.NoError(t, os.MkdirAll(fullPath, fileutil.DefaultDirectoryMask)) 182 } else { 183 // create file 184 f, err := os.Create(fullPath) 185 require.NoError(t, err) 186 require.NoError(t, f.Close()) 187 files = append(files, f.Name()) 188 } 189 } 190 191 // prune 192 removedDirs, err := fileutil.PruneEmptyDirectories(currentRoot) 193 require.NoError(t, err) 194 195 // make relative 196 removedDirsRel := make([]string, len(removedDirs)) 197 for i, d := range removedDirs { 198 relPath, _ := filepath.Rel(currentRoot, d) 199 removedDirsRel[i] = relPath 200 } 201 202 // Verify root 203 _, err = fileutil.IsDir(currentRoot) 204 require.NoError(t, err) 205 206 // Compare pruned list 207 require.ElementsMatch(t, tt.expected, removedDirsRel) 208 209 // Verify files 210 for _, f := range files { 211 _, err = os.ReadFile(f) 212 require.NoError(t, err) 213 } 214 }) 215 } 216 }