github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/countlog/output/lumberjack/lumberjack_test.go (about) 1 package lumberjack 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "encoding/json" 7 "fmt" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "testing" 12 "time" 13 14 "github.com/BurntSushi/toml" 15 "gopkg.in/yaml.v2" 16 ) 17 18 // !!!NOTE!!! 19 // 20 // Running these tests in parallel will almost certainly cause sporadic (or even 21 // regular) failures, because they're all messing with the same global variable 22 // that controls the logic's mocked time.Now. So... don't do that. 23 24 // Since all the tests uses the time to determine filenames etc, we need to 25 // control the wall clock as much as possible, which means having a wall clock 26 // that doesn't change unless we want it to. 27 var fakeCurrentTime = time.Now() 28 29 func fakeTime() time.Time { 30 return fakeCurrentTime 31 } 32 33 func TestNewFile(t *testing.T) { 34 currentTime = fakeTime 35 36 dir := makeTempDir("TestNewFile", t) 37 defer os.RemoveAll(dir) 38 l := &Logger{ 39 Filename: logFile(dir), 40 } 41 defer l.Close() 42 b := []byte("boo!") 43 n, err := l.Write(b) 44 isNil(err, t) 45 equals(len(b), n, t) 46 existsWithContent(logFile(dir), b, t) 47 fileCount(dir, 1, t) 48 } 49 50 func TestOpenExisting(t *testing.T) { 51 currentTime = fakeTime 52 dir := makeTempDir("TestOpenExisting", t) 53 defer os.RemoveAll(dir) 54 55 filename := logFile(dir) 56 data := []byte("foo!") 57 err := ioutil.WriteFile(filename, data, 0644) 58 isNil(err, t) 59 existsWithContent(filename, data, t) 60 61 l := &Logger{ 62 Filename: filename, 63 } 64 defer l.Close() 65 b := []byte("boo!") 66 n, err := l.Write(b) 67 isNil(err, t) 68 equals(len(b), n, t) 69 70 // make sure the file got appended 71 existsWithContent(filename, append(data, b...), t) 72 73 // make sure no other files were created 74 fileCount(dir, 1, t) 75 } 76 77 func TestWriteTooLong(t *testing.T) { 78 currentTime = fakeTime 79 megabyte = 1 80 dir := makeTempDir("TestWriteTooLong", t) 81 defer os.RemoveAll(dir) 82 l := &Logger{ 83 Filename: logFile(dir), 84 MaxSize: 5, 85 } 86 defer l.Close() 87 b := []byte("booooooooooooooo!") 88 n, err := l.Write(b) 89 notNil(err, t) 90 equals(0, n, t) 91 equals(err.Error(), 92 fmt.Sprintf("write length %d exceeds maximum file size %d", len(b), l.MaxSize), t) 93 _, err = os.Stat(logFile(dir)) 94 assert(os.IsNotExist(err), t, "File exists, but should not have been created") 95 } 96 97 func TestMakeLogDir(t *testing.T) { 98 currentTime = fakeTime 99 dir := time.Now().Format("TestMakeLogDir" + backupTimeFormat) 100 dir = filepath.Join(os.TempDir(), dir) 101 defer os.RemoveAll(dir) 102 filename := logFile(dir) 103 l := &Logger{ 104 Filename: filename, 105 } 106 defer l.Close() 107 b := []byte("boo!") 108 n, err := l.Write(b) 109 isNil(err, t) 110 equals(len(b), n, t) 111 existsWithContent(logFile(dir), b, t) 112 fileCount(dir, 1, t) 113 } 114 115 func TestDefaultFilename(t *testing.T) { 116 currentTime = fakeTime 117 dir := os.TempDir() 118 filename := filepath.Join(dir, filepath.Base(os.Args[0])+"-lumberjack.log") 119 defer os.Remove(filename) 120 l := &Logger{} 121 defer l.Close() 122 b := []byte("boo!") 123 n, err := l.Write(b) 124 125 isNil(err, t) 126 equals(len(b), n, t) 127 existsWithContent(filename, b, t) 128 } 129 130 func TestAutoRotate(t *testing.T) { 131 currentTime = fakeTime 132 megabyte = 1 133 134 dir := makeTempDir("TestAutoRotate", t) 135 defer os.RemoveAll(dir) 136 137 filename := logFile(dir) 138 l := &Logger{ 139 Filename: filename, 140 MaxSize: 10, 141 } 142 defer l.Close() 143 b := []byte("boo!") 144 n, err := l.Write(b) 145 isNil(err, t) 146 equals(len(b), n, t) 147 148 existsWithContent(filename, b, t) 149 fileCount(dir, 1, t) 150 151 newFakeTime() 152 153 b2 := []byte("foooooo!") 154 n, err = l.Write(b2) 155 isNil(err, t) 156 equals(len(b2), n, t) 157 158 // the old logfile should be moved aside and the main logfile should have 159 // only the last write in it. 160 existsWithContent(filename, b2, t) 161 162 // the backup file will use the current fake time and have the old contents. 163 existsWithContent(backupFile(dir), b, t) 164 165 fileCount(dir, 2, t) 166 } 167 168 func TestFirstWriteRotate(t *testing.T) { 169 currentTime = fakeTime 170 megabyte = 1 171 dir := makeTempDir("TestFirstWriteRotate", t) 172 defer os.RemoveAll(dir) 173 174 filename := logFile(dir) 175 l := &Logger{ 176 Filename: filename, 177 MaxSize: 10, 178 } 179 defer l.Close() 180 181 start := []byte("boooooo!") 182 err := ioutil.WriteFile(filename, start, 0600) 183 isNil(err, t) 184 185 newFakeTime() 186 187 // this would make us rotate 188 b := []byte("fooo!") 189 n, err := l.Write(b) 190 isNil(err, t) 191 equals(len(b), n, t) 192 193 existsWithContent(filename, b, t) 194 existsWithContent(backupFile(dir), start, t) 195 196 fileCount(dir, 2, t) 197 } 198 199 func TestMaxBackups(t *testing.T) { 200 currentTime = fakeTime 201 megabyte = 1 202 dir := makeTempDir("TestMaxBackups", t) 203 defer os.RemoveAll(dir) 204 205 filename := logFile(dir) 206 l := &Logger{ 207 Filename: filename, 208 MaxSize: 10, 209 MaxBackups: 1, 210 } 211 defer l.Close() 212 b := []byte("boo!") 213 n, err := l.Write(b) 214 isNil(err, t) 215 equals(len(b), n, t) 216 217 existsWithContent(filename, b, t) 218 fileCount(dir, 1, t) 219 220 newFakeTime() 221 222 // this will put us over the max 223 b2 := []byte("foooooo!") 224 n, err = l.Write(b2) 225 isNil(err, t) 226 equals(len(b2), n, t) 227 228 // this will use the new fake time 229 secondFilename := backupFile(dir) 230 existsWithContent(secondFilename, b, t) 231 232 // make sure the old file still exists with the same content. 233 existsWithContent(filename, b2, t) 234 235 fileCount(dir, 2, t) 236 237 newFakeTime() 238 239 // this will make us rotate again 240 b3 := []byte("baaaaaar!") 241 n, err = l.Write(b3) 242 isNil(err, t) 243 equals(len(b3), n, t) 244 245 // this will use the new fake time 246 thirdFilename := backupFile(dir) 247 existsWithContent(thirdFilename, b2, t) 248 249 existsWithContent(filename, b3, t) 250 251 // we need to wait a little bit since the files get deleted on a different 252 // goroutine. 253 <-time.After(time.Millisecond * 10) 254 255 // should only have two files in the dir still 256 fileCount(dir, 2, t) 257 258 // second file name should still exist 259 existsWithContent(thirdFilename, b2, t) 260 261 // should have deleted the first backup 262 notExist(secondFilename, t) 263 264 // now test that we don't delete directories or non-logfile files 265 266 newFakeTime() 267 268 // create a file that is close to but different from the logfile name. 269 // It shouldn't get caught by our deletion filters. 270 notlogfile := logFile(dir) + ".foo" 271 err = ioutil.WriteFile(notlogfile, []byte("data"), 0644) 272 isNil(err, t) 273 274 // Make a directory that exactly matches our log file filters... it still 275 // shouldn't get caught by the deletion filter since it's a directory. 276 notlogfiledir := backupFile(dir) 277 err = os.Mkdir(notlogfiledir, 0700) 278 isNil(err, t) 279 280 newFakeTime() 281 282 // this will use the new fake time 283 fourthFilename := backupFile(dir) 284 285 // Create a log file that is/was being compressed - this should 286 // not be counted since both the compressed and the uncompressed 287 // log files still exist. 288 compLogFile := fourthFilename+compressSuffix 289 err = ioutil.WriteFile(compLogFile, []byte("compress"), 0644) 290 isNil(err, t) 291 292 // this will make us rotate again 293 b4 := []byte("baaaaaaz!") 294 n, err = l.Write(b4) 295 isNil(err, t) 296 equals(len(b4), n, t) 297 298 existsWithContent(fourthFilename, b3, t) 299 existsWithContent(fourthFilename+compressSuffix, []byte("compress"), t) 300 301 // we need to wait a little bit since the files get deleted on a different 302 // goroutine. 303 <-time.After(time.Millisecond * 10) 304 305 // We should have four things in the directory now - the 2 log files, the 306 // not log file, and the directory 307 fileCount(dir, 5, t) 308 309 // third file name should still exist 310 existsWithContent(filename, b4, t) 311 312 existsWithContent(fourthFilename, b3, t) 313 314 // should have deleted the first filename 315 notExist(thirdFilename, t) 316 317 // the not-a-logfile should still exist 318 exists(notlogfile, t) 319 320 // the directory 321 exists(notlogfiledir, t) 322 } 323 324 func TestCleanupExistingBackups(t *testing.T) { 325 // test that if we start with more backup files than we're supposed to have 326 // in total, that extra ones get cleaned up when we rotate. 327 328 currentTime = fakeTime 329 megabyte = 1 330 331 dir := makeTempDir("TestCleanupExistingBackups", t) 332 defer os.RemoveAll(dir) 333 334 // make 3 backup files 335 336 data := []byte("data") 337 backup := backupFile(dir) 338 err := ioutil.WriteFile(backup, data, 0644) 339 isNil(err, t) 340 341 newFakeTime() 342 343 backup = backupFile(dir) 344 err = ioutil.WriteFile(backup+compressSuffix, data, 0644) 345 isNil(err, t) 346 347 newFakeTime() 348 349 backup = backupFile(dir) 350 err = ioutil.WriteFile(backup, data, 0644) 351 isNil(err, t) 352 353 // now create a primary log file with some data 354 filename := logFile(dir) 355 err = ioutil.WriteFile(filename, data, 0644) 356 isNil(err, t) 357 358 l := &Logger{ 359 Filename: filename, 360 MaxSize: 10, 361 MaxBackups: 1, 362 } 363 defer l.Close() 364 365 newFakeTime() 366 367 b2 := []byte("foooooo!") 368 n, err := l.Write(b2) 369 isNil(err, t) 370 equals(len(b2), n, t) 371 372 // we need to wait a little bit since the files get deleted on a different 373 // goroutine. 374 <-time.After(time.Millisecond * 10) 375 376 // now we should only have 2 files left - the primary and one backup 377 fileCount(dir, 2, t) 378 } 379 380 func TestMaxAge(t *testing.T) { 381 currentTime = fakeTime 382 megabyte = 1 383 384 dir := makeTempDir("TestMaxAge", t) 385 defer os.RemoveAll(dir) 386 387 filename := logFile(dir) 388 l := &Logger{ 389 Filename: filename, 390 MaxSize: 10, 391 MaxAge: 1, 392 } 393 defer l.Close() 394 b := []byte("boo!") 395 n, err := l.Write(b) 396 isNil(err, t) 397 equals(len(b), n, t) 398 399 existsWithContent(filename, b, t) 400 fileCount(dir, 1, t) 401 402 // two days later 403 newFakeTime() 404 405 b2 := []byte("foooooo!") 406 n, err = l.Write(b2) 407 isNil(err, t) 408 equals(len(b2), n, t) 409 existsWithContent(backupFile(dir), b, t) 410 411 // we need to wait a little bit since the files get deleted on a different 412 // goroutine. 413 <-time.After(10 * time.Millisecond) 414 415 // We should still have 2 log files, since the most recent backup was just 416 // created. 417 fileCount(dir, 2, t) 418 419 existsWithContent(filename, b2, t) 420 421 // we should have deleted the old file due to being too old 422 existsWithContent(backupFile(dir), b, t) 423 424 // two days later 425 newFakeTime() 426 427 b3 := []byte("baaaaar!") 428 n, err = l.Write(b3) 429 isNil(err, t) 430 equals(len(b3), n, t) 431 existsWithContent(backupFile(dir), b2, t) 432 433 // we need to wait a little bit since the files get deleted on a different 434 // goroutine. 435 <-time.After(10 * time.Millisecond) 436 437 // We should have 2 log files - the main log file, and the most recent 438 // backup. The earlier backup is past the cutoff and should be gone. 439 fileCount(dir, 2, t) 440 441 existsWithContent(filename, b3, t) 442 443 // we should have deleted the old file due to being too old 444 existsWithContent(backupFile(dir), b2, t) 445 } 446 447 func TestOldLogFiles(t *testing.T) { 448 currentTime = fakeTime 449 megabyte = 1 450 451 dir := makeTempDir("TestOldLogFiles", t) 452 defer os.RemoveAll(dir) 453 454 filename := logFile(dir) 455 data := []byte("data") 456 err := ioutil.WriteFile(filename, data, 07) 457 isNil(err, t) 458 459 // This gives us a time with the same precision as the time we get from the 460 // timestamp in the name. 461 t1, err := time.Parse(backupTimeFormat, fakeTime().UTC().Format(backupTimeFormat)) 462 isNil(err, t) 463 464 backup := backupFile(dir) 465 err = ioutil.WriteFile(backup, data, 07) 466 isNil(err, t) 467 468 newFakeTime() 469 470 t2, err := time.Parse(backupTimeFormat, fakeTime().UTC().Format(backupTimeFormat)) 471 isNil(err, t) 472 473 backup2 := backupFile(dir) 474 err = ioutil.WriteFile(backup2, data, 07) 475 isNil(err, t) 476 477 l := &Logger{Filename: filename} 478 files, err := l.oldLogFiles() 479 isNil(err, t) 480 equals(2, len(files), t) 481 482 // should be sorted by newest file first, which would be t2 483 equals(t2, files[0].timestamp, t) 484 equals(t1, files[1].timestamp, t) 485 } 486 487 func TestTimeFromName(t *testing.T) { 488 l := &Logger{Filename: "/var/log/myfoo/foo.log"} 489 prefix, ext := l.prefixAndExt() 490 491 tests := []struct { 492 filename string 493 want time.Time 494 wantErr bool 495 }{ 496 {"foo-2014-05-04T14-44-33.555.log", time.Date(2014, 5, 4, 14, 44, 33, 555000000, time.UTC), false}, 497 {"foo-2014-05-04T14-44-33.555", time.Time{}, true}, 498 {"2014-05-04T14-44-33.555.log", time.Time{}, true}, 499 {"foo.log", time.Time{}, true}, 500 } 501 502 for _, test := range tests { 503 got, err := l.timeFromName(test.filename, prefix, ext) 504 equals(got, test.want, t) 505 equals(err != nil, test.wantErr, t) 506 } 507 } 508 509 func TestLocalTime(t *testing.T) { 510 currentTime = fakeTime 511 megabyte = 1 512 513 dir := makeTempDir("TestLocalTime", t) 514 defer os.RemoveAll(dir) 515 516 l := &Logger{ 517 Filename: logFile(dir), 518 MaxSize: 10, 519 LocalTime: true, 520 } 521 defer l.Close() 522 b := []byte("boo!") 523 n, err := l.Write(b) 524 isNil(err, t) 525 equals(len(b), n, t) 526 527 b2 := []byte("fooooooo!") 528 n2, err := l.Write(b2) 529 isNil(err, t) 530 equals(len(b2), n2, t) 531 532 existsWithContent(logFile(dir), b2, t) 533 existsWithContent(backupFileLocal(dir), b, t) 534 } 535 536 func TestRotate(t *testing.T) { 537 currentTime = fakeTime 538 dir := makeTempDir("TestRotate", t) 539 defer os.RemoveAll(dir) 540 541 filename := logFile(dir) 542 543 l := &Logger{ 544 Filename: filename, 545 MaxBackups: 1, 546 MaxSize: 100, // megabytes 547 } 548 defer l.Close() 549 b := []byte("boo!") 550 n, err := l.Write(b) 551 isNil(err, t) 552 equals(len(b), n, t) 553 554 existsWithContent(filename, b, t) 555 fileCount(dir, 1, t) 556 557 newFakeTime() 558 559 err = l.Rotate() 560 isNil(err, t) 561 562 // we need to wait a little bit since the files get deleted on a different 563 // goroutine. 564 <-time.After(10 * time.Millisecond) 565 566 filename2 := backupFile(dir) 567 existsWithContent(filename2, b, t) 568 existsWithContent(filename, []byte{}, t) 569 fileCount(dir, 2, t) 570 newFakeTime() 571 572 err = l.Rotate() 573 isNil(err, t) 574 575 // we need to wait a little bit since the files get deleted on a different 576 // goroutine. 577 <-time.After(10 * time.Millisecond) 578 579 filename3 := backupFile(dir) 580 existsWithContent(filename3, []byte{}, t) 581 existsWithContent(filename, []byte{}, t) 582 fileCount(dir, 2, t) 583 584 b2 := []byte("foooooo!") 585 n, err = l.Write(b2) 586 isNil(err, t) 587 equals(len(b2), n, t) 588 589 // this will use the new fake time 590 existsWithContent(filename, b2, t) 591 } 592 593 func TestCompressOnRotate(t *testing.T) { 594 currentTime = fakeTime 595 megabyte = 1 596 597 dir := makeTempDir("TestCompressOnRotate", t) 598 defer os.RemoveAll(dir) 599 600 filename := logFile(dir) 601 l := &Logger{ 602 Compress: true, 603 Filename: filename, 604 MaxSize: 10, 605 } 606 defer l.Close() 607 b := []byte("boo!") 608 n, err := l.Write(b) 609 isNil(err, t) 610 equals(len(b), n, t) 611 612 existsWithContent(filename, b, t) 613 fileCount(dir, 1, t) 614 615 newFakeTime() 616 617 err = l.Rotate() 618 isNil(err, t) 619 620 // the old logfile should be moved aside and the main logfile should have 621 // nothing in it. 622 existsWithContent(filename, []byte{}, t) 623 624 // we need to wait a little bit since the files get compressed on a different 625 // goroutine. 626 <-time.After(10 * time.Millisecond) 627 628 // a compressed version of the log file should now exist and the original 629 // should have been removed. 630 bc := new(bytes.Buffer) 631 gz := gzip.NewWriter(bc) 632 _, err = gz.Write(b) 633 isNil(err, t) 634 err = gz.Close() 635 isNil(err, t) 636 existsWithContent(backupFile(dir)+compressSuffix, bc.Bytes(), t) 637 notExist(backupFile(dir), t) 638 639 fileCount(dir, 2, t) 640 } 641 642 func TestCompressOnResume(t *testing.T) { 643 currentTime = fakeTime 644 megabyte = 1 645 646 dir := makeTempDir("TestCompressOnResume", t) 647 defer os.RemoveAll(dir) 648 649 filename := logFile(dir) 650 l := &Logger{ 651 Compress: true, 652 Filename: filename, 653 MaxSize: 10, 654 } 655 defer l.Close() 656 657 // Create a backup file and empty "compressed" file. 658 filename2 := backupFile(dir) 659 b := []byte("foo!") 660 err := ioutil.WriteFile(filename2, b, 0644) 661 isNil(err, t) 662 err = ioutil.WriteFile(filename2+compressSuffix, []byte{}, 0644) 663 isNil(err, t) 664 665 newFakeTime() 666 667 b2 := []byte("boo!") 668 n, err := l.Write(b2) 669 isNil(err, t) 670 equals(len(b2), n, t) 671 existsWithContent(filename, b2, t) 672 673 // we need to wait a little bit since the files get compressed on a different 674 // goroutine. 675 <-time.After(10 * time.Millisecond) 676 677 // The write should have started the compression - a compressed version of 678 // the log file should now exist and the original should have been removed. 679 bc := new(bytes.Buffer) 680 gz := gzip.NewWriter(bc) 681 _, err = gz.Write(b) 682 isNil(err, t) 683 err = gz.Close() 684 isNil(err, t) 685 existsWithContent(filename2+compressSuffix, bc.Bytes(), t) 686 notExist(filename2, t) 687 688 fileCount(dir, 2, t) 689 } 690 691 func TestJson(t *testing.T) { 692 data := []byte(` 693 { 694 "filename": "foo", 695 "maxsize": 5, 696 "maxage": 10, 697 "maxbackups": 3, 698 "localtime": true, 699 "compress": true 700 }`[1:]) 701 702 l := Logger{} 703 err := json.Unmarshal(data, &l) 704 isNil(err, t) 705 equals("foo", l.Filename, t) 706 equals(5, l.MaxSize, t) 707 equals(10, l.MaxAge, t) 708 equals(3, l.MaxBackups, t) 709 equals(true, l.LocalTime, t) 710 equals(true, l.Compress, t) 711 } 712 713 func TestYaml(t *testing.T) { 714 data := []byte(` 715 filename: foo 716 maxsize: 5 717 maxage: 10 718 maxbackups: 3 719 localtime: true 720 compress: true`[1:]) 721 722 l := Logger{} 723 err := yaml.Unmarshal(data, &l) 724 isNil(err, t) 725 equals("foo", l.Filename, t) 726 equals(5, l.MaxSize, t) 727 equals(10, l.MaxAge, t) 728 equals(3, l.MaxBackups, t) 729 equals(true, l.LocalTime, t) 730 equals(true, l.Compress, t) 731 } 732 733 func TestToml(t *testing.T) { 734 data := ` 735 filename = "foo" 736 maxsize = 5 737 maxage = 10 738 maxbackups = 3 739 localtime = true 740 compress = true`[1:] 741 742 l := Logger{} 743 md, err := toml.Decode(data, &l) 744 isNil(err, t) 745 equals("foo", l.Filename, t) 746 equals(5, l.MaxSize, t) 747 equals(10, l.MaxAge, t) 748 equals(3, l.MaxBackups, t) 749 equals(true, l.LocalTime, t) 750 equals(true, l.Compress, t) 751 equals(0, len(md.Undecoded()), t) 752 } 753 754 // makeTempDir creates a file with a semi-unique name in the OS temp directory. 755 // It should be based on the name of the test, to keep parallel tests from 756 // colliding, and must be cleaned up after the test is finished. 757 func makeTempDir(name string, t testing.TB) string { 758 dir := time.Now().Format(name + backupTimeFormat) 759 dir = filepath.Join(os.TempDir(), dir) 760 isNilUp(os.Mkdir(dir, 0700), t, 1) 761 return dir 762 } 763 764 // existsWithContent checks that the given file exists and has the correct content. 765 func existsWithContent(path string, content []byte, t testing.TB) { 766 info, err := os.Stat(path) 767 isNilUp(err, t, 1) 768 equalsUp(int64(len(content)), info.Size(), t, 1) 769 770 b, err := ioutil.ReadFile(path) 771 isNilUp(err, t, 1) 772 equalsUp(content, b, t, 1) 773 } 774 775 // logFile returns the log file name in the given directory for the current fake 776 // time. 777 func logFile(dir string) string { 778 return filepath.Join(dir, "foobar.log") 779 } 780 781 func backupFile(dir string) string { 782 return filepath.Join(dir, "foobar-"+fakeTime().UTC().Format(backupTimeFormat)+".log") 783 } 784 785 func backupFileLocal(dir string) string { 786 return filepath.Join(dir, "foobar-"+fakeTime().Format(backupTimeFormat)+".log") 787 } 788 789 // logFileLocal returns the log file name in the given directory for the current 790 // fake time using the local timezone. 791 func logFileLocal(dir string) string { 792 return filepath.Join(dir, fakeTime().Format(backupTimeFormat)) 793 } 794 795 // fileCount checks that the number of files in the directory is exp. 796 func fileCount(dir string, exp int, t testing.TB) { 797 files, err := ioutil.ReadDir(dir) 798 isNilUp(err, t, 1) 799 // Make sure no other files were created. 800 equalsUp(exp, len(files), t, 1) 801 } 802 803 // newFakeTime sets the fake "current time" to two days later. 804 func newFakeTime() { 805 fakeCurrentTime = fakeCurrentTime.Add(time.Hour * 24 * 2) 806 } 807 808 func notExist(path string, t testing.TB) { 809 _, err := os.Stat(path) 810 assertUp(os.IsNotExist(err), t, 1, "expected to get os.IsNotExist, but instead got %v", err) 811 } 812 813 func exists(path string, t testing.TB) { 814 _, err := os.Stat(path) 815 assertUp(err == nil, t, 1, "expected file to exist, but got error from os.Stat: %v", err) 816 }