github.com/haraldrudell/parl@v0.4.176/pfs/traverser_test.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package pfs 7 8 import ( 9 "errors" 10 "io/fs" 11 "os" 12 "path/filepath" 13 "testing" 14 15 "github.com/haraldrudell/parl/perrors" 16 ) 17 18 // tests traversing single entry 19 func TestTraverser(t *testing.T) { 20 var tempDir = t.TempDir() 21 // path is unclean providedPath 22 var path = filepath.Join(tempDir, ".") 23 var expAbs = func() (abs string) { 24 var e error 25 if abs, e = filepath.Abs(tempDir); e != nil { 26 panic(e) 27 } else if abs, e = filepath.EvalSymlinks(abs); e != nil { 28 panic(e) 29 } 30 return 31 }() 32 var expNo1 = uint64(1) 33 var expBase = filepath.Base(tempDir) 34 35 var result ResultEntry 36 37 var traverser = NewTraverser(path) 38 39 // first Next should be dir 40 result = traverser.Next() 41 if result.ProvidedPath != path { 42 t.Errorf("Next path %q exp %q", result.ProvidedPath, path) 43 } 44 if result.Abs != expAbs { 45 t.Errorf("Next Abs %q exp %q", result.Abs, expAbs) 46 } 47 if result.No != expNo1 { 48 t.Errorf("Next No %d exp %d", result.No, expNo1) 49 } 50 if result.Err != nil { 51 t.Errorf("Next Err: %s", perrors.Short(result.Err)) 52 } 53 if result.SkipEntry == nil { 54 t.Error("Next SkipEntry nil") 55 } 56 if n := result.Name(); n != expBase { 57 t.Errorf("Next Name %q exp %q", n, expBase) 58 } 59 60 // NextNext should be end 61 result = traverser.Next() 62 if !result.IsEnd() { 63 t.Error("NextNext not end") 64 } 65 } 66 67 // tests traversing directory 68 func TestTraverserDir(t *testing.T) { 69 var tempDir = t.TempDir() 70 var fileBase = "file" 71 var filename = filepath.Join(tempDir, fileBase) 72 if e := os.WriteFile(filename, nil, 0700); e != nil { 73 panic(e) 74 } 75 76 var result ResultEntry 77 78 var traverser = NewTraverser(tempDir) 79 80 // Next should be tempDir 81 result = traverser.Next() 82 if result.Err != nil { 83 t.Errorf("Next err: %s", result.Err) 84 } 85 if result.ProvidedPath != tempDir { 86 t.Errorf("Next ProvidedPath %q exp %q", result.ProvidedPath, tempDir) 87 } 88 89 // NextNext should be filename 90 result = traverser.Next() 91 if result.Err != nil { 92 t.Errorf("NextNext err: %s", result.Err) 93 } 94 if result.ProvidedPath != filename { 95 t.Errorf("NextNext ProvidedPath %q exp %q", result.ProvidedPath, filename) 96 } 97 98 // NextNextNext should be end 99 result = traverser.Next() 100 if result.Err != nil { 101 t.Errorf("NextNextNext err: %s", result.Err) 102 } 103 if !result.IsEnd() { 104 t.Error("NextNextNext not end") 105 } 106 } 107 108 // tests returning an error 109 func TestTraverserError(t *testing.T) { 110 var path = "%" 111 112 var result ResultEntry 113 114 var traverser = NewTraverser(path) 115 116 // first Next should be err 117 result = traverser.Next() 118 if result.Err == nil { 119 t.Error("Next Err nil") 120 } 121 } 122 123 // tests skip of entry 124 func TestTraverserSkip(t *testing.T) { 125 var tempDir = t.TempDir() 126 var fileBase = "file" 127 var filename = filepath.Join(tempDir, fileBase) 128 if e := os.WriteFile(filename, nil, 0700); e != nil { 129 panic(e) 130 } 131 132 var result ResultEntry 133 134 var traverser = NewTraverser(tempDir) 135 136 // Next should be tempDir 137 result = traverser.Next() 138 if result.Err != nil { 139 t.Errorf("Next err: %s", result.Err) 140 } 141 if result.ProvidedPath != tempDir { 142 t.Errorf("Next ProvidedPath %q exp %q", result.ProvidedPath, tempDir) 143 } 144 if result.SkipEntry == nil { 145 t.Fatalf("Next result.SkipEntry nil") 146 } 147 result.Skip() 148 149 // NextNext should be end 150 result = traverser.Next() 151 if result.Err != nil { 152 t.Errorf("NextNext err: %s", result.Err) 153 } 154 if !result.IsEnd() { 155 t.Error("NextNext not end") 156 } 157 } 158 159 // tests that in-root symlinks are ignored 160 func TestTraverserInRootSymlink(t *testing.T) { 161 var tempDir = t.TempDir() 162 var symlink = filepath.Join(tempDir, "symlink") 163 if e := os.Symlink(tempDir, symlink); e != nil { 164 panic(e) 165 } 166 var cleanTempDir = func() (abs string) { 167 var err error 168 if abs, err = filepath.Abs(tempDir); err != nil { 169 panic(err) 170 } else if abs, err = filepath.EvalSymlinks(abs); err != nil { 171 panic(err) 172 } 173 return 174 }() 175 176 var result ResultEntry 177 178 var traverser = NewTraverser(tempDir) 179 180 // Next should be tempDir 181 result = traverser.Next() 182 if result.Err != nil { 183 t.Errorf("Next err: %s", result.Err) 184 } 185 if result.ProvidedPath != tempDir { 186 t.Errorf("Next ProvidedPath %q exp %q", result.ProvidedPath, tempDir) 187 } 188 if result.SkipEntry == nil { 189 t.Fatalf("Next result.SkipEntry nil") 190 } 191 192 // NextNext should be symlink 193 result = traverser.Next() 194 if result.Err != nil { 195 t.Errorf("NextNext err: %s", result.Err) 196 } 197 if result.ProvidedPath != symlink { 198 t.Errorf("NextNext ProvidedPath %q exp %q", result.ProvidedPath, symlink) 199 } 200 if result.Abs != cleanTempDir { 201 t.Errorf("NextNext Abs %q exp %q", result.Abs, cleanTempDir) 202 } 203 204 // NextNextNext should be end 205 result = traverser.Next() 206 if result.Err != nil { 207 t.Errorf("NextNextNext err: %s", result.Err) 208 } 209 if !result.IsEnd() { 210 t.Errorf("NextNextNext not end path: %q", result.ProvidedPath) 211 } 212 } 213 214 // tests that disparate symlinks are followed 215 func TestTraverserDisparateSymlink(t *testing.T) { 216 var tempDir = t.TempDir() 217 var tempDir2 = t.TempDir() 218 var cleanDir2 = func() (abs string) { 219 var err error 220 if abs, err = filepath.Abs(tempDir2); err != nil { 221 panic(err) 222 } else if abs, err = filepath.EvalSymlinks(abs); err != nil { 223 panic(err) 224 } 225 return 226 }() 227 var symlink = filepath.Join(tempDir, "symlink") 228 if e := os.Symlink(tempDir2, symlink); e != nil { 229 panic(e) 230 } 231 232 var result ResultEntry 233 234 var traverser = NewTraverser(tempDir) 235 236 // Next should be tempDir 237 result = traverser.Next() 238 if result.Err != nil { 239 t.Errorf("Next err: %s", result.Err) 240 } 241 if result.ProvidedPath != tempDir { 242 t.Errorf("Next ProvidedPath %q exp %q", result.ProvidedPath, tempDir) 243 } 244 245 // NextNext should be symlink 246 result = traverser.Next() 247 if result.Err != nil { 248 t.Errorf("NextNext err: %s", result.Err) 249 } 250 if result.ProvidedPath != symlink { 251 t.Errorf("NextNext ProvidedPath %q exp %q", result.ProvidedPath, symlink) 252 } 253 if result.Abs != cleanDir2 { 254 t.Errorf("NextNext Abs %q exp %q", result.Abs, cleanDir2) 255 } 256 257 // Next3 should be cleanDir2 258 result = traverser.Next() 259 if result.Err != nil { 260 t.Errorf("Next3 err: %s", result.Err) 261 } 262 if result.IsEnd() { 263 t.Error("Next3 IsEnd") 264 } 265 if result.ProvidedPath != cleanDir2 { 266 t.Errorf("Next3 ProvidedPath %q exp %q", result.ProvidedPath, cleanDir2) 267 } 268 269 // Next4 should be end 270 result = traverser.Next() 271 if result.Err != nil { 272 t.Errorf("Next4 err: %s", result.Err) 273 } 274 if !result.IsEnd() { 275 t.Error("Next4 not end") 276 } 277 } 278 279 func TestTraverserBrokenSymlink(t *testing.T) { 280 //t.Fail() 281 var tempDir = t.TempDir() 282 var brokenLink = filepath.Join(tempDir, "brokenLink") 283 if e := os.Symlink(filepath.Join(tempDir, "nonExistent"), brokenLink); e != nil { 284 panic(e) 285 } 286 287 var result ResultEntry 288 289 var traverser = NewTraverser(brokenLink) 290 291 // Next should be err 292 result = traverser.Next() 293 294 // result.Err: pfs.Load filepath.EvalSymlinks 295 // lstat /private/var/folders/sq/0x1_9fyn1bv907s7ypfryt1c0000gn/T/TestTraverserBrokenSymlink814588820/001/nonExistent: 296 // no such file or directory at pfs.(*Root2).Load()-root2.go:51 297 t.Logf("result.Err: %s", perrors.Short(result.Err)) 298 299 if result.Err == nil { 300 t.Error("Next missing err") 301 } else if !errors.Is(result.Err, fs.ErrNotExist) { 302 t.Errorf("Next err not fs.ErrNotExist: %s", perrors.Short(result.Err)) 303 } 304 } 305 306 func TestTraverserObsoletingSymlink(t *testing.T) { 307 var tempDir = t.TempDir() 308 var dir = filepath.Join(tempDir, "dir") 309 if e := os.Mkdir(dir, 0700); e != nil { 310 panic(e) 311 } 312 var obsoletingLink = filepath.Join(dir, "obsoletingLink") 313 if e := os.Symlink(tempDir, obsoletingLink); e != nil { 314 panic(e) 315 } 316 var cleanTempDir = func() (abs string) { 317 var err error 318 if abs, err = filepath.Abs(tempDir); err != nil { 319 panic(err) 320 } else if abs, err = filepath.EvalSymlinks(abs); err != nil { 321 panic(err) 322 } 323 return 324 }() 325 326 var result ResultEntry 327 328 var traverser = NewTraverser(dir) 329 330 // Next should be dir 331 result = traverser.Next() 332 if result.Err != nil { 333 t.Errorf("Next err: %s", result.Err) 334 } 335 if result.ProvidedPath != dir { 336 t.Errorf("Next ProvidedPath %q exp %q", result.ProvidedPath, dir) 337 } 338 339 // Next2 should be obsoletingLink 340 result = traverser.Next() 341 if result.Err != nil { 342 t.Errorf("Next2 err: %s", result.Err) 343 } 344 if result.IsEnd() { 345 t.Error("Next2 end") 346 } 347 if result.ProvidedPath != obsoletingLink { 348 t.Errorf("Next2 ProvidedPath %q exp %q", result.ProvidedPath, obsoletingLink) 349 } 350 if result.Abs != cleanTempDir { 351 t.Errorf("Next2 Abs %q exp %q", result.Abs, cleanTempDir) 352 } 353 354 // NextNext should be cleanTempDir 355 // - dir/obsoletingLink links to parent tempDir 356 // - entries from dir ends 357 // - the new root is traversed 358 result = traverser.Next() 359 if result.Err != nil { 360 t.Errorf("NextNext err: %s", result.Err) 361 } 362 if result.IsEnd() { 363 t.Error("NextNext end") 364 } 365 if result.ProvidedPath != cleanTempDir { 366 t.Errorf("NextNext ProvidedPath %q exp %q", result.ProvidedPath, cleanTempDir) 367 } 368 369 // NextNextNext should end 370 result = traverser.Next() 371 if result.Err != nil { 372 t.Errorf("NextNextNext err: %s", result.Err) 373 } 374 if !result.IsEnd() { 375 t.Error("NextNextNext not end") 376 } 377 }