github.com/IBM/fsgo@v0.0.0-20220920202152-e16fd2119d49/memmap_test.go (about) 1 // Copyright 2022 IBM Inc. All rights reserved 2 // Copyright © 2014 Steve Francia <spf@spf13.com> 3 // 4 // SPDX-License-Identifier: Apache2.0 5 package fsgo 6 7 import ( 8 "fmt" 9 "io" 10 "os" 11 "path/filepath" 12 "runtime" 13 "testing" 14 "time" 15 ) 16 17 func TestNormalizePath(t *testing.T) { 18 type test struct { 19 input string 20 expected string 21 } 22 23 data := []test{ 24 {".", FilePathSeparator}, 25 {"./", FilePathSeparator}, 26 {"..", FilePathSeparator}, 27 {"../", FilePathSeparator}, 28 {"./..", FilePathSeparator}, 29 {"./../", FilePathSeparator}, 30 } 31 32 for i, d := range data { 33 cpath := normalizePath(d.input) 34 if d.expected != cpath { 35 t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, cpath) 36 } 37 } 38 } 39 40 func TestPathErrors(t *testing.T) { 41 path := filepath.Join(".", "some", "path") 42 path2 := filepath.Join(".", "different", "path") 43 fs := NewMemMapFs() 44 perm := os.FileMode(0755) 45 uid := 1000 46 gid := 1000 47 48 // relevant functions: 49 // func (m *MemMapFs) Chmod(name string, mode os.FileMode) error 50 // func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error 51 // func (m *MemMapFs) Create(name string) (File, error) 52 // func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error 53 // func (m *MemMapFs) MkdirAll(path string, perm os.FileMode) error 54 // func (m *MemMapFs) Open(name string) (File, error) 55 // func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) 56 // func (m *MemMapFs) Remove(name string) error 57 // func (m *MemMapFs) Rename(oldname, newname string) error 58 // func (m *MemMapFs) Stat(name string) (os.FileInfo, error) 59 60 err := fs.Chmod(path, perm) 61 checkPathError(t, err, "Chmod") 62 63 err = fs.Chown(path, uid, gid) 64 checkPathError(t, err, "Chown") 65 66 err = fs.Chtimes(path, time.Now(), time.Now()) 67 checkPathError(t, err, "Chtimes") 68 69 // fs.Create doesn't return an error 70 71 err = fs.Mkdir(path2, perm) 72 if err != nil { 73 t.Error(err) 74 } 75 err = fs.Mkdir(path2, perm) 76 checkPathError(t, err, "Mkdir") 77 78 err = fs.MkdirAll(path2, perm) 79 if err != nil { 80 t.Error("MkdirAll:", err) 81 } 82 83 _, err = fs.Open(path) 84 checkPathError(t, err, "Open") 85 86 _, err = fs.OpenFile(path, os.O_RDWR, perm) 87 checkPathError(t, err, "OpenFile") 88 89 err = fs.Remove(path) 90 checkPathError(t, err, "Remove") 91 92 err = fs.RemoveAll(path) 93 if err != nil { 94 t.Error("RemoveAll:", err) 95 } 96 97 err = fs.Rename(path, path2) 98 checkPathError(t, err, "Rename") 99 100 _, err = fs.Stat(path) 101 checkPathError(t, err, "Stat") 102 } 103 104 func checkPathError(t *testing.T, err error, op string) { 105 pathErr, ok := err.(*os.PathError) 106 if !ok { 107 t.Error(op+":", err, "is not a os.PathError") 108 return 109 } 110 _, ok = pathErr.Err.(*os.PathError) 111 if ok { 112 t.Error(op+":", err, "contains another os.PathError") 113 } 114 } 115 116 // Ensure os.O_EXCL is correctly handled. 117 func TestOpenFileExcl(t *testing.T) { 118 const fileName = "/myFileTest" 119 const fileMode = os.FileMode(0765) 120 121 fs := NewMemMapFs() 122 123 // First creation should succeed. 124 f, err := fs.OpenFile(fileName, os.O_CREATE|os.O_EXCL, fileMode) 125 if err != nil { 126 t.Errorf("OpenFile Create Excl failed: %s", err) 127 return 128 } 129 f.Close() 130 131 // Second creation should fail. 132 _, err = fs.OpenFile(fileName, os.O_CREATE|os.O_EXCL, fileMode) 133 if err == nil { 134 t.Errorf("OpenFile Create Excl should have failed, but it didn't") 135 } 136 checkPathError(t, err, "Open") 137 } 138 139 // Ensure Permissions are set on OpenFile/Mkdir/MkdirAll 140 func TestPermSet(t *testing.T) { 141 const fileName = "/myFileTest" 142 const dirPath = "/myDirTest" 143 const dirPathAll = "/my/path/to/dir" 144 145 const fileMode = os.FileMode(0765) 146 // directories will also have the directory bit set 147 const dirMode = fileMode | os.ModeDir 148 149 fs := NewMemMapFs() 150 151 // Test Openfile 152 f, err := fs.OpenFile(fileName, os.O_CREATE, fileMode) 153 if err != nil { 154 t.Errorf("OpenFile Create failed: %s", err) 155 return 156 } 157 f.Close() 158 159 s, err := fs.Stat(fileName) 160 if err != nil { 161 t.Errorf("Stat failed: %s", err) 162 return 163 } 164 if s.Mode().String() != fileMode.String() { 165 t.Errorf("Permissions Incorrect: %s != %s", s.Mode().String(), fileMode.String()) 166 return 167 } 168 169 // Test Mkdir 170 err = fs.Mkdir(dirPath, dirMode) 171 if err != nil { 172 t.Errorf("MkDir Create failed: %s", err) 173 return 174 } 175 s, err = fs.Stat(dirPath) 176 if err != nil { 177 t.Errorf("Stat failed: %s", err) 178 return 179 } 180 // sets File 181 if s.Mode().String() != dirMode.String() { 182 t.Errorf("Permissions Incorrect: %s != %s", s.Mode().String(), dirMode.String()) 183 return 184 } 185 186 // Test MkdirAll 187 err = fs.MkdirAll(dirPathAll, dirMode) 188 if err != nil { 189 t.Errorf("MkDir Create failed: %s", err) 190 return 191 } 192 s, err = fs.Stat(dirPathAll) 193 if err != nil { 194 t.Errorf("Stat failed: %s", err) 195 return 196 } 197 if s.Mode().String() != dirMode.String() { 198 t.Errorf("Permissions Incorrect: %s != %s", s.Mode().String(), dirMode.String()) 199 return 200 } 201 } 202 203 // Fails if multiple file objects use the same file.at counter in MemMapFs 204 func TestMultipleOpenFiles(t *testing.T) { 205 defer removeAllTestFiles(t) 206 const fileName = "fsgo-demo2.txt" 207 208 var data = make([][]byte, len(Fss)) 209 210 for i, fs := range Fss { 211 dir := testDir(fs) 212 path := filepath.Join(dir, fileName) 213 fh1, err := fs.Create(path) 214 if err != nil { 215 t.Error("fs.Create failed: " + err.Error()) 216 } 217 _, err = fh1.Write([]byte("test")) 218 if err != nil { 219 t.Error("fh.Write failed: " + err.Error()) 220 } 221 _, err = fh1.Seek(0, io.SeekStart) 222 if err != nil { 223 t.Error(err) 224 } 225 226 fh2, err := fs.OpenFile(path, os.O_RDWR, 0777) 227 if err != nil { 228 t.Error("fs.OpenFile failed: " + err.Error()) 229 } 230 _, err = fh2.Seek(0, os.SEEK_END) 231 if err != nil { 232 t.Error(err) 233 } 234 _, err = fh2.Write([]byte("data")) 235 if err != nil { 236 t.Error(err) 237 } 238 err = fh2.Close() 239 if err != nil { 240 t.Error(err) 241 } 242 243 _, err = fh1.Write([]byte("data")) 244 if err != nil { 245 t.Error(err) 246 } 247 err = fh1.Close() 248 if err != nil { 249 t.Error(err) 250 } 251 // the file now should contain "datadata" 252 data[i], err = ReadFile(fs, path) 253 if err != nil { 254 t.Error(err) 255 } 256 } 257 258 for i, fs := range Fss { 259 if i == 0 { 260 continue 261 } 262 if string(data[0]) != string(data[i]) { 263 t.Errorf("%s and %s don't behave the same\n"+ 264 "%s: \"%s\"\n%s: \"%s\"\n", 265 Fss[0].Name(), fs.Name(), Fss[0].Name(), data[0], fs.Name(), data[i]) 266 } 267 } 268 } 269 270 // Test if file.Write() fails when opened as read only 271 func TestReadOnly(t *testing.T) { 272 defer removeAllTestFiles(t) 273 const fileName = "fsgo-demo.txt" 274 275 for _, fs := range Fss { 276 dir := testDir(fs) 277 path := filepath.Join(dir, fileName) 278 279 f, err := fs.Create(path) 280 if err != nil { 281 t.Error(fs.Name()+":", "fs.Create failed: "+err.Error()) 282 } 283 _, err = f.Write([]byte("test")) 284 if err != nil { 285 t.Error(fs.Name()+":", "Write failed: "+err.Error()) 286 } 287 f.Close() 288 289 f, err = fs.Open(path) 290 if err != nil { 291 t.Error("fs.Open failed: " + err.Error()) 292 } 293 _, err = f.Write([]byte("data")) 294 if err == nil { 295 t.Error(fs.Name()+":", "No write error") 296 } 297 f.Close() 298 299 f, err = fs.OpenFile(path, os.O_RDONLY, 0644) 300 if err != nil { 301 t.Error("fs.Open failed: " + err.Error()) 302 } 303 _, err = f.Write([]byte("data")) 304 if err == nil { 305 t.Error(fs.Name()+":", "No write error") 306 } 307 f.Close() 308 } 309 } 310 311 func TestWriteCloseTime(t *testing.T) { 312 defer removeAllTestFiles(t) 313 const fileName = "fsgo-demo.txt" 314 315 for _, fs := range Fss { 316 dir := testDir(fs) 317 path := filepath.Join(dir, fileName) 318 319 f, err := fs.Create(path) 320 if err != nil { 321 t.Error(fs.Name()+":", "fs.Create failed: "+err.Error()) 322 } 323 f.Close() 324 325 f, err = fs.Create(path) 326 if err != nil { 327 t.Error(fs.Name()+":", "fs.Create failed: "+err.Error()) 328 } 329 fi, err := f.Stat() 330 if err != nil { 331 t.Error(fs.Name()+":", "Stat failed: "+err.Error()) 332 } 333 timeBefore := fi.ModTime() 334 335 // sorry for the delay, but we have to make sure time advances, 336 // also on non Un*x systems... 337 switch runtime.GOOS { 338 case "windows": 339 time.Sleep(2 * time.Second) 340 case "darwin": 341 time.Sleep(1 * time.Second) 342 default: // depending on the FS, this may work with < 1 second, on my old ext3 it does not 343 time.Sleep(1 * time.Second) 344 } 345 346 _, err = f.Write([]byte("test")) 347 if err != nil { 348 t.Error(fs.Name()+":", "Write failed: "+err.Error()) 349 } 350 f.Close() 351 fi, err = fs.Stat(path) 352 if err != nil { 353 t.Error(fs.Name()+":", "fs.Stat failed: "+err.Error()) 354 } 355 if fi.ModTime().Equal(timeBefore) { 356 t.Error(fs.Name()+":", "ModTime was not set on Close()") 357 } 358 } 359 } 360 361 // This test should be run with the race detector on: 362 // go test -race -v -timeout 10s -run TestRacingDeleteAndClose 363 func TestRacingDeleteAndClose(t *testing.T) { 364 fs := NewMemMapFs() 365 pathname := "testfile" 366 f, err := fs.Create(pathname) 367 if err != nil { 368 t.Fatal(err) 369 } 370 371 in := make(chan bool) 372 373 go func() { 374 <-in 375 f.Close() 376 }() 377 go func() { 378 <-in 379 fs.Remove(pathname) 380 }() 381 close(in) 382 } 383 384 // This test should be run with the race detector on: 385 // go test -run TestMemFsDataRace -race 386 func TestMemFsDataRace(t *testing.T) { 387 const dir = "test_dir" 388 fs := NewMemMapFs() 389 390 if err := fs.MkdirAll(dir, 0777); err != nil { 391 t.Fatal(err) 392 } 393 394 const n = 1000 395 done := make(chan struct{}) 396 397 go func() { 398 defer close(done) 399 for i := 0; i < n; i++ { 400 fname := filepath.Join(dir, fmt.Sprintf("%d.txt", i)) 401 if err := WriteFile(fs, fname, []byte(""), 0777); err != nil { 402 panic(err) 403 } 404 if err := fs.Remove(fname); err != nil { 405 panic(err) 406 } 407 } 408 }() 409 410 loop: 411 for { 412 select { 413 case <-done: 414 break loop 415 default: 416 _, err := ReadDir(fs, dir) 417 if err != nil { 418 t.Fatal(err) 419 } 420 } 421 } 422 } 423 424 // root is a directory 425 func TestMemFsRootDirMode(t *testing.T) { 426 t.Parallel() 427 428 fs := NewMemMapFs() 429 info, err := fs.Stat("/") 430 if err != nil { 431 t.Fatal(err) 432 } 433 if !info.IsDir() { 434 t.Error("should be a directory") 435 } 436 if !info.Mode().IsDir() { 437 t.Errorf("FileMode is not directory, is %s", info.Mode().String()) 438 } 439 } 440 441 // MkdirAll creates intermediate directories with correct mode 442 func TestMemFsMkdirAllMode(t *testing.T) { 443 t.Parallel() 444 445 fs := NewMemMapFs() 446 err := fs.MkdirAll("/a/b/c", 0755) 447 if err != nil { 448 t.Fatal(err) 449 } 450 info, err := fs.Stat("/a") 451 if err != nil { 452 t.Fatal(err) 453 } 454 if !info.Mode().IsDir() { 455 t.Error("/a: mode is not directory") 456 } 457 if !info.ModTime().After(time.Now().Add(-1 * time.Hour)) { 458 t.Errorf("/a: mod time not set, got %s", info.ModTime()) 459 } 460 if info.Mode() != os.FileMode(os.ModeDir|0755) { 461 t.Errorf("/a: wrong permissions, expected drwxr-xr-x, got %s", info.Mode()) 462 } 463 info, err = fs.Stat("/a/b") 464 if err != nil { 465 t.Fatal(err) 466 } 467 if !info.Mode().IsDir() { 468 t.Error("/a/b: mode is not directory") 469 } 470 if info.Mode() != os.FileMode(os.ModeDir|0755) { 471 t.Errorf("/a/b: wrong permissions, expected drwxr-xr-x, got %s", info.Mode()) 472 } 473 if !info.ModTime().After(time.Now().Add(-1 * time.Hour)) { 474 t.Errorf("/a/b: mod time not set, got %s", info.ModTime()) 475 } 476 info, err = fs.Stat("/a/b/c") 477 if err != nil { 478 t.Fatal(err) 479 } 480 if !info.Mode().IsDir() { 481 t.Error("/a/b/c: mode is not directory") 482 } 483 if info.Mode() != os.FileMode(os.ModeDir|0755) { 484 t.Errorf("/a/b/c: wrong permissions, expected drwxr-xr-x, got %s", info.Mode()) 485 } 486 if !info.ModTime().After(time.Now().Add(-1 * time.Hour)) { 487 t.Errorf("/a/b/c: mod time not set, got %s", info.ModTime()) 488 } 489 } 490 491 // MkdirAll does not change permissions of already-existing directories 492 func TestMemFsMkdirAllNoClobber(t *testing.T) { 493 t.Parallel() 494 495 fs := NewMemMapFs() 496 err := fs.MkdirAll("/a/b/c", 0755) 497 if err != nil { 498 t.Fatal(err) 499 } 500 info, err := fs.Stat("/a/b") 501 if err != nil { 502 t.Fatal(err) 503 } 504 if info.Mode() != os.FileMode(os.ModeDir|0755) { 505 t.Errorf("/a/b: wrong permissions, expected drwxr-xr-x, got %s", info.Mode()) 506 } 507 err = fs.MkdirAll("/a/b/c/d/e/f", 0710) 508 // '/a/b' is unchanged 509 if err != nil { 510 t.Fatal(err) 511 } 512 info, err = fs.Stat("/a/b") 513 if err != nil { 514 t.Fatal(err) 515 } 516 if info.Mode() != os.FileMode(os.ModeDir|0755) { 517 t.Errorf("/a/b: wrong permissions, expected drwxr-xr-x, got %s", info.Mode()) 518 } 519 // new directories created with proper permissions 520 info, err = fs.Stat("/a/b/c/d") 521 if err != nil { 522 t.Fatal(err) 523 } 524 if info.Mode() != os.FileMode(os.ModeDir|0710) { 525 t.Errorf("/a/b/c/d: wrong permissions, expected drwx--x---, got %s", info.Mode()) 526 } 527 info, err = fs.Stat("/a/b/c/d/e") 528 if err != nil { 529 t.Fatal(err) 530 } 531 if info.Mode() != os.FileMode(os.ModeDir|0710) { 532 t.Errorf("/a/b/c/d/e: wrong permissions, expected drwx--x---, got %s", info.Mode()) 533 } 534 info, err = fs.Stat("/a/b/c/d/e/f") 535 if err != nil { 536 t.Fatal(err) 537 } 538 if info.Mode() != os.FileMode(os.ModeDir|0710) { 539 t.Errorf("/a/b/c/d/e/f: wrong permissions, expected drwx--x---, got %s", info.Mode()) 540 } 541 } 542 543 func TestMemFsDirMode(t *testing.T) { 544 fs := NewMemMapFs() 545 err := fs.Mkdir("/testDir1", 0644) 546 if err != nil { 547 t.Error(err) 548 } 549 err = fs.MkdirAll("/sub/testDir2", 0644) 550 if err != nil { 551 t.Error(err) 552 } 553 info, err := fs.Stat("/testDir1") 554 if err != nil { 555 t.Error(err) 556 } 557 if !info.IsDir() { 558 t.Error("should be a directory") 559 } 560 if !info.Mode().IsDir() { 561 t.Error("FileMode is not directory") 562 } 563 info, err = fs.Stat("/sub/testDir2") 564 if err != nil { 565 t.Error(err) 566 } 567 if !info.IsDir() { 568 t.Error("should be a directory") 569 } 570 if !info.Mode().IsDir() { 571 t.Error("FileMode is not directory") 572 } 573 } 574 575 func TestMemFsUnexpectedEOF(t *testing.T) { 576 t.Parallel() 577 578 fs := NewMemMapFs() 579 580 if err := WriteFile(fs, "file.txt", []byte("abc"), 0777); err != nil { 581 t.Fatal(err) 582 } 583 584 f, err := fs.Open("file.txt") 585 if err != nil { 586 t.Fatal(err) 587 } 588 defer f.Close() 589 590 // Seek beyond the end. 591 _, err = f.Seek(512, 0) 592 if err != nil { 593 t.Fatal(err) 594 } 595 596 buff := make([]byte, 256) 597 _, err = io.ReadAtLeast(f, buff, 256) 598 599 if err != io.ErrUnexpectedEOF { 600 t.Fatal("Expected ErrUnexpectedEOF") 601 } 602 } 603 604 func TestMemFsChmod(t *testing.T) { 605 t.Parallel() 606 607 fs := NewMemMapFs() 608 const file = "hello" 609 if err := fs.Mkdir(file, 0700); err != nil { 610 t.Fatal(err) 611 } 612 613 info, err := fs.Stat(file) 614 if err != nil { 615 t.Fatal(err) 616 } 617 if info.Mode().String() != "drwx------" { 618 t.Fatal("mkdir failed to create a directory: mode =", info.Mode()) 619 } 620 621 err = fs.Chmod(file, 0) 622 if err != nil { 623 t.Error("Failed to run chmod:", err) 624 } 625 626 info, err = fs.Stat(file) 627 if err != nil { 628 t.Fatal(err) 629 } 630 if info.Mode().String() != "d---------" { 631 t.Error("chmod should not change file type. New mode =", info.Mode()) 632 } 633 } 634 635 // can't use Mkdir to get around which permissions we're allowed to set 636 func TestMemFsMkdirModeIllegal(t *testing.T) { 637 t.Parallel() 638 639 fs := NewMemMapFs() 640 err := fs.Mkdir("/a", os.ModeSocket|0755) 641 if err != nil { 642 t.Fatal(err) 643 } 644 info, err := fs.Stat("/a") 645 if err != nil { 646 t.Fatal(err) 647 } 648 if info.Mode() != os.FileMode(os.ModeDir|0755) { 649 t.Fatalf("should not be able to use Mkdir to set illegal mode: %s", info.Mode().String()) 650 } 651 } 652 653 // can't use OpenFile to get around which permissions we're allowed to set 654 func TestMemFsOpenFileModeIllegal(t *testing.T) { 655 t.Parallel() 656 657 fs := NewMemMapFs() 658 file, err := fs.OpenFile("/a", os.O_CREATE, os.ModeSymlink|0644) 659 if err != nil { 660 t.Fatal(err) 661 } 662 defer file.Close() 663 info, err := fs.Stat("/a") 664 if err != nil { 665 t.Fatal(err) 666 } 667 if info.Mode() != os.FileMode(0644) { 668 t.Fatalf("should not be able to use OpenFile to set illegal mode: %s", info.Mode().String()) 669 } 670 } 671 672 // LstatIfPossible should always return false, since MemMapFs does not 673 // support symlinks. 674 func TestMemFsLstatIfPossible(t *testing.T) { 675 t.Parallel() 676 677 fs := NewMemMapFs() 678 679 // We assert that fs implements Lstater 680 fsAsserted, ok := fs.(Lstater) 681 if !ok { 682 t.Fatalf("The filesytem does not implement Lstater") 683 } 684 685 file, err := fs.OpenFile("/a.txt", os.O_CREATE, 0o644) 686 if err != nil { 687 t.Fatalf("Error when opening file: %v", err) 688 } 689 defer file.Close() 690 691 _, lstatCalled, err := fsAsserted.LstatIfPossible("/a.txt") 692 if err != nil { 693 t.Fatalf("Function returned err: %v", err) 694 } 695 if lstatCalled { 696 t.Fatalf("Function indicated lstat was called. This should never be true.") 697 } 698 }