github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/webdav/file_test.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package webdav 6 7 import ( 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 "path" 13 "path/filepath" 14 "reflect" 15 "runtime" 16 "sort" 17 "strconv" 18 "strings" 19 "testing" 20 21 "golang.org/x/net/webdav/internal/xml" 22 ) 23 24 func TestSlashClean(t *testing.T) { 25 testCases := []string{ 26 "", 27 ".", 28 "/", 29 "/./", 30 "//", 31 "//.", 32 "//a", 33 "/a", 34 "/a/b/c", 35 "/a//b/./../c/d/", 36 "a", 37 "a/b/c", 38 } 39 for _, tc := range testCases { 40 got := slashClean(tc) 41 want := path.Clean("/" + tc) 42 if got != want { 43 t.Errorf("tc=%q: got %q, want %q", tc, got, want) 44 } 45 } 46 } 47 48 func TestDirResolve(t *testing.T) { 49 testCases := []struct { 50 dir, name, want string 51 }{ 52 {"/", "", "/"}, 53 {"/", "/", "/"}, 54 {"/", ".", "/"}, 55 {"/", "./a", "/a"}, 56 {"/", "..", "/"}, 57 {"/", "..", "/"}, 58 {"/", "../", "/"}, 59 {"/", "../.", "/"}, 60 {"/", "../a", "/a"}, 61 {"/", "../..", "/"}, 62 {"/", "../bar/a", "/bar/a"}, 63 {"/", "../baz/a", "/baz/a"}, 64 {"/", "...", "/..."}, 65 {"/", ".../a", "/.../a"}, 66 {"/", ".../..", "/"}, 67 {"/", "a", "/a"}, 68 {"/", "a/./b", "/a/b"}, 69 {"/", "a/../../b", "/b"}, 70 {"/", "a/../b", "/b"}, 71 {"/", "a/b", "/a/b"}, 72 {"/", "a/b/c/../../d", "/a/d"}, 73 {"/", "a/b/c/../../../d", "/d"}, 74 {"/", "a/b/c/../../../../d", "/d"}, 75 {"/", "a/b/c/d", "/a/b/c/d"}, 76 77 {"/foo/bar", "", "/foo/bar"}, 78 {"/foo/bar", "/", "/foo/bar"}, 79 {"/foo/bar", ".", "/foo/bar"}, 80 {"/foo/bar", "./a", "/foo/bar/a"}, 81 {"/foo/bar", "..", "/foo/bar"}, 82 {"/foo/bar", "../", "/foo/bar"}, 83 {"/foo/bar", "../.", "/foo/bar"}, 84 {"/foo/bar", "../a", "/foo/bar/a"}, 85 {"/foo/bar", "../..", "/foo/bar"}, 86 {"/foo/bar", "../bar/a", "/foo/bar/bar/a"}, 87 {"/foo/bar", "../baz/a", "/foo/bar/baz/a"}, 88 {"/foo/bar", "...", "/foo/bar/..."}, 89 {"/foo/bar", ".../a", "/foo/bar/.../a"}, 90 {"/foo/bar", ".../..", "/foo/bar"}, 91 {"/foo/bar", "a", "/foo/bar/a"}, 92 {"/foo/bar", "a/./b", "/foo/bar/a/b"}, 93 {"/foo/bar", "a/../../b", "/foo/bar/b"}, 94 {"/foo/bar", "a/../b", "/foo/bar/b"}, 95 {"/foo/bar", "a/b", "/foo/bar/a/b"}, 96 {"/foo/bar", "a/b/c/../../d", "/foo/bar/a/d"}, 97 {"/foo/bar", "a/b/c/../../../d", "/foo/bar/d"}, 98 {"/foo/bar", "a/b/c/../../../../d", "/foo/bar/d"}, 99 {"/foo/bar", "a/b/c/d", "/foo/bar/a/b/c/d"}, 100 101 {"/foo/bar/", "", "/foo/bar"}, 102 {"/foo/bar/", "/", "/foo/bar"}, 103 {"/foo/bar/", ".", "/foo/bar"}, 104 {"/foo/bar/", "./a", "/foo/bar/a"}, 105 {"/foo/bar/", "..", "/foo/bar"}, 106 107 {"/foo//bar///", "", "/foo/bar"}, 108 {"/foo//bar///", "/", "/foo/bar"}, 109 {"/foo//bar///", ".", "/foo/bar"}, 110 {"/foo//bar///", "./a", "/foo/bar/a"}, 111 {"/foo//bar///", "..", "/foo/bar"}, 112 113 {"/x/y/z", "ab/c\x00d/ef", ""}, 114 115 {".", "", "."}, 116 {".", "/", "."}, 117 {".", ".", "."}, 118 {".", "./a", "a"}, 119 {".", "..", "."}, 120 {".", "..", "."}, 121 {".", "../", "."}, 122 {".", "../.", "."}, 123 {".", "../a", "a"}, 124 {".", "../..", "."}, 125 {".", "../bar/a", "bar/a"}, 126 {".", "../baz/a", "baz/a"}, 127 {".", "...", "..."}, 128 {".", ".../a", ".../a"}, 129 {".", ".../..", "."}, 130 {".", "a", "a"}, 131 {".", "a/./b", "a/b"}, 132 {".", "a/../../b", "b"}, 133 {".", "a/../b", "b"}, 134 {".", "a/b", "a/b"}, 135 {".", "a/b/c/../../d", "a/d"}, 136 {".", "a/b/c/../../../d", "d"}, 137 {".", "a/b/c/../../../../d", "d"}, 138 {".", "a/b/c/d", "a/b/c/d"}, 139 140 {"", "", "."}, 141 {"", "/", "."}, 142 {"", ".", "."}, 143 {"", "./a", "a"}, 144 {"", "..", "."}, 145 } 146 147 for _, tc := range testCases { 148 d := Dir(filepath.FromSlash(tc.dir)) 149 if got := filepath.ToSlash(d.resolve(tc.name)); got != tc.want { 150 t.Errorf("dir=%q, name=%q: got %q, want %q", tc.dir, tc.name, got, tc.want) 151 } 152 } 153 } 154 155 func TestWalk(t *testing.T) { 156 type walkStep struct { 157 name, frag string 158 final bool 159 } 160 161 testCases := []struct { 162 dir string 163 want []walkStep 164 }{ 165 {"", []walkStep{ 166 {"", "", true}, 167 }}, 168 {"/", []walkStep{ 169 {"", "", true}, 170 }}, 171 {"/a", []walkStep{ 172 {"", "a", true}, 173 }}, 174 {"/a/", []walkStep{ 175 {"", "a", true}, 176 }}, 177 {"/a/b", []walkStep{ 178 {"", "a", false}, 179 {"a", "b", true}, 180 }}, 181 {"/a/b/", []walkStep{ 182 {"", "a", false}, 183 {"a", "b", true}, 184 }}, 185 {"/a/b/c", []walkStep{ 186 {"", "a", false}, 187 {"a", "b", false}, 188 {"b", "c", true}, 189 }}, 190 // The following test case is the one mentioned explicitly 191 // in the method description. 192 {"/foo/bar/x", []walkStep{ 193 {"", "foo", false}, 194 {"foo", "bar", false}, 195 {"bar", "x", true}, 196 }}, 197 } 198 199 for _, tc := range testCases { 200 fs := NewMemFS().(*memFS) 201 202 parts := strings.Split(tc.dir, "/") 203 for p := 2; p < len(parts); p++ { 204 d := strings.Join(parts[:p], "/") 205 if err := fs.Mkdir(d, 0666); err != nil { 206 t.Errorf("tc.dir=%q: mkdir: %q: %v", tc.dir, d, err) 207 } 208 } 209 210 i, prevFrag := 0, "" 211 err := fs.walk("test", tc.dir, func(dir *memFSNode, frag string, final bool) error { 212 got := walkStep{ 213 name: prevFrag, 214 frag: frag, 215 final: final, 216 } 217 want := tc.want[i] 218 219 if got != want { 220 return fmt.Errorf("got %+v, want %+v", got, want) 221 } 222 i, prevFrag = i+1, frag 223 return nil 224 }) 225 if err != nil { 226 t.Errorf("tc.dir=%q: %v", tc.dir, err) 227 } 228 } 229 } 230 231 // find appends to ss the names of the named file and its children. It is 232 // analogous to the Unix find command. 233 // 234 // The returned strings are not guaranteed to be in any particular order. 235 func find(ss []string, fs FileSystem, name string) ([]string, error) { 236 stat, err := fs.Stat(name) 237 if err != nil { 238 return nil, err 239 } 240 ss = append(ss, name) 241 if stat.IsDir() { 242 f, err := fs.OpenFile(name, os.O_RDONLY, 0) 243 if err != nil { 244 return nil, err 245 } 246 defer f.Close() 247 children, err := f.Readdir(-1) 248 if err != nil { 249 return nil, err 250 } 251 for _, c := range children { 252 ss, err = find(ss, fs, path.Join(name, c.Name())) 253 if err != nil { 254 return nil, err 255 } 256 } 257 } 258 return ss, nil 259 } 260 261 func testFS(t *testing.T, fs FileSystem) { 262 errStr := func(err error) string { 263 switch { 264 case os.IsExist(err): 265 return "errExist" 266 case os.IsNotExist(err): 267 return "errNotExist" 268 case err != nil: 269 return "err" 270 } 271 return "ok" 272 } 273 274 // The non-"find" non-"stat" test cases should change the file system state. The 275 // indentation of the "find"s and "stat"s helps distinguish such test cases. 276 testCases := []string{ 277 " stat / want dir", 278 " stat /a want errNotExist", 279 " stat /d want errNotExist", 280 " stat /d/e want errNotExist", 281 "create /a A want ok", 282 " stat /a want 1", 283 "create /d/e EEE want errNotExist", 284 "mk-dir /a want errExist", 285 "mk-dir /d/m want errNotExist", 286 "mk-dir /d want ok", 287 " stat /d want dir", 288 "create /d/e EEE want ok", 289 " stat /d/e want 3", 290 " find / /a /d /d/e", 291 "create /d/f FFFF want ok", 292 "create /d/g GGGGGGG want ok", 293 "mk-dir /d/m want ok", 294 "mk-dir /d/m want errExist", 295 "create /d/m/p PPPPP want ok", 296 " stat /d/e want 3", 297 " stat /d/f want 4", 298 " stat /d/g want 7", 299 " stat /d/h want errNotExist", 300 " stat /d/m want dir", 301 " stat /d/m/p want 5", 302 " find / /a /d /d/e /d/f /d/g /d/m /d/m/p", 303 "rm-all /d want ok", 304 " stat /a want 1", 305 " stat /d want errNotExist", 306 " stat /d/e want errNotExist", 307 " stat /d/f want errNotExist", 308 " stat /d/g want errNotExist", 309 " stat /d/m want errNotExist", 310 " stat /d/m/p want errNotExist", 311 " find / /a", 312 "mk-dir /d/m want errNotExist", 313 "mk-dir /d want ok", 314 "create /d/f FFFF want ok", 315 "rm-all /d/f want ok", 316 "mk-dir /d/m want ok", 317 "rm-all /z want ok", 318 "rm-all / want err", 319 "create /b BB want ok", 320 " stat / want dir", 321 " stat /a want 1", 322 " stat /b want 2", 323 " stat /c want errNotExist", 324 " stat /d want dir", 325 " stat /d/m want dir", 326 " find / /a /b /d /d/m", 327 "move__ o=F /b /c want ok", 328 " stat /b want errNotExist", 329 " stat /c want 2", 330 " stat /d/m want dir", 331 " stat /d/n want errNotExist", 332 " find / /a /c /d /d/m", 333 "move__ o=F /d/m /d/n want ok", 334 "create /d/n/q QQQQ want ok", 335 " stat /d/m want errNotExist", 336 " stat /d/n want dir", 337 " stat /d/n/q want 4", 338 "move__ o=F /d /d/n/z want err", 339 "move__ o=T /c /d/n/q want ok", 340 " stat /c want errNotExist", 341 " stat /d/n/q want 2", 342 " find / /a /d /d/n /d/n/q", 343 "create /d/n/r RRRRR want ok", 344 "mk-dir /u want ok", 345 "mk-dir /u/v want ok", 346 "move__ o=F /d/n /u want errExist", 347 "create /t TTTTTT want ok", 348 "move__ o=F /d/n /t want errExist", 349 "rm-all /t want ok", 350 "move__ o=F /d/n /t want ok", 351 " stat /d want dir", 352 " stat /d/n want errNotExist", 353 " stat /d/n/r want errNotExist", 354 " stat /t want dir", 355 " stat /t/q want 2", 356 " stat /t/r want 5", 357 " find / /a /d /t /t/q /t/r /u /u/v", 358 "move__ o=F /t / want errExist", 359 "move__ o=T /t /u/v want ok", 360 " stat /u/v/r want 5", 361 "move__ o=F / /z want err", 362 " find / /a /d /u /u/v /u/v/q /u/v/r", 363 " stat /a want 1", 364 " stat /b want errNotExist", 365 " stat /c want errNotExist", 366 " stat /u/v/r want 5", 367 "copy__ o=F d=0 /a /b want ok", 368 "copy__ o=T d=0 /a /c want ok", 369 " stat /a want 1", 370 " stat /b want 1", 371 " stat /c want 1", 372 " stat /u/v/r want 5", 373 "copy__ o=F d=0 /u/v/r /b want errExist", 374 " stat /b want 1", 375 "copy__ o=T d=0 /u/v/r /b want ok", 376 " stat /a want 1", 377 " stat /b want 5", 378 " stat /u/v/r want 5", 379 "rm-all /a want ok", 380 "rm-all /b want ok", 381 "mk-dir /u/v/w want ok", 382 "create /u/v/w/s SSSSSSSS want ok", 383 " stat /d want dir", 384 " stat /d/x want errNotExist", 385 " stat /d/y want errNotExist", 386 " stat /u/v/r want 5", 387 " stat /u/v/w/s want 8", 388 " find / /c /d /u /u/v /u/v/q /u/v/r /u/v/w /u/v/w/s", 389 "copy__ o=T d=0 /u/v /d/x want ok", 390 "copy__ o=T d=∞ /u/v /d/y want ok", 391 "rm-all /u want ok", 392 " stat /d/x want dir", 393 " stat /d/x/q want errNotExist", 394 " stat /d/x/r want errNotExist", 395 " stat /d/x/w want errNotExist", 396 " stat /d/x/w/s want errNotExist", 397 " stat /d/y want dir", 398 " stat /d/y/q want 2", 399 " stat /d/y/r want 5", 400 " stat /d/y/w want dir", 401 " stat /d/y/w/s want 8", 402 " stat /u want errNotExist", 403 " find / /c /d /d/x /d/y /d/y/q /d/y/r /d/y/w /d/y/w/s", 404 "copy__ o=F d=∞ /d/y /d/x want errExist", 405 } 406 407 for i, tc := range testCases { 408 tc = strings.TrimSpace(tc) 409 j := strings.IndexByte(tc, ' ') 410 if j < 0 { 411 t.Fatalf("test case #%d %q: invalid command", i, tc) 412 } 413 op, arg := tc[:j], tc[j+1:] 414 415 switch op { 416 default: 417 t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op) 418 419 case "create": 420 parts := strings.Split(arg, " ") 421 if len(parts) != 4 || parts[2] != "want" { 422 t.Fatalf("test case #%d %q: invalid write", i, tc) 423 } 424 f, opErr := fs.OpenFile(parts[0], os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 425 if got := errStr(opErr); got != parts[3] { 426 t.Fatalf("test case #%d %q: OpenFile: got %q (%v), want %q", i, tc, got, opErr, parts[3]) 427 } 428 if f != nil { 429 if _, err := f.Write([]byte(parts[1])); err != nil { 430 t.Fatalf("test case #%d %q: Write: %v", i, tc, err) 431 } 432 if err := f.Close(); err != nil { 433 t.Fatalf("test case #%d %q: Close: %v", i, tc, err) 434 } 435 } 436 437 case "find": 438 got, err := find(nil, fs, "/") 439 if err != nil { 440 t.Fatalf("test case #%d %q: find: %v", i, tc, err) 441 } 442 sort.Strings(got) 443 want := strings.Split(arg, " ") 444 if !reflect.DeepEqual(got, want) { 445 t.Fatalf("test case #%d %q:\ngot %s\nwant %s", i, tc, got, want) 446 } 447 448 case "copy__", "mk-dir", "move__", "rm-all", "stat": 449 nParts := 3 450 switch op { 451 case "copy__": 452 nParts = 6 453 case "move__": 454 nParts = 5 455 } 456 parts := strings.Split(arg, " ") 457 if len(parts) != nParts { 458 t.Fatalf("test case #%d %q: invalid %s", i, tc, op) 459 } 460 461 got, opErr := "", error(nil) 462 switch op { 463 case "copy__": 464 depth := 0 465 if parts[1] == "d=∞" { 466 depth = infiniteDepth 467 } 468 _, opErr = copyFiles(fs, parts[2], parts[3], parts[0] == "o=T", depth, 0) 469 case "mk-dir": 470 opErr = fs.Mkdir(parts[0], 0777) 471 case "move__": 472 _, opErr = moveFiles(fs, parts[1], parts[2], parts[0] == "o=T") 473 case "rm-all": 474 opErr = fs.RemoveAll(parts[0]) 475 case "stat": 476 var stat os.FileInfo 477 fileName := parts[0] 478 if stat, opErr = fs.Stat(fileName); opErr == nil { 479 if stat.IsDir() { 480 got = "dir" 481 } else { 482 got = strconv.Itoa(int(stat.Size())) 483 } 484 485 if fileName == "/" { 486 // For a Dir FileSystem, the virtual file system root maps to a 487 // real file system name like "/tmp/webdav-test012345", which does 488 // not end with "/". We skip such cases. 489 } else if statName := stat.Name(); path.Base(fileName) != statName { 490 t.Fatalf("test case #%d %q: file name %q inconsistent with stat name %q", 491 i, tc, fileName, statName) 492 } 493 } 494 } 495 if got == "" { 496 got = errStr(opErr) 497 } 498 499 if parts[len(parts)-2] != "want" { 500 t.Fatalf("test case #%d %q: invalid %s", i, tc, op) 501 } 502 if want := parts[len(parts)-1]; got != want { 503 t.Fatalf("test case #%d %q: got %q (%v), want %q", i, tc, got, opErr, want) 504 } 505 } 506 } 507 } 508 509 func TestDir(t *testing.T) { 510 switch runtime.GOOS { 511 case "nacl": 512 t.Skip("see golang.org/issue/12004") 513 case "plan9": 514 t.Skip("see golang.org/issue/11453") 515 } 516 517 td, err := ioutil.TempDir("", "webdav-test") 518 if err != nil { 519 t.Fatal(err) 520 } 521 defer os.RemoveAll(td) 522 testFS(t, Dir(td)) 523 } 524 525 func TestMemFS(t *testing.T) { 526 testFS(t, NewMemFS()) 527 } 528 529 func TestMemFSRoot(t *testing.T) { 530 fs := NewMemFS() 531 for i := 0; i < 5; i++ { 532 stat, err := fs.Stat("/") 533 if err != nil { 534 t.Fatalf("i=%d: Stat: %v", i, err) 535 } 536 if !stat.IsDir() { 537 t.Fatalf("i=%d: Stat.IsDir is false, want true", i) 538 } 539 540 f, err := fs.OpenFile("/", os.O_RDONLY, 0) 541 if err != nil { 542 t.Fatalf("i=%d: OpenFile: %v", i, err) 543 } 544 defer f.Close() 545 children, err := f.Readdir(-1) 546 if err != nil { 547 t.Fatalf("i=%d: Readdir: %v", i, err) 548 } 549 if len(children) != i { 550 t.Fatalf("i=%d: got %d children, want %d", i, len(children), i) 551 } 552 553 if _, err := f.Write(make([]byte, 1)); err == nil { 554 t.Fatalf("i=%d: Write: got nil error, want non-nil", i) 555 } 556 557 if err := fs.Mkdir(fmt.Sprintf("/dir%d", i), 0777); err != nil { 558 t.Fatalf("i=%d: Mkdir: %v", i, err) 559 } 560 } 561 } 562 563 func TestMemFileReaddir(t *testing.T) { 564 fs := NewMemFS() 565 if err := fs.Mkdir("/foo", 0777); err != nil { 566 t.Fatalf("Mkdir: %v", err) 567 } 568 readdir := func(count int) ([]os.FileInfo, error) { 569 f, err := fs.OpenFile("/foo", os.O_RDONLY, 0) 570 if err != nil { 571 t.Fatalf("OpenFile: %v", err) 572 } 573 defer f.Close() 574 return f.Readdir(count) 575 } 576 if got, err := readdir(-1); len(got) != 0 || err != nil { 577 t.Fatalf("readdir(-1): got %d fileInfos with err=%v, want 0, <nil>", len(got), err) 578 } 579 if got, err := readdir(+1); len(got) != 0 || err != io.EOF { 580 t.Fatalf("readdir(+1): got %d fileInfos with err=%v, want 0, EOF", len(got), err) 581 } 582 } 583 584 func TestMemFile(t *testing.T) { 585 testCases := []string{ 586 "wantData ", 587 "wantSize 0", 588 "write abc", 589 "wantData abc", 590 "write de", 591 "wantData abcde", 592 "wantSize 5", 593 "write 5*x", 594 "write 4*y+2*z", 595 "write 3*st", 596 "wantData abcdexxxxxyyyyzzststst", 597 "wantSize 22", 598 "seek set 4 want 4", 599 "write EFG", 600 "wantData abcdEFGxxxyyyyzzststst", 601 "wantSize 22", 602 "seek set 2 want 2", 603 "read cdEF", 604 "read Gx", 605 "seek cur 0 want 8", 606 "seek cur 2 want 10", 607 "seek cur -1 want 9", 608 "write J", 609 "wantData abcdEFGxxJyyyyzzststst", 610 "wantSize 22", 611 "seek cur -4 want 6", 612 "write ghijk", 613 "wantData abcdEFghijkyyyzzststst", 614 "wantSize 22", 615 "read yyyz", 616 "seek cur 0 want 15", 617 "write ", 618 "seek cur 0 want 15", 619 "read ", 620 "seek cur 0 want 15", 621 "seek end -3 want 19", 622 "write ZZ", 623 "wantData abcdEFghijkyyyzzstsZZt", 624 "wantSize 22", 625 "write 4*A", 626 "wantData abcdEFghijkyyyzzstsZZAAAA", 627 "wantSize 25", 628 "seek end 0 want 25", 629 "seek end -5 want 20", 630 "read Z+4*A", 631 "write 5*B", 632 "wantData abcdEFghijkyyyzzstsZZAAAABBBBB", 633 "wantSize 30", 634 "seek end 10 want 40", 635 "write C", 636 "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........C", 637 "wantSize 41", 638 "write D", 639 "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........CD", 640 "wantSize 42", 641 "seek set 43 want 43", 642 "write E", 643 "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........CD.E", 644 "wantSize 44", 645 "seek set 0 want 0", 646 "write 5*123456789_", 647 "wantData 123456789_123456789_123456789_123456789_123456789_", 648 "wantSize 50", 649 "seek cur 0 want 50", 650 "seek cur -99 want err", 651 } 652 653 const filename = "/foo" 654 fs := NewMemFS() 655 f, err := fs.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 656 if err != nil { 657 t.Fatalf("OpenFile: %v", err) 658 } 659 defer f.Close() 660 661 for i, tc := range testCases { 662 j := strings.IndexByte(tc, ' ') 663 if j < 0 { 664 t.Fatalf("test case #%d %q: invalid command", i, tc) 665 } 666 op, arg := tc[:j], tc[j+1:] 667 668 // Expand an arg like "3*a+2*b" to "aaabb". 669 parts := strings.Split(arg, "+") 670 for j, part := range parts { 671 if k := strings.IndexByte(part, '*'); k >= 0 { 672 repeatCount, repeatStr := part[:k], part[k+1:] 673 n, err := strconv.Atoi(repeatCount) 674 if err != nil { 675 t.Fatalf("test case #%d %q: invalid repeat count %q", i, tc, repeatCount) 676 } 677 parts[j] = strings.Repeat(repeatStr, n) 678 } 679 } 680 arg = strings.Join(parts, "") 681 682 switch op { 683 default: 684 t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op) 685 686 case "read": 687 buf := make([]byte, len(arg)) 688 if _, err := io.ReadFull(f, buf); err != nil { 689 t.Fatalf("test case #%d %q: ReadFull: %v", i, tc, err) 690 } 691 if got := string(buf); got != arg { 692 t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, arg) 693 } 694 695 case "seek": 696 parts := strings.Split(arg, " ") 697 if len(parts) != 4 { 698 t.Fatalf("test case #%d %q: invalid seek", i, tc) 699 } 700 701 whence := 0 702 switch parts[0] { 703 default: 704 t.Fatalf("test case #%d %q: invalid seek whence", i, tc) 705 case "set": 706 whence = os.SEEK_SET 707 case "cur": 708 whence = os.SEEK_CUR 709 case "end": 710 whence = os.SEEK_END 711 } 712 offset, err := strconv.Atoi(parts[1]) 713 if err != nil { 714 t.Fatalf("test case #%d %q: invalid offset %q", i, tc, parts[1]) 715 } 716 717 if parts[2] != "want" { 718 t.Fatalf("test case #%d %q: invalid seek", i, tc) 719 } 720 if parts[3] == "err" { 721 _, err := f.Seek(int64(offset), whence) 722 if err == nil { 723 t.Fatalf("test case #%d %q: Seek returned nil error, want non-nil", i, tc) 724 } 725 } else { 726 got, err := f.Seek(int64(offset), whence) 727 if err != nil { 728 t.Fatalf("test case #%d %q: Seek: %v", i, tc, err) 729 } 730 want, err := strconv.Atoi(parts[3]) 731 if err != nil { 732 t.Fatalf("test case #%d %q: invalid want %q", i, tc, parts[3]) 733 } 734 if got != int64(want) { 735 t.Fatalf("test case #%d %q: got %d, want %d", i, tc, got, want) 736 } 737 } 738 739 case "write": 740 n, err := f.Write([]byte(arg)) 741 if err != nil { 742 t.Fatalf("test case #%d %q: write: %v", i, tc, err) 743 } 744 if n != len(arg) { 745 t.Fatalf("test case #%d %q: write returned %d bytes, want %d", i, tc, n, len(arg)) 746 } 747 748 case "wantData": 749 g, err := fs.OpenFile(filename, os.O_RDONLY, 0666) 750 if err != nil { 751 t.Fatalf("test case #%d %q: OpenFile: %v", i, tc, err) 752 } 753 gotBytes, err := ioutil.ReadAll(g) 754 if err != nil { 755 t.Fatalf("test case #%d %q: ReadAll: %v", i, tc, err) 756 } 757 for i, c := range gotBytes { 758 if c == '\x00' { 759 gotBytes[i] = '.' 760 } 761 } 762 got := string(gotBytes) 763 if got != arg { 764 t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, arg) 765 } 766 if err := g.Close(); err != nil { 767 t.Fatalf("test case #%d %q: Close: %v", i, tc, err) 768 } 769 770 case "wantSize": 771 n, err := strconv.Atoi(arg) 772 if err != nil { 773 t.Fatalf("test case #%d %q: invalid size %q", i, tc, arg) 774 } 775 fi, err := fs.Stat(filename) 776 if err != nil { 777 t.Fatalf("test case #%d %q: Stat: %v", i, tc, err) 778 } 779 if got, want := fi.Size(), int64(n); got != want { 780 t.Fatalf("test case #%d %q: got %d, want %d", i, tc, got, want) 781 } 782 } 783 } 784 } 785 786 // TestMemFileWriteAllocs tests that writing N consecutive 1KiB chunks to a 787 // memFile doesn't allocate a new buffer for each of those N times. Otherwise, 788 // calling io.Copy(aMemFile, src) is likely to have quadratic complexity. 789 func TestMemFileWriteAllocs(t *testing.T) { 790 fs := NewMemFS() 791 f, err := fs.OpenFile("/xxx", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 792 if err != nil { 793 t.Fatalf("OpenFile: %v", err) 794 } 795 defer f.Close() 796 797 xxx := make([]byte, 1024) 798 for i := range xxx { 799 xxx[i] = 'x' 800 } 801 802 a := testing.AllocsPerRun(100, func() { 803 f.Write(xxx) 804 }) 805 // AllocsPerRun returns an integral value, so we compare the rounded-down 806 // number to zero. 807 if a > 0 { 808 t.Fatalf("%v allocs per run, want 0", a) 809 } 810 } 811 812 func BenchmarkMemFileWrite(b *testing.B) { 813 fs := NewMemFS() 814 xxx := make([]byte, 1024) 815 for i := range xxx { 816 xxx[i] = 'x' 817 } 818 819 b.ResetTimer() 820 for i := 0; i < b.N; i++ { 821 f, err := fs.OpenFile("/xxx", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 822 if err != nil { 823 b.Fatalf("OpenFile: %v", err) 824 } 825 for j := 0; j < 100; j++ { 826 f.Write(xxx) 827 } 828 if err := f.Close(); err != nil { 829 b.Fatalf("Close: %v", err) 830 } 831 if err := fs.RemoveAll("/xxx"); err != nil { 832 b.Fatalf("RemoveAll: %v", err) 833 } 834 } 835 } 836 837 func TestCopyMoveProps(t *testing.T) { 838 fs := NewMemFS() 839 create := func(name string) error { 840 f, err := fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 841 if err != nil { 842 return err 843 } 844 _, wErr := f.Write([]byte("contents")) 845 cErr := f.Close() 846 if wErr != nil { 847 return wErr 848 } 849 return cErr 850 } 851 patch := func(name string, patches ...Proppatch) error { 852 f, err := fs.OpenFile(name, os.O_RDWR, 0666) 853 if err != nil { 854 return err 855 } 856 _, pErr := f.(DeadPropsHolder).Patch(patches) 857 cErr := f.Close() 858 if pErr != nil { 859 return pErr 860 } 861 return cErr 862 } 863 props := func(name string) (map[xml.Name]Property, error) { 864 f, err := fs.OpenFile(name, os.O_RDWR, 0666) 865 if err != nil { 866 return nil, err 867 } 868 m, pErr := f.(DeadPropsHolder).DeadProps() 869 cErr := f.Close() 870 if pErr != nil { 871 return nil, pErr 872 } 873 if cErr != nil { 874 return nil, cErr 875 } 876 return m, nil 877 } 878 879 p0 := Property{ 880 XMLName: xml.Name{Space: "x:", Local: "boat"}, 881 InnerXML: []byte("pea-green"), 882 } 883 p1 := Property{ 884 XMLName: xml.Name{Space: "x:", Local: "ring"}, 885 InnerXML: []byte("1 shilling"), 886 } 887 p2 := Property{ 888 XMLName: xml.Name{Space: "x:", Local: "spoon"}, 889 InnerXML: []byte("runcible"), 890 } 891 p3 := Property{ 892 XMLName: xml.Name{Space: "x:", Local: "moon"}, 893 InnerXML: []byte("light"), 894 } 895 896 if err := create("/src"); err != nil { 897 t.Fatalf("create /src: %v", err) 898 } 899 if err := patch("/src", Proppatch{Props: []Property{p0, p1}}); err != nil { 900 t.Fatalf("patch /src +p0 +p1: %v", err) 901 } 902 if _, err := copyFiles(fs, "/src", "/tmp", true, infiniteDepth, 0); err != nil { 903 t.Fatalf("copyFiles /src /tmp: %v", err) 904 } 905 if _, err := moveFiles(fs, "/tmp", "/dst", true); err != nil { 906 t.Fatalf("moveFiles /tmp /dst: %v", err) 907 } 908 if err := patch("/src", Proppatch{Props: []Property{p0}, Remove: true}); err != nil { 909 t.Fatalf("patch /src -p0: %v", err) 910 } 911 if err := patch("/src", Proppatch{Props: []Property{p2}}); err != nil { 912 t.Fatalf("patch /src +p2: %v", err) 913 } 914 if err := patch("/dst", Proppatch{Props: []Property{p1}, Remove: true}); err != nil { 915 t.Fatalf("patch /dst -p1: %v", err) 916 } 917 if err := patch("/dst", Proppatch{Props: []Property{p3}}); err != nil { 918 t.Fatalf("patch /dst +p3: %v", err) 919 } 920 921 gotSrc, err := props("/src") 922 if err != nil { 923 t.Fatalf("props /src: %v", err) 924 } 925 wantSrc := map[xml.Name]Property{ 926 p1.XMLName: p1, 927 p2.XMLName: p2, 928 } 929 if !reflect.DeepEqual(gotSrc, wantSrc) { 930 t.Fatalf("props /src:\ngot %v\nwant %v", gotSrc, wantSrc) 931 } 932 933 gotDst, err := props("/dst") 934 if err != nil { 935 t.Fatalf("props /dst: %v", err) 936 } 937 wantDst := map[xml.Name]Property{ 938 p0.XMLName: p0, 939 p3.XMLName: p3, 940 } 941 if !reflect.DeepEqual(gotDst, wantDst) { 942 t.Fatalf("props /dst:\ngot %v\nwant %v", gotDst, wantDst) 943 } 944 } 945 946 func TestWalkFS(t *testing.T) { 947 testCases := []struct { 948 desc string 949 buildfs []string 950 startAt string 951 depth int 952 walkFn filepath.WalkFunc 953 want []string 954 }{{ 955 "just root", 956 []string{}, 957 "/", 958 infiniteDepth, 959 nil, 960 []string{ 961 "/", 962 }, 963 }, { 964 "infinite walk from root", 965 []string{ 966 "mkdir /a", 967 "mkdir /a/b", 968 "touch /a/b/c", 969 "mkdir /a/d", 970 "mkdir /e", 971 "touch /f", 972 }, 973 "/", 974 infiniteDepth, 975 nil, 976 []string{ 977 "/", 978 "/a", 979 "/a/b", 980 "/a/b/c", 981 "/a/d", 982 "/e", 983 "/f", 984 }, 985 }, { 986 "infinite walk from subdir", 987 []string{ 988 "mkdir /a", 989 "mkdir /a/b", 990 "touch /a/b/c", 991 "mkdir /a/d", 992 "mkdir /e", 993 "touch /f", 994 }, 995 "/a", 996 infiniteDepth, 997 nil, 998 []string{ 999 "/a", 1000 "/a/b", 1001 "/a/b/c", 1002 "/a/d", 1003 }, 1004 }, { 1005 "depth 1 walk from root", 1006 []string{ 1007 "mkdir /a", 1008 "mkdir /a/b", 1009 "touch /a/b/c", 1010 "mkdir /a/d", 1011 "mkdir /e", 1012 "touch /f", 1013 }, 1014 "/", 1015 1, 1016 nil, 1017 []string{ 1018 "/", 1019 "/a", 1020 "/e", 1021 "/f", 1022 }, 1023 }, { 1024 "depth 1 walk from subdir", 1025 []string{ 1026 "mkdir /a", 1027 "mkdir /a/b", 1028 "touch /a/b/c", 1029 "mkdir /a/b/g", 1030 "mkdir /a/b/g/h", 1031 "touch /a/b/g/i", 1032 "touch /a/b/g/h/j", 1033 }, 1034 "/a/b", 1035 1, 1036 nil, 1037 []string{ 1038 "/a/b", 1039 "/a/b/c", 1040 "/a/b/g", 1041 }, 1042 }, { 1043 "depth 0 walk from subdir", 1044 []string{ 1045 "mkdir /a", 1046 "mkdir /a/b", 1047 "touch /a/b/c", 1048 "mkdir /a/b/g", 1049 "mkdir /a/b/g/h", 1050 "touch /a/b/g/i", 1051 "touch /a/b/g/h/j", 1052 }, 1053 "/a/b", 1054 0, 1055 nil, 1056 []string{ 1057 "/a/b", 1058 }, 1059 }, { 1060 "infinite walk from file", 1061 []string{ 1062 "mkdir /a", 1063 "touch /a/b", 1064 "touch /a/c", 1065 }, 1066 "/a/b", 1067 0, 1068 nil, 1069 []string{ 1070 "/a/b", 1071 }, 1072 }, { 1073 "infinite walk with skipped subdir", 1074 []string{ 1075 "mkdir /a", 1076 "mkdir /a/b", 1077 "touch /a/b/c", 1078 "mkdir /a/b/g", 1079 "mkdir /a/b/g/h", 1080 "touch /a/b/g/i", 1081 "touch /a/b/g/h/j", 1082 "touch /a/b/z", 1083 }, 1084 "/", 1085 infiniteDepth, 1086 func(path string, info os.FileInfo, err error) error { 1087 if path == "/a/b/g" { 1088 return filepath.SkipDir 1089 } 1090 return nil 1091 }, 1092 []string{ 1093 "/", 1094 "/a", 1095 "/a/b", 1096 "/a/b/c", 1097 "/a/b/z", 1098 }, 1099 }} 1100 for _, tc := range testCases { 1101 fs, err := buildTestFS(tc.buildfs) 1102 if err != nil { 1103 t.Fatalf("%s: cannot create test filesystem: %v", tc.desc, err) 1104 } 1105 var got []string 1106 traceFn := func(path string, info os.FileInfo, err error) error { 1107 if tc.walkFn != nil { 1108 err = tc.walkFn(path, info, err) 1109 if err != nil { 1110 return err 1111 } 1112 } 1113 got = append(got, path) 1114 return nil 1115 } 1116 fi, err := fs.Stat(tc.startAt) 1117 if err != nil { 1118 t.Fatalf("%s: cannot stat: %v", tc.desc, err) 1119 } 1120 err = walkFS(fs, tc.depth, tc.startAt, fi, traceFn) 1121 if err != nil { 1122 t.Errorf("%s:\ngot error %v, want nil", tc.desc, err) 1123 continue 1124 } 1125 sort.Strings(got) 1126 sort.Strings(tc.want) 1127 if !reflect.DeepEqual(got, tc.want) { 1128 t.Errorf("%s:\ngot %q\nwant %q", tc.desc, got, tc.want) 1129 continue 1130 } 1131 } 1132 } 1133 1134 func buildTestFS(buildfs []string) (FileSystem, error) { 1135 // TODO: Could this be merged with the build logic in TestFS? 1136 1137 fs := NewMemFS() 1138 for _, b := range buildfs { 1139 op := strings.Split(b, " ") 1140 switch op[0] { 1141 case "mkdir": 1142 err := fs.Mkdir(op[1], os.ModeDir|0777) 1143 if err != nil { 1144 return nil, err 1145 } 1146 case "touch": 1147 f, err := fs.OpenFile(op[1], os.O_RDWR|os.O_CREATE, 0666) 1148 if err != nil { 1149 return nil, err 1150 } 1151 f.Close() 1152 case "write": 1153 f, err := fs.OpenFile(op[1], os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 1154 if err != nil { 1155 return nil, err 1156 } 1157 _, err = f.Write([]byte(op[2])) 1158 f.Close() 1159 if err != nil { 1160 return nil, err 1161 } 1162 default: 1163 return nil, fmt.Errorf("unknown file operation %q", op[0]) 1164 } 1165 } 1166 return fs, nil 1167 }