Source file
src/os/removeall_test.go
1
2
3
4
5 package os_test
6
7 import (
8 "bytes"
9 "fmt"
10 "internal/testenv"
11 . "os"
12 "path/filepath"
13 "runtime"
14 "strconv"
15 "strings"
16 "testing"
17 )
18
19 func TestRemoveAll(t *testing.T) {
20 t.Parallel()
21
22 tmpDir := t.TempDir()
23 if err := RemoveAll(""); err != nil {
24 t.Errorf("RemoveAll(\"\"): %v; want nil", err)
25 }
26
27 file := filepath.Join(tmpDir, "file")
28 path := filepath.Join(tmpDir, "_TestRemoveAll_")
29 fpath := filepath.Join(path, "file")
30 dpath := filepath.Join(path, "dir")
31
32
33 fd, err := Create(file)
34 if err != nil {
35 t.Fatalf("create %q: %s", file, err)
36 }
37 fd.Close()
38 if err = RemoveAll(file); err != nil {
39 t.Fatalf("RemoveAll %q (first): %s", file, err)
40 }
41 if _, err = Lstat(file); err == nil {
42 t.Fatalf("Lstat %q succeeded after RemoveAll (first)", file)
43 }
44
45
46 if err := MkdirAll(path, 0777); err != nil {
47 t.Fatalf("MkdirAll %q: %s", path, err)
48 }
49 fd, err = Create(fpath)
50 if err != nil {
51 t.Fatalf("create %q: %s", fpath, err)
52 }
53 fd.Close()
54 if err = RemoveAll(path); err != nil {
55 t.Fatalf("RemoveAll %q (second): %s", path, err)
56 }
57 if _, err = Lstat(path); err == nil {
58 t.Fatalf("Lstat %q succeeded after RemoveAll (second)", path)
59 }
60
61
62 if err = MkdirAll(dpath, 0777); err != nil {
63 t.Fatalf("MkdirAll %q: %s", dpath, err)
64 }
65 fd, err = Create(fpath)
66 if err != nil {
67 t.Fatalf("create %q: %s", fpath, err)
68 }
69 fd.Close()
70 fd, err = Create(dpath + "/file")
71 if err != nil {
72 t.Fatalf("create %q: %s", fpath, err)
73 }
74 fd.Close()
75 if err = RemoveAll(path); err != nil {
76 t.Fatalf("RemoveAll %q (third): %s", path, err)
77 }
78 if _, err := Lstat(path); err == nil {
79 t.Fatalf("Lstat %q succeeded after RemoveAll (third)", path)
80 }
81
82
83 if runtime.GOOS != "windows" && runtime.GOOS != "wasip1" && Getuid() != 0 {
84
85 if err = MkdirAll(dpath, 0777); err != nil {
86 t.Fatalf("MkdirAll %q: %s", dpath, err)
87 }
88
89 for _, s := range []string{fpath, dpath + "/file1", path + "/zzz"} {
90 fd, err = Create(s)
91 if err != nil {
92 t.Fatalf("create %q: %s", s, err)
93 }
94 fd.Close()
95 }
96 if err = Chmod(dpath, 0); err != nil {
97 t.Fatalf("Chmod %q 0: %s", dpath, err)
98 }
99
100
101
102
103
104
105
106
107 RemoveAll(path)
108 Chmod(dpath, 0777)
109
110 for _, s := range []string{fpath, path + "/zzz"} {
111 if _, err = Lstat(s); err == nil {
112 t.Fatalf("Lstat %q succeeded after partial RemoveAll", s)
113 }
114 }
115 }
116 if err = RemoveAll(path); err != nil {
117 t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err)
118 }
119 if _, err = Lstat(path); err == nil {
120 t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path)
121 }
122 }
123
124
125 func TestRemoveAllLarge(t *testing.T) {
126 if testing.Short() {
127 t.Skip("skipping in short mode")
128 }
129 t.Parallel()
130
131 tmpDir := t.TempDir()
132 path := filepath.Join(tmpDir, "_TestRemoveAllLarge_")
133
134
135 if err := MkdirAll(path, 0777); err != nil {
136 t.Fatalf("MkdirAll %q: %s", path, err)
137 }
138 for i := 0; i < 1000; i++ {
139 fpath := fmt.Sprintf("%s/file%d", path, i)
140 fd, err := Create(fpath)
141 if err != nil {
142 t.Fatalf("create %q: %s", fpath, err)
143 }
144 fd.Close()
145 }
146 if err := RemoveAll(path); err != nil {
147 t.Fatalf("RemoveAll %q: %s", path, err)
148 }
149 if _, err := Lstat(path); err == nil {
150 t.Fatalf("Lstat %q succeeded after RemoveAll", path)
151 }
152 }
153
154 func TestRemoveAllLongPath(t *testing.T) {
155 switch runtime.GOOS {
156 case "aix", "darwin", "ios", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "illumos", "solaris":
157 break
158 default:
159 t.Skip("skipping for not implemented platforms")
160 }
161
162 prevDir, err := Getwd()
163 if err != nil {
164 t.Fatalf("Could not get wd: %s", err)
165 }
166
167 startPath, err := MkdirTemp("", "TestRemoveAllLongPath-")
168 if err != nil {
169 t.Fatalf("Could not create TempDir: %s", err)
170 }
171 defer RemoveAll(startPath)
172
173 err = Chdir(startPath)
174 if err != nil {
175 t.Fatalf("Could not chdir %s: %s", startPath, err)
176 }
177
178
179 for i := 0; i < 41; i++ {
180 name := strings.Repeat("a", 100)
181
182 err = Mkdir(name, 0755)
183 if err != nil {
184 t.Fatalf("Could not mkdir %s: %s", name, err)
185 }
186
187 err = Chdir(name)
188 if err != nil {
189 t.Fatalf("Could not chdir %s: %s", name, err)
190 }
191 }
192
193 err = Chdir(prevDir)
194 if err != nil {
195 t.Fatalf("Could not chdir %s: %s", prevDir, err)
196 }
197
198 err = RemoveAll(startPath)
199 if err != nil {
200 t.Errorf("RemoveAll could not remove long file path %s: %s", startPath, err)
201 }
202 }
203
204 func TestRemoveAllDot(t *testing.T) {
205 prevDir, err := Getwd()
206 if err != nil {
207 t.Fatalf("Could not get wd: %s", err)
208 }
209 tempDir, err := MkdirTemp("", "TestRemoveAllDot-")
210 if err != nil {
211 t.Fatalf("Could not create TempDir: %s", err)
212 }
213 defer RemoveAll(tempDir)
214
215 err = Chdir(tempDir)
216 if err != nil {
217 t.Fatalf("Could not chdir to tempdir: %s", err)
218 }
219
220 err = RemoveAll(".")
221 if err == nil {
222 t.Errorf("RemoveAll succeed to remove .")
223 }
224
225 err = Chdir(prevDir)
226 if err != nil {
227 t.Fatalf("Could not chdir %s: %s", prevDir, err)
228 }
229 }
230
231 func TestRemoveAllDotDot(t *testing.T) {
232 t.Parallel()
233
234 tempDir := t.TempDir()
235 subdir := filepath.Join(tempDir, "x")
236 subsubdir := filepath.Join(subdir, "y")
237 if err := MkdirAll(subsubdir, 0777); err != nil {
238 t.Fatal(err)
239 }
240 if err := RemoveAll(filepath.Join(subsubdir, "..")); err != nil {
241 t.Error(err)
242 }
243 for _, dir := range []string{subsubdir, subdir} {
244 if _, err := Stat(dir); err == nil {
245 t.Errorf("%s: exists after RemoveAll", dir)
246 }
247 }
248 }
249
250
251 func TestRemoveReadOnlyDir(t *testing.T) {
252 t.Parallel()
253
254 tempDir := t.TempDir()
255 subdir := filepath.Join(tempDir, "x")
256 if err := Mkdir(subdir, 0); err != nil {
257 t.Fatal(err)
258 }
259
260
261
262 defer Chmod(subdir, 0777)
263
264 if err := RemoveAll(subdir); err != nil {
265 t.Fatal(err)
266 }
267
268 if _, err := Stat(subdir); err == nil {
269 t.Error("subdirectory was not removed")
270 }
271 }
272
273
274 func TestRemoveAllButReadOnlyAndPathError(t *testing.T) {
275 switch runtime.GOOS {
276 case "js", "wasip1", "windows":
277 t.Skipf("skipping test on %s", runtime.GOOS)
278 }
279
280 if Getuid() == 0 {
281 t.Skip("skipping test when running as root")
282 }
283
284 t.Parallel()
285
286 tempDir := t.TempDir()
287 dirs := []string{
288 "a",
289 "a/x",
290 "a/x/1",
291 "b",
292 "b/y",
293 "b/y/2",
294 "c",
295 "c/z",
296 "c/z/3",
297 }
298 readonly := []string{
299 "b",
300 }
301 inReadonly := func(d string) bool {
302 for _, ro := range readonly {
303 if d == ro {
304 return true
305 }
306 dd, _ := filepath.Split(d)
307 if filepath.Clean(dd) == ro {
308 return true
309 }
310 }
311 return false
312 }
313
314 for _, dir := range dirs {
315 if err := Mkdir(filepath.Join(tempDir, dir), 0777); err != nil {
316 t.Fatal(err)
317 }
318 }
319 for _, dir := range readonly {
320 d := filepath.Join(tempDir, dir)
321 if err := Chmod(d, 0555); err != nil {
322 t.Fatal(err)
323 }
324
325
326
327 defer Chmod(d, 0777)
328 }
329
330 err := RemoveAll(tempDir)
331 if err == nil {
332 t.Fatal("RemoveAll succeeded unexpectedly")
333 }
334
335
336
337 if pathErr, ok := err.(*PathError); ok {
338 want := filepath.Join(tempDir, "b", "y")
339 if pathErr.Path != want {
340 t.Errorf("RemoveAll(%q): err.Path=%q, want %q", tempDir, pathErr.Path, want)
341 }
342 } else {
343 t.Errorf("RemoveAll(%q): error has type %T, want *fs.PathError", tempDir, err)
344 }
345
346 for _, dir := range dirs {
347 _, err := Stat(filepath.Join(tempDir, dir))
348 if inReadonly(dir) {
349 if err != nil {
350 t.Errorf("file %q was deleted but should still exist", dir)
351 }
352 } else {
353 if err == nil {
354 t.Errorf("file %q still exists but should have been deleted", dir)
355 }
356 }
357 }
358 }
359
360 func TestRemoveUnreadableDir(t *testing.T) {
361 switch runtime.GOOS {
362 case "js":
363 t.Skipf("skipping test on %s", runtime.GOOS)
364 }
365
366 if Getuid() == 0 {
367 t.Skip("skipping test when running as root")
368 }
369
370 t.Parallel()
371
372 tempDir := t.TempDir()
373 target := filepath.Join(tempDir, "d0", "d1", "d2")
374 if err := MkdirAll(target, 0755); err != nil {
375 t.Fatal(err)
376 }
377 if err := Chmod(target, 0300); err != nil {
378 t.Fatal(err)
379 }
380 if err := RemoveAll(filepath.Join(tempDir, "d0")); err != nil {
381 t.Fatal(err)
382 }
383 }
384
385
386 func TestRemoveAllWithMoreErrorThanReqSize(t *testing.T) {
387 if testing.Short() {
388 t.Skip("skipping in short mode")
389 }
390 t.Parallel()
391
392 tmpDir := t.TempDir()
393 path := filepath.Join(tmpDir, "_TestRemoveAllWithMoreErrorThanReqSize_")
394
395
396 if err := MkdirAll(path, 0777); err != nil {
397 t.Fatalf("MkdirAll %q: %s", path, err)
398 }
399 for i := 0; i < 1025; i++ {
400 fpath := filepath.Join(path, fmt.Sprintf("file%d", i))
401 fd, err := Create(fpath)
402 if err != nil {
403 t.Fatalf("create %q: %s", fpath, err)
404 }
405 fd.Close()
406 }
407
408
409
410 if err := Chmod(path, 0555); err != nil {
411 t.Fatal(err)
412 }
413 defer Chmod(path, 0755)
414
415
416
417 err := RemoveAll(path)
418
419 if Getuid() == 0 {
420
421 return
422 }
423 if err == nil {
424 if runtime.GOOS == "windows" || runtime.GOOS == "wasip1" {
425
426
427
428
429
430 return
431 }
432 t.Fatal("RemoveAll(<read-only directory>) = nil; want error")
433 }
434
435 dir, err := Open(path)
436 if err != nil {
437 t.Fatal(err)
438 }
439 defer dir.Close()
440
441 names, _ := dir.Readdirnames(1025)
442 if len(names) < 1025 {
443 t.Fatalf("RemoveAll(<read-only directory>) unexpectedly removed %d read-only files from that directory", 1025-len(names))
444 }
445 }
446
447 func TestRemoveAllNoFcntl(t *testing.T) {
448 if testing.Short() {
449 t.Skip("skipping in short mode")
450 }
451
452 const env = "GO_TEST_REMOVE_ALL_NO_FCNTL"
453 if dir := Getenv(env); dir != "" {
454 if err := RemoveAll(dir); err != nil {
455 t.Fatal(err)
456 }
457 return
458 }
459
460
461
462
463 if runtime.GOOS != "linux" {
464 t.Skipf("skipping test on %s", runtime.GOOS)
465 }
466 if _, err := Stat("/bin/strace"); err != nil {
467 t.Skipf("skipping test because /bin/strace not found: %v", err)
468 }
469 me, err := Executable()
470 if err != nil {
471 t.Skipf("skipping because Executable failed: %v", err)
472 }
473
474
475
476
477 tmpdir := t.TempDir()
478 subdir := filepath.Join(tmpdir, "subdir")
479 if err := Mkdir(subdir, 0o755); err != nil {
480 t.Fatal(err)
481 }
482 for i := 0; i < 100; i++ {
483 subsubdir := filepath.Join(subdir, strconv.Itoa(i))
484 if err := Mkdir(filepath.Join(subdir, strconv.Itoa(i)), 0o755); err != nil {
485 t.Fatal(err)
486 }
487 if err := WriteFile(filepath.Join(subsubdir, "file"), nil, 0o644); err != nil {
488 t.Fatal(err)
489 }
490 }
491
492 cmd := testenv.Command(t, "/bin/strace", "-f", "-e", "fcntl", me, "-test.run=^TestRemoveAllNoFcntl$")
493 cmd = testenv.CleanCmdEnv(cmd)
494 cmd.Env = append(cmd.Env, env+"="+subdir)
495 out, err := cmd.CombinedOutput()
496 if len(out) > 0 {
497 t.Logf("%s", out)
498 }
499 if err != nil {
500 t.Fatal(err)
501 }
502
503 if got := bytes.Count(out, []byte("fcntl")); got >= 100 {
504 t.Errorf("found %d fcntl calls, want < 100", got)
505 }
506 }
507
508 func BenchmarkRemoveAll(b *testing.B) {
509 tmpDir := filepath.Join(b.TempDir(), "target")
510 b.ReportAllocs()
511 b.ResetTimer()
512 for i := 0; i < b.N; i++ {
513 b.StopTimer()
514 err := CopyFS(tmpDir, DirFS("."))
515 if err != nil {
516 b.Fatal(err)
517 }
518 b.StartTimer()
519 if err := RemoveAll(tmpDir); err != nil {
520 b.Fatal(err)
521 }
522 }
523 }
524
View as plain text