github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/src/archive/tar/writer_test.go (about) 1 // Copyright 2009 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 tar 6 7 import ( 8 "bytes" 9 "encoding/hex" 10 "errors" 11 "io" 12 "io/ioutil" 13 "os" 14 "path" 15 "reflect" 16 "sort" 17 "strings" 18 "testing" 19 "testing/iotest" 20 "time" 21 ) 22 23 func bytediff(a, b []byte) string { 24 const ( 25 uniqueA = "- " 26 uniqueB = "+ " 27 identity = " " 28 ) 29 var ss []string 30 sa := strings.Split(strings.TrimSpace(hex.Dump(a)), "\n") 31 sb := strings.Split(strings.TrimSpace(hex.Dump(b)), "\n") 32 for len(sa) > 0 && len(sb) > 0 { 33 if sa[0] == sb[0] { 34 ss = append(ss, identity+sa[0]) 35 } else { 36 ss = append(ss, uniqueA+sa[0]) 37 ss = append(ss, uniqueB+sb[0]) 38 } 39 sa, sb = sa[1:], sb[1:] 40 } 41 for len(sa) > 0 { 42 ss = append(ss, uniqueA+sa[0]) 43 sa = sa[1:] 44 } 45 for len(sb) > 0 { 46 ss = append(ss, uniqueB+sb[0]) 47 sb = sb[1:] 48 } 49 return strings.Join(ss, "\n") 50 } 51 52 func TestWriter(t *testing.T) { 53 type ( 54 testHeader struct { // WriteHeader(hdr) == wantErr 55 hdr Header 56 wantErr error 57 } 58 testWrite struct { // Write(str) == (wantCnt, wantErr) 59 str string 60 wantCnt int 61 wantErr error 62 } 63 testReadFrom struct { // ReadFrom(testFile{ops}) == (wantCnt, wantErr) 64 ops fileOps 65 wantCnt int64 66 wantErr error 67 } 68 testClose struct { // Close() == wantErr 69 wantErr error 70 } 71 testFnc interface{} // testHeader | testWrite | testReadFrom | testClose 72 ) 73 74 vectors := []struct { 75 file string // Optional filename of expected output 76 tests []testFnc 77 }{{ 78 // The writer test file was produced with this command: 79 // tar (GNU tar) 1.26 80 // ln -s small.txt link.txt 81 // tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt 82 file: "testdata/writer.tar", 83 tests: []testFnc{ 84 testHeader{Header{ 85 Typeflag: TypeReg, 86 Name: "small.txt", 87 Size: 5, 88 Mode: 0640, 89 Uid: 73025, 90 Gid: 5000, 91 Uname: "dsymonds", 92 Gname: "eng", 93 ModTime: time.Unix(1246508266, 0), 94 }, nil}, 95 testWrite{"Kilts", 5, nil}, 96 97 testHeader{Header{ 98 Typeflag: TypeReg, 99 Name: "small2.txt", 100 Size: 11, 101 Mode: 0640, 102 Uid: 73025, 103 Uname: "dsymonds", 104 Gname: "eng", 105 Gid: 5000, 106 ModTime: time.Unix(1245217492, 0), 107 }, nil}, 108 testWrite{"Google.com\n", 11, nil}, 109 110 testHeader{Header{ 111 Typeflag: TypeSymlink, 112 Name: "link.txt", 113 Linkname: "small.txt", 114 Mode: 0777, 115 Uid: 1000, 116 Gid: 1000, 117 Uname: "strings", 118 Gname: "strings", 119 ModTime: time.Unix(1314603082, 0), 120 }, nil}, 121 testWrite{"", 0, nil}, 122 123 testClose{nil}, 124 }, 125 }, { 126 // The truncated test file was produced using these commands: 127 // dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt 128 // tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar 129 file: "testdata/writer-big.tar", 130 tests: []testFnc{ 131 testHeader{Header{ 132 Typeflag: TypeReg, 133 Name: "tmp/16gig.txt", 134 Size: 16 << 30, 135 Mode: 0640, 136 Uid: 73025, 137 Gid: 5000, 138 Uname: "dsymonds", 139 Gname: "eng", 140 ModTime: time.Unix(1254699560, 0), 141 Format: FormatGNU, 142 }, nil}, 143 }, 144 }, { 145 // This truncated file was produced using this library. 146 // It was verified to work with GNU tar 1.27.1 and BSD tar 3.1.2. 147 // dd if=/dev/zero bs=1G count=16 >> writer-big-long.tar 148 // gnutar -xvf writer-big-long.tar 149 // bsdtar -xvf writer-big-long.tar 150 // 151 // This file is in PAX format. 152 file: "testdata/writer-big-long.tar", 153 tests: []testFnc{ 154 testHeader{Header{ 155 Typeflag: TypeReg, 156 Name: strings.Repeat("longname/", 15) + "16gig.txt", 157 Size: 16 << 30, 158 Mode: 0644, 159 Uid: 1000, 160 Gid: 1000, 161 Uname: "guillaume", 162 Gname: "guillaume", 163 ModTime: time.Unix(1399583047, 0), 164 }, nil}, 165 }, 166 }, { 167 // This file was produced using GNU tar v1.17. 168 // gnutar -b 4 --format=ustar (longname/)*15 + file.txt 169 file: "testdata/ustar.tar", 170 tests: []testFnc{ 171 testHeader{Header{ 172 Typeflag: TypeReg, 173 Name: strings.Repeat("longname/", 15) + "file.txt", 174 Size: 6, 175 Mode: 0644, 176 Uid: 501, 177 Gid: 20, 178 Uname: "shane", 179 Gname: "staff", 180 ModTime: time.Unix(1360135598, 0), 181 }, nil}, 182 testWrite{"hello\n", 6, nil}, 183 testClose{nil}, 184 }, 185 }, { 186 // This file was produced using GNU tar v1.26: 187 // echo "Slartibartfast" > file.txt 188 // ln file.txt hard.txt 189 // tar -b 1 --format=ustar -c -f hardlink.tar file.txt hard.txt 190 file: "testdata/hardlink.tar", 191 tests: []testFnc{ 192 testHeader{Header{ 193 Typeflag: TypeReg, 194 Name: "file.txt", 195 Size: 15, 196 Mode: 0644, 197 Uid: 1000, 198 Gid: 100, 199 Uname: "vbatts", 200 Gname: "users", 201 ModTime: time.Unix(1425484303, 0), 202 }, nil}, 203 testWrite{"Slartibartfast\n", 15, nil}, 204 205 testHeader{Header{ 206 Typeflag: TypeLink, 207 Name: "hard.txt", 208 Linkname: "file.txt", 209 Mode: 0644, 210 Uid: 1000, 211 Gid: 100, 212 Uname: "vbatts", 213 Gname: "users", 214 ModTime: time.Unix(1425484303, 0), 215 }, nil}, 216 testWrite{"", 0, nil}, 217 218 testClose{nil}, 219 }, 220 }, { 221 tests: []testFnc{ 222 testHeader{Header{ 223 Typeflag: TypeReg, 224 Name: "bad-null.txt", 225 Xattrs: map[string]string{"null\x00null\x00": "fizzbuzz"}, 226 }, headerError{}}, 227 }, 228 }, { 229 tests: []testFnc{ 230 testHeader{Header{ 231 Typeflag: TypeReg, 232 Name: "null\x00.txt", 233 }, headerError{}}, 234 }, 235 }, { 236 file: "testdata/pax-records.tar", 237 tests: []testFnc{ 238 testHeader{Header{ 239 Typeflag: TypeReg, 240 Name: "file", 241 Uname: strings.Repeat("long", 10), 242 PAXRecords: map[string]string{ 243 "path": "FILE", // Should be ignored 244 "GNU.sparse.map": "0,0", // Should be ignored 245 "comment": "Hello, 世界", 246 "GOLANG.pkg": "tar", 247 }, 248 }, nil}, 249 testClose{nil}, 250 }, 251 }, { 252 // Craft a theoretically valid PAX archive with global headers. 253 // The GNU and BSD tar tools do not parse these the same way. 254 // 255 // BSD tar v3.1.2 parses and ignores all global headers; 256 // the behavior is verified by researching the source code. 257 // 258 // $ bsdtar -tvf pax-global-records.tar 259 // ---------- 0 0 0 0 Dec 31 1969 file1 260 // ---------- 0 0 0 0 Dec 31 1969 file2 261 // ---------- 0 0 0 0 Dec 31 1969 file3 262 // ---------- 0 0 0 0 May 13 2014 file4 263 // 264 // GNU tar v1.27.1 applies global headers to subsequent records, 265 // but does not do the following properly: 266 // * It does not treat an empty record as deletion. 267 // * It does not use subsequent global headers to update previous ones. 268 // 269 // $ gnutar -tvf pax-global-records.tar 270 // ---------- 0/0 0 2017-07-13 19:40 global1 271 // ---------- 0/0 0 2017-07-13 19:40 file2 272 // gnutar: Substituting `.' for empty member name 273 // ---------- 0/0 0 1969-12-31 16:00 274 // gnutar: Substituting `.' for empty member name 275 // ---------- 0/0 0 2014-05-13 09:53 276 // 277 // According to the PAX specification, this should have been the result: 278 // ---------- 0/0 0 2017-07-13 19:40 global1 279 // ---------- 0/0 0 2017-07-13 19:40 file2 280 // ---------- 0/0 0 2017-07-13 19:40 file3 281 // ---------- 0/0 0 2014-05-13 09:53 file4 282 file: "testdata/pax-global-records.tar", 283 tests: []testFnc{ 284 testHeader{Header{ 285 Typeflag: TypeXGlobalHeader, 286 PAXRecords: map[string]string{"path": "global1", "mtime": "1500000000.0"}, 287 }, nil}, 288 testHeader{Header{ 289 Typeflag: TypeReg, Name: "file1", 290 }, nil}, 291 testHeader{Header{ 292 Typeflag: TypeReg, 293 Name: "file2", 294 PAXRecords: map[string]string{"path": "file2"}, 295 }, nil}, 296 testHeader{Header{ 297 Typeflag: TypeXGlobalHeader, 298 PAXRecords: map[string]string{"path": ""}, // Should delete "path", but keep "mtime" 299 }, nil}, 300 testHeader{Header{ 301 Typeflag: TypeReg, Name: "file3", 302 }, nil}, 303 testHeader{Header{ 304 Typeflag: TypeReg, 305 Name: "file4", 306 ModTime: time.Unix(1400000000, 0), 307 PAXRecords: map[string]string{"mtime": "1400000000"}, 308 }, nil}, 309 testClose{nil}, 310 }, 311 }, { 312 file: "testdata/gnu-utf8.tar", 313 tests: []testFnc{ 314 testHeader{Header{ 315 Typeflag: TypeReg, 316 Name: "☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹", 317 Mode: 0644, 318 Uid: 1000, Gid: 1000, 319 Uname: "☺", 320 Gname: "⚹", 321 ModTime: time.Unix(0, 0), 322 Format: FormatGNU, 323 }, nil}, 324 testClose{nil}, 325 }, 326 }, { 327 file: "testdata/gnu-not-utf8.tar", 328 tests: []testFnc{ 329 testHeader{Header{ 330 Typeflag: TypeReg, 331 Name: "hi\x80\x81\x82\x83bye", 332 Mode: 0644, 333 Uid: 1000, 334 Gid: 1000, 335 Uname: "rawr", 336 Gname: "dsnet", 337 ModTime: time.Unix(0, 0), 338 Format: FormatGNU, 339 }, nil}, 340 testClose{nil}, 341 }, 342 }, { 343 file: "testdata/gnu-nil-sparse-data.tar", 344 tests: []testFnc{ 345 testHeader{Header{ 346 Typeflag: TypeGNUSparse, 347 Name: "sparse.db", 348 Size: 1000, 349 SparseHoles: []SparseEntry{{Offset: 1000, Length: 0}}, 350 }, nil}, 351 testWrite{strings.Repeat("0123456789", 100), 1000, nil}, 352 testClose{}, 353 }, 354 }, { 355 file: "testdata/gnu-nil-sparse-hole.tar", 356 tests: []testFnc{ 357 testHeader{Header{ 358 Typeflag: TypeGNUSparse, 359 Name: "sparse.db", 360 Size: 1000, 361 SparseHoles: []SparseEntry{{Offset: 0, Length: 1000}}, 362 }, nil}, 363 testWrite{strings.Repeat("\x00", 1000), 1000, nil}, 364 testClose{}, 365 }, 366 }, { 367 file: "testdata/pax-nil-sparse-data.tar", 368 tests: []testFnc{ 369 testHeader{Header{ 370 Typeflag: TypeReg, 371 Name: "sparse.db", 372 Size: 1000, 373 SparseHoles: []SparseEntry{{Offset: 1000, Length: 0}}, 374 }, nil}, 375 testWrite{strings.Repeat("0123456789", 100), 1000, nil}, 376 testClose{}, 377 }, 378 }, { 379 file: "testdata/pax-nil-sparse-hole.tar", 380 tests: []testFnc{ 381 testHeader{Header{ 382 Typeflag: TypeReg, 383 Name: "sparse.db", 384 Size: 1000, 385 SparseHoles: []SparseEntry{{Offset: 0, Length: 1000}}, 386 }, nil}, 387 testWrite{strings.Repeat("\x00", 1000), 1000, nil}, 388 testClose{}, 389 }, 390 }, { 391 file: "testdata/gnu-sparse-big.tar", 392 tests: []testFnc{ 393 testHeader{Header{ 394 Typeflag: TypeGNUSparse, 395 Name: "gnu-sparse", 396 Size: 6e10, 397 SparseHoles: []SparseEntry{ 398 {Offset: 0e10, Length: 1e10 - 100}, 399 {Offset: 1e10, Length: 1e10 - 100}, 400 {Offset: 2e10, Length: 1e10 - 100}, 401 {Offset: 3e10, Length: 1e10 - 100}, 402 {Offset: 4e10, Length: 1e10 - 100}, 403 {Offset: 5e10, Length: 1e10 - 100}, 404 }, 405 }, nil}, 406 testReadFrom{fileOps{ 407 int64(1e10 - blockSize), 408 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 409 int64(1e10 - blockSize), 410 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 411 int64(1e10 - blockSize), 412 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 413 int64(1e10 - blockSize), 414 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 415 int64(1e10 - blockSize), 416 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 417 int64(1e10 - blockSize), 418 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 419 }, 6e10, nil}, 420 testClose{nil}, 421 }, 422 }, { 423 file: "testdata/pax-sparse-big.tar", 424 tests: []testFnc{ 425 testHeader{Header{ 426 Typeflag: TypeReg, 427 Name: "pax-sparse", 428 Size: 6e10, 429 SparseHoles: []SparseEntry{ 430 {Offset: 0e10, Length: 1e10 - 100}, 431 {Offset: 1e10, Length: 1e10 - 100}, 432 {Offset: 2e10, Length: 1e10 - 100}, 433 {Offset: 3e10, Length: 1e10 - 100}, 434 {Offset: 4e10, Length: 1e10 - 100}, 435 {Offset: 5e10, Length: 1e10 - 100}, 436 }, 437 }, nil}, 438 testReadFrom{fileOps{ 439 int64(1e10 - blockSize), 440 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 441 int64(1e10 - blockSize), 442 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 443 int64(1e10 - blockSize), 444 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 445 int64(1e10 - blockSize), 446 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 447 int64(1e10 - blockSize), 448 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 449 int64(1e10 - blockSize), 450 strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), 451 }, 6e10, nil}, 452 testClose{nil}, 453 }, 454 }, { 455 file: "testdata/trailing-slash.tar", 456 tests: []testFnc{ 457 testHeader{Header{Name: strings.Repeat("123456789/", 30)}, nil}, 458 testClose{nil}, 459 }, 460 }} 461 462 equalError := func(x, y error) bool { 463 _, ok1 := x.(headerError) 464 _, ok2 := y.(headerError) 465 if ok1 || ok2 { 466 return ok1 && ok2 467 } 468 return x == y 469 } 470 for _, v := range vectors { 471 t.Run(path.Base(v.file), func(t *testing.T) { 472 const maxSize = 10 << 10 // 10KiB 473 buf := new(bytes.Buffer) 474 tw := NewWriter(iotest.TruncateWriter(buf, maxSize)) 475 476 for i, tf := range v.tests { 477 switch tf := tf.(type) { 478 case testHeader: 479 err := tw.WriteHeader(&tf.hdr) 480 if !equalError(err, tf.wantErr) { 481 t.Fatalf("test %d, WriteHeader() = %v, want %v", i, err, tf.wantErr) 482 } 483 case testWrite: 484 got, err := tw.Write([]byte(tf.str)) 485 if got != tf.wantCnt || !equalError(err, tf.wantErr) { 486 t.Fatalf("test %d, Write() = (%d, %v), want (%d, %v)", i, got, err, tf.wantCnt, tf.wantErr) 487 } 488 case testReadFrom: 489 f := &testFile{ops: tf.ops} 490 got, err := tw.ReadFrom(f) 491 if _, ok := err.(testError); ok { 492 t.Errorf("test %d, ReadFrom(): %v", i, err) 493 } else if got != tf.wantCnt || !equalError(err, tf.wantErr) { 494 t.Errorf("test %d, ReadFrom() = (%d, %v), want (%d, %v)", i, got, err, tf.wantCnt, tf.wantErr) 495 } 496 if len(f.ops) > 0 { 497 t.Errorf("test %d, expected %d more operations", i, len(f.ops)) 498 } 499 case testClose: 500 err := tw.Close() 501 if !equalError(err, tf.wantErr) { 502 t.Fatalf("test %d, Close() = %v, want %v", i, err, tf.wantErr) 503 } 504 default: 505 t.Fatalf("test %d, unknown test operation: %T", i, tf) 506 } 507 } 508 509 if v.file != "" { 510 want, err := ioutil.ReadFile(v.file) 511 if err != nil { 512 t.Fatalf("ReadFile() = %v, want nil", err) 513 } 514 got := buf.Bytes() 515 if !bytes.Equal(want, got) { 516 t.Fatalf("incorrect result: (-got +want)\n%v", bytediff(got, want)) 517 } 518 } 519 }) 520 } 521 } 522 523 func TestPax(t *testing.T) { 524 // Create an archive with a large name 525 fileinfo, err := os.Stat("testdata/small.txt") 526 if err != nil { 527 t.Fatal(err) 528 } 529 hdr, err := FileInfoHeader(fileinfo, "") 530 if err != nil { 531 t.Fatalf("os.Stat: %v", err) 532 } 533 // Force a PAX long name to be written 534 longName := strings.Repeat("ab", 100) 535 contents := strings.Repeat(" ", int(hdr.Size)) 536 hdr.Name = longName 537 var buf bytes.Buffer 538 writer := NewWriter(&buf) 539 if err := writer.WriteHeader(hdr); err != nil { 540 t.Fatal(err) 541 } 542 if _, err = writer.Write([]byte(contents)); err != nil { 543 t.Fatal(err) 544 } 545 if err := writer.Close(); err != nil { 546 t.Fatal(err) 547 } 548 // Simple test to make sure PAX extensions are in effect 549 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { 550 t.Fatal("Expected at least one PAX header to be written.") 551 } 552 // Test that we can get a long name back out of the archive. 553 reader := NewReader(&buf) 554 hdr, err = reader.Next() 555 if err != nil { 556 t.Fatal(err) 557 } 558 if hdr.Name != longName { 559 t.Fatal("Couldn't recover long file name") 560 } 561 } 562 563 func TestPaxSymlink(t *testing.T) { 564 // Create an archive with a large linkname 565 fileinfo, err := os.Stat("testdata/small.txt") 566 if err != nil { 567 t.Fatal(err) 568 } 569 hdr, err := FileInfoHeader(fileinfo, "") 570 hdr.Typeflag = TypeSymlink 571 if err != nil { 572 t.Fatalf("os.Stat:1 %v", err) 573 } 574 // Force a PAX long linkname to be written 575 longLinkname := strings.Repeat("1234567890/1234567890", 10) 576 hdr.Linkname = longLinkname 577 578 hdr.Size = 0 579 var buf bytes.Buffer 580 writer := NewWriter(&buf) 581 if err := writer.WriteHeader(hdr); err != nil { 582 t.Fatal(err) 583 } 584 if err := writer.Close(); err != nil { 585 t.Fatal(err) 586 } 587 // Simple test to make sure PAX extensions are in effect 588 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { 589 t.Fatal("Expected at least one PAX header to be written.") 590 } 591 // Test that we can get a long name back out of the archive. 592 reader := NewReader(&buf) 593 hdr, err = reader.Next() 594 if err != nil { 595 t.Fatal(err) 596 } 597 if hdr.Linkname != longLinkname { 598 t.Fatal("Couldn't recover long link name") 599 } 600 } 601 602 func TestPaxNonAscii(t *testing.T) { 603 // Create an archive with non ascii. These should trigger a pax header 604 // because pax headers have a defined utf-8 encoding. 605 fileinfo, err := os.Stat("testdata/small.txt") 606 if err != nil { 607 t.Fatal(err) 608 } 609 610 hdr, err := FileInfoHeader(fileinfo, "") 611 if err != nil { 612 t.Fatalf("os.Stat:1 %v", err) 613 } 614 615 // some sample data 616 chineseFilename := "文件名" 617 chineseGroupname := "組" 618 chineseUsername := "用戶名" 619 620 hdr.Name = chineseFilename 621 hdr.Gname = chineseGroupname 622 hdr.Uname = chineseUsername 623 624 contents := strings.Repeat(" ", int(hdr.Size)) 625 626 var buf bytes.Buffer 627 writer := NewWriter(&buf) 628 if err := writer.WriteHeader(hdr); err != nil { 629 t.Fatal(err) 630 } 631 if _, err = writer.Write([]byte(contents)); err != nil { 632 t.Fatal(err) 633 } 634 if err := writer.Close(); err != nil { 635 t.Fatal(err) 636 } 637 // Simple test to make sure PAX extensions are in effect 638 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { 639 t.Fatal("Expected at least one PAX header to be written.") 640 } 641 // Test that we can get a long name back out of the archive. 642 reader := NewReader(&buf) 643 hdr, err = reader.Next() 644 if err != nil { 645 t.Fatal(err) 646 } 647 if hdr.Name != chineseFilename { 648 t.Fatal("Couldn't recover unicode name") 649 } 650 if hdr.Gname != chineseGroupname { 651 t.Fatal("Couldn't recover unicode group") 652 } 653 if hdr.Uname != chineseUsername { 654 t.Fatal("Couldn't recover unicode user") 655 } 656 } 657 658 func TestPaxXattrs(t *testing.T) { 659 xattrs := map[string]string{ 660 "user.key": "value", 661 } 662 663 // Create an archive with an xattr 664 fileinfo, err := os.Stat("testdata/small.txt") 665 if err != nil { 666 t.Fatal(err) 667 } 668 hdr, err := FileInfoHeader(fileinfo, "") 669 if err != nil { 670 t.Fatalf("os.Stat: %v", err) 671 } 672 contents := "Kilts" 673 hdr.Xattrs = xattrs 674 var buf bytes.Buffer 675 writer := NewWriter(&buf) 676 if err := writer.WriteHeader(hdr); err != nil { 677 t.Fatal(err) 678 } 679 if _, err = writer.Write([]byte(contents)); err != nil { 680 t.Fatal(err) 681 } 682 if err := writer.Close(); err != nil { 683 t.Fatal(err) 684 } 685 // Test that we can get the xattrs back out of the archive. 686 reader := NewReader(&buf) 687 hdr, err = reader.Next() 688 if err != nil { 689 t.Fatal(err) 690 } 691 if !reflect.DeepEqual(hdr.Xattrs, xattrs) { 692 t.Fatalf("xattrs did not survive round trip: got %+v, want %+v", 693 hdr.Xattrs, xattrs) 694 } 695 } 696 697 func TestPaxHeadersSorted(t *testing.T) { 698 fileinfo, err := os.Stat("testdata/small.txt") 699 if err != nil { 700 t.Fatal(err) 701 } 702 hdr, err := FileInfoHeader(fileinfo, "") 703 if err != nil { 704 t.Fatalf("os.Stat: %v", err) 705 } 706 contents := strings.Repeat(" ", int(hdr.Size)) 707 708 hdr.Xattrs = map[string]string{ 709 "foo": "foo", 710 "bar": "bar", 711 "baz": "baz", 712 "qux": "qux", 713 } 714 715 var buf bytes.Buffer 716 writer := NewWriter(&buf) 717 if err := writer.WriteHeader(hdr); err != nil { 718 t.Fatal(err) 719 } 720 if _, err = writer.Write([]byte(contents)); err != nil { 721 t.Fatal(err) 722 } 723 if err := writer.Close(); err != nil { 724 t.Fatal(err) 725 } 726 // Simple test to make sure PAX extensions are in effect 727 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { 728 t.Fatal("Expected at least one PAX header to be written.") 729 } 730 731 // xattr bar should always appear before others 732 indices := []int{ 733 bytes.Index(buf.Bytes(), []byte("bar=bar")), 734 bytes.Index(buf.Bytes(), []byte("baz=baz")), 735 bytes.Index(buf.Bytes(), []byte("foo=foo")), 736 bytes.Index(buf.Bytes(), []byte("qux=qux")), 737 } 738 if !sort.IntsAreSorted(indices) { 739 t.Fatal("PAX headers are not sorted") 740 } 741 } 742 743 func TestUSTARLongName(t *testing.T) { 744 // Create an archive with a path that failed to split with USTAR extension in previous versions. 745 fileinfo, err := os.Stat("testdata/small.txt") 746 if err != nil { 747 t.Fatal(err) 748 } 749 hdr, err := FileInfoHeader(fileinfo, "") 750 hdr.Typeflag = TypeDir 751 if err != nil { 752 t.Fatalf("os.Stat:1 %v", err) 753 } 754 // Force a PAX long name to be written. The name was taken from a practical example 755 // that fails and replaced ever char through numbers to anonymize the sample. 756 longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/" 757 hdr.Name = longName 758 759 hdr.Size = 0 760 var buf bytes.Buffer 761 writer := NewWriter(&buf) 762 if err := writer.WriteHeader(hdr); err != nil { 763 t.Fatal(err) 764 } 765 if err := writer.Close(); err != nil { 766 t.Fatal(err) 767 } 768 // Test that we can get a long name back out of the archive. 769 reader := NewReader(&buf) 770 hdr, err = reader.Next() 771 if err != nil { 772 t.Fatal(err) 773 } 774 if hdr.Name != longName { 775 t.Fatal("Couldn't recover long name") 776 } 777 } 778 779 func TestValidTypeflagWithPAXHeader(t *testing.T) { 780 var buffer bytes.Buffer 781 tw := NewWriter(&buffer) 782 783 fileName := strings.Repeat("ab", 100) 784 785 hdr := &Header{ 786 Name: fileName, 787 Size: 4, 788 Typeflag: 0, 789 } 790 if err := tw.WriteHeader(hdr); err != nil { 791 t.Fatalf("Failed to write header: %s", err) 792 } 793 if _, err := tw.Write([]byte("fooo")); err != nil { 794 t.Fatalf("Failed to write the file's data: %s", err) 795 } 796 tw.Close() 797 798 tr := NewReader(&buffer) 799 800 for { 801 header, err := tr.Next() 802 if err == io.EOF { 803 break 804 } 805 if err != nil { 806 t.Fatalf("Failed to read header: %s", err) 807 } 808 if header.Typeflag != 0 { 809 t.Fatalf("Typeflag should've been 0, found %d", header.Typeflag) 810 } 811 } 812 } 813 814 // failOnceWriter fails exactly once and then always reports success. 815 type failOnceWriter bool 816 817 func (w *failOnceWriter) Write(b []byte) (int, error) { 818 if !*w { 819 return 0, io.ErrShortWrite 820 } 821 *w = true 822 return len(b), nil 823 } 824 825 func TestWriterErrors(t *testing.T) { 826 t.Run("HeaderOnly", func(t *testing.T) { 827 tw := NewWriter(new(bytes.Buffer)) 828 hdr := &Header{Name: "dir/", Typeflag: TypeDir} 829 if err := tw.WriteHeader(hdr); err != nil { 830 t.Fatalf("WriteHeader() = %v, want nil", err) 831 } 832 if _, err := tw.Write([]byte{0x00}); err != ErrWriteTooLong { 833 t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong) 834 } 835 }) 836 837 t.Run("NegativeSize", func(t *testing.T) { 838 tw := NewWriter(new(bytes.Buffer)) 839 hdr := &Header{Name: "small.txt", Size: -1} 840 if err := tw.WriteHeader(hdr); err == nil { 841 t.Fatalf("WriteHeader() = nil, want non-nil error") 842 } 843 }) 844 845 t.Run("BeforeHeader", func(t *testing.T) { 846 tw := NewWriter(new(bytes.Buffer)) 847 if _, err := tw.Write([]byte("Kilts")); err != ErrWriteTooLong { 848 t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong) 849 } 850 }) 851 852 t.Run("AfterClose", func(t *testing.T) { 853 tw := NewWriter(new(bytes.Buffer)) 854 hdr := &Header{Name: "small.txt"} 855 if err := tw.WriteHeader(hdr); err != nil { 856 t.Fatalf("WriteHeader() = %v, want nil", err) 857 } 858 if err := tw.Close(); err != nil { 859 t.Fatalf("Close() = %v, want nil", err) 860 } 861 if _, err := tw.Write([]byte("Kilts")); err != ErrWriteAfterClose { 862 t.Fatalf("Write() = %v, want %v", err, ErrWriteAfterClose) 863 } 864 if err := tw.Flush(); err != ErrWriteAfterClose { 865 t.Fatalf("Flush() = %v, want %v", err, ErrWriteAfterClose) 866 } 867 if err := tw.Close(); err != nil { 868 t.Fatalf("Close() = %v, want nil", err) 869 } 870 }) 871 872 t.Run("PrematureFlush", func(t *testing.T) { 873 tw := NewWriter(new(bytes.Buffer)) 874 hdr := &Header{Name: "small.txt", Size: 5} 875 if err := tw.WriteHeader(hdr); err != nil { 876 t.Fatalf("WriteHeader() = %v, want nil", err) 877 } 878 if err := tw.Flush(); err == nil { 879 t.Fatalf("Flush() = %v, want non-nil error", err) 880 } 881 }) 882 883 t.Run("PrematureClose", func(t *testing.T) { 884 tw := NewWriter(new(bytes.Buffer)) 885 hdr := &Header{Name: "small.txt", Size: 5} 886 if err := tw.WriteHeader(hdr); err != nil { 887 t.Fatalf("WriteHeader() = %v, want nil", err) 888 } 889 if err := tw.Close(); err == nil { 890 t.Fatalf("Close() = %v, want non-nil error", err) 891 } 892 }) 893 894 t.Run("Persistence", func(t *testing.T) { 895 tw := NewWriter(new(failOnceWriter)) 896 if err := tw.WriteHeader(&Header{}); err != io.ErrShortWrite { 897 t.Fatalf("WriteHeader() = %v, want %v", err, io.ErrShortWrite) 898 } 899 if err := tw.WriteHeader(&Header{Name: "small.txt"}); err == nil { 900 t.Errorf("WriteHeader() = got %v, want non-nil error", err) 901 } 902 if _, err := tw.Write(nil); err == nil { 903 t.Errorf("Write() = %v, want non-nil error", err) 904 } 905 if err := tw.Flush(); err == nil { 906 t.Errorf("Flush() = %v, want non-nil error", err) 907 } 908 if err := tw.Close(); err == nil { 909 t.Errorf("Close() = %v, want non-nil error", err) 910 } 911 }) 912 } 913 914 func TestSplitUSTARPath(t *testing.T) { 915 sr := strings.Repeat 916 917 vectors := []struct { 918 input string // Input path 919 prefix string // Expected output prefix 920 suffix string // Expected output suffix 921 ok bool // Split success? 922 }{ 923 {"", "", "", false}, 924 {"abc", "", "", false}, 925 {"用戶名", "", "", false}, 926 {sr("a", nameSize), "", "", false}, 927 {sr("a", nameSize) + "/", "", "", false}, 928 {sr("a", nameSize) + "/a", sr("a", nameSize), "a", true}, 929 {sr("a", prefixSize) + "/", "", "", false}, 930 {sr("a", prefixSize) + "/a", sr("a", prefixSize), "a", true}, 931 {sr("a", nameSize+1), "", "", false}, 932 {sr("/", nameSize+1), sr("/", nameSize-1), "/", true}, 933 {sr("a", prefixSize) + "/" + sr("b", nameSize), 934 sr("a", prefixSize), sr("b", nameSize), true}, 935 {sr("a", prefixSize) + "//" + sr("b", nameSize), "", "", false}, 936 {sr("a/", nameSize), sr("a/", 77) + "a", sr("a/", 22), true}, 937 } 938 939 for _, v := range vectors { 940 prefix, suffix, ok := splitUSTARPath(v.input) 941 if prefix != v.prefix || suffix != v.suffix || ok != v.ok { 942 t.Errorf("splitUSTARPath(%q):\ngot (%q, %q, %v)\nwant (%q, %q, %v)", 943 v.input, prefix, suffix, ok, v.prefix, v.suffix, v.ok) 944 } 945 } 946 } 947 948 // TestIssue12594 tests that the Writer does not attempt to populate the prefix 949 // field when encoding a header in the GNU format. The prefix field is valid 950 // in USTAR and PAX, but not GNU. 951 func TestIssue12594(t *testing.T) { 952 names := []string{ 953 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/file.txt", 954 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/file.txt", 955 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/333/file.txt", 956 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/file.txt", 957 "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/file.txt", 958 "/home/support/.openoffice.org/3/user/uno_packages/cache/registry/com.sun.star.comp.deployment.executable.PackageRegistryBackend", 959 } 960 961 for i, name := range names { 962 var b bytes.Buffer 963 964 tw := NewWriter(&b) 965 if err := tw.WriteHeader(&Header{ 966 Name: name, 967 Uid: 1 << 25, // Prevent USTAR format 968 }); err != nil { 969 t.Errorf("test %d, unexpected WriteHeader error: %v", i, err) 970 } 971 if err := tw.Close(); err != nil { 972 t.Errorf("test %d, unexpected Close error: %v", i, err) 973 } 974 975 // The prefix field should never appear in the GNU format. 976 var blk block 977 copy(blk[:], b.Bytes()) 978 prefix := string(blk.USTAR().Prefix()) 979 if i := strings.IndexByte(prefix, 0); i >= 0 { 980 prefix = prefix[:i] // Truncate at the NUL terminator 981 } 982 if blk.GetFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) { 983 t.Errorf("test %d, found prefix in GNU format: %s", i, prefix) 984 } 985 986 tr := NewReader(&b) 987 hdr, err := tr.Next() 988 if err != nil { 989 t.Errorf("test %d, unexpected Next error: %v", i, err) 990 } 991 if hdr.Name != name { 992 t.Errorf("test %d, hdr.Name = %s, want %s", i, hdr.Name, name) 993 } 994 } 995 } 996 997 // testNonEmptyWriter wraps an io.Writer and ensures that 998 // Write is never called with an empty buffer. 999 type testNonEmptyWriter struct{ io.Writer } 1000 1001 func (w testNonEmptyWriter) Write(b []byte) (int, error) { 1002 if len(b) == 0 { 1003 return 0, errors.New("unexpected empty Write call") 1004 } 1005 return w.Writer.Write(b) 1006 } 1007 1008 func TestFileWriter(t *testing.T) { 1009 type ( 1010 testWrite struct { // Write(str) == (wantCnt, wantErr) 1011 str string 1012 wantCnt int 1013 wantErr error 1014 } 1015 testReadFrom struct { // ReadFrom(testFile{ops}) == (wantCnt, wantErr) 1016 ops fileOps 1017 wantCnt int64 1018 wantErr error 1019 } 1020 testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt 1021 wantLCnt int64 1022 wantPCnt int64 1023 } 1024 testFnc interface{} // testWrite | testReadFrom | testRemaining 1025 ) 1026 1027 type ( 1028 makeReg struct { 1029 size int64 1030 wantStr string 1031 } 1032 makeSparse struct { 1033 makeReg makeReg 1034 sph sparseHoles 1035 size int64 1036 } 1037 fileMaker interface{} // makeReg | makeSparse 1038 ) 1039 1040 vectors := []struct { 1041 maker fileMaker 1042 tests []testFnc 1043 }{{ 1044 maker: makeReg{0, ""}, 1045 tests: []testFnc{ 1046 testRemaining{0, 0}, 1047 testWrite{"", 0, nil}, 1048 testWrite{"a", 0, ErrWriteTooLong}, 1049 testReadFrom{fileOps{""}, 0, nil}, 1050 testReadFrom{fileOps{"a"}, 0, ErrWriteTooLong}, 1051 testRemaining{0, 0}, 1052 }, 1053 }, { 1054 maker: makeReg{1, "a"}, 1055 tests: []testFnc{ 1056 testRemaining{1, 1}, 1057 testWrite{"", 0, nil}, 1058 testWrite{"a", 1, nil}, 1059 testWrite{"bcde", 0, ErrWriteTooLong}, 1060 testWrite{"", 0, nil}, 1061 testReadFrom{fileOps{""}, 0, nil}, 1062 testReadFrom{fileOps{"a"}, 0, ErrWriteTooLong}, 1063 testRemaining{0, 0}, 1064 }, 1065 }, { 1066 maker: makeReg{5, "hello"}, 1067 tests: []testFnc{ 1068 testRemaining{5, 5}, 1069 testWrite{"hello", 5, nil}, 1070 testRemaining{0, 0}, 1071 }, 1072 }, { 1073 maker: makeReg{5, "\x00\x00\x00\x00\x00"}, 1074 tests: []testFnc{ 1075 testRemaining{5, 5}, 1076 testReadFrom{fileOps{"\x00\x00\x00\x00\x00"}, 5, nil}, 1077 testRemaining{0, 0}, 1078 }, 1079 }, { 1080 maker: makeReg{5, "\x00\x00\x00\x00\x00"}, 1081 tests: []testFnc{ 1082 testRemaining{5, 5}, 1083 testReadFrom{fileOps{"\x00\x00\x00\x00\x00extra"}, 5, ErrWriteTooLong}, 1084 testRemaining{0, 0}, 1085 }, 1086 }, { 1087 maker: makeReg{5, "abc\x00\x00"}, 1088 tests: []testFnc{ 1089 testRemaining{5, 5}, 1090 testWrite{"abc", 3, nil}, 1091 testRemaining{2, 2}, 1092 testReadFrom{fileOps{"\x00\x00"}, 2, nil}, 1093 testRemaining{0, 0}, 1094 }, 1095 }, { 1096 maker: makeReg{5, "\x00\x00abc"}, 1097 tests: []testFnc{ 1098 testRemaining{5, 5}, 1099 testWrite{"\x00\x00", 2, nil}, 1100 testRemaining{3, 3}, 1101 testWrite{"abc", 3, nil}, 1102 testReadFrom{fileOps{"z"}, 0, ErrWriteTooLong}, 1103 testWrite{"z", 0, ErrWriteTooLong}, 1104 testRemaining{0, 0}, 1105 }, 1106 }, { 1107 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, 1108 tests: []testFnc{ 1109 testRemaining{8, 5}, 1110 testWrite{"ab\x00\x00\x00cde", 8, nil}, 1111 testWrite{"a", 0, ErrWriteTooLong}, 1112 testRemaining{0, 0}, 1113 }, 1114 }, { 1115 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, 1116 tests: []testFnc{ 1117 testWrite{"ab\x00\x00\x00cdez", 8, ErrWriteTooLong}, 1118 testRemaining{0, 0}, 1119 }, 1120 }, { 1121 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, 1122 tests: []testFnc{ 1123 testWrite{"ab\x00", 3, nil}, 1124 testRemaining{5, 3}, 1125 testWrite{"\x00\x00cde", 5, nil}, 1126 testWrite{"a", 0, ErrWriteTooLong}, 1127 testRemaining{0, 0}, 1128 }, 1129 }, { 1130 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, 1131 tests: []testFnc{ 1132 testWrite{"ab", 2, nil}, 1133 testRemaining{6, 3}, 1134 testReadFrom{fileOps{int64(3), "cde"}, 6, nil}, 1135 testRemaining{0, 0}, 1136 }, 1137 }, { 1138 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, 1139 tests: []testFnc{ 1140 testReadFrom{fileOps{"ab", int64(3), "cde"}, 8, nil}, 1141 testRemaining{0, 0}, 1142 }, 1143 }, { 1144 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, 1145 tests: []testFnc{ 1146 testReadFrom{fileOps{"ab", int64(3), "cdeX"}, 8, ErrWriteTooLong}, 1147 testRemaining{0, 0}, 1148 }, 1149 }, { 1150 maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8}, 1151 tests: []testFnc{ 1152 testReadFrom{fileOps{"ab", int64(3), "cd"}, 7, io.ErrUnexpectedEOF}, 1153 testRemaining{1, 0}, 1154 }, 1155 }, { 1156 maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8}, 1157 tests: []testFnc{ 1158 testReadFrom{fileOps{"ab", int64(3), "cde"}, 7, errMissData}, 1159 testRemaining{1, 0}, 1160 }, 1161 }, { 1162 maker: makeSparse{makeReg{6, "abcde"}, sparseHoles{{2, 3}}, 8}, 1163 tests: []testFnc{ 1164 testReadFrom{fileOps{"ab", int64(3), "cde"}, 8, errUnrefData}, 1165 testRemaining{0, 1}, 1166 }, 1167 }, { 1168 maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8}, 1169 tests: []testFnc{ 1170 testWrite{"ab", 2, nil}, 1171 testRemaining{6, 2}, 1172 testWrite{"\x00\x00\x00", 3, nil}, 1173 testRemaining{3, 2}, 1174 testWrite{"cde", 2, errMissData}, 1175 testRemaining{1, 0}, 1176 }, 1177 }, { 1178 maker: makeSparse{makeReg{6, "abcde"}, sparseHoles{{2, 3}}, 8}, 1179 tests: []testFnc{ 1180 testWrite{"ab", 2, nil}, 1181 testRemaining{6, 4}, 1182 testWrite{"\x00\x00\x00", 3, nil}, 1183 testRemaining{3, 4}, 1184 testWrite{"cde", 3, errUnrefData}, 1185 testRemaining{0, 1}, 1186 }, 1187 }, { 1188 maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, 1189 tests: []testFnc{ 1190 testRemaining{7, 3}, 1191 testWrite{"\x00\x00abc\x00\x00", 7, nil}, 1192 testRemaining{0, 0}, 1193 }, 1194 }, { 1195 maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, 1196 tests: []testFnc{ 1197 testRemaining{7, 3}, 1198 testReadFrom{fileOps{int64(2), "abc", int64(1), "\x00"}, 7, nil}, 1199 testRemaining{0, 0}, 1200 }, 1201 }, { 1202 maker: makeSparse{makeReg{3, ""}, sparseHoles{{0, 2}, {5, 2}}, 7}, 1203 tests: []testFnc{ 1204 testWrite{"abcdefg", 0, errWriteHole}, 1205 }, 1206 }, { 1207 maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, 1208 tests: []testFnc{ 1209 testWrite{"\x00\x00abcde", 5, errWriteHole}, 1210 }, 1211 }, { 1212 maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, 1213 tests: []testFnc{ 1214 testWrite{"\x00\x00abc\x00\x00z", 7, ErrWriteTooLong}, 1215 testRemaining{0, 0}, 1216 }, 1217 }, { 1218 maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, 1219 tests: []testFnc{ 1220 testWrite{"\x00\x00", 2, nil}, 1221 testRemaining{5, 3}, 1222 testWrite{"abc", 3, nil}, 1223 testRemaining{2, 0}, 1224 testWrite{"\x00\x00", 2, nil}, 1225 testRemaining{0, 0}, 1226 }, 1227 }, { 1228 maker: makeSparse{makeReg{2, "ab"}, sparseHoles{{0, 2}, {5, 2}}, 7}, 1229 tests: []testFnc{ 1230 testWrite{"\x00\x00", 2, nil}, 1231 testWrite{"abc", 2, errMissData}, 1232 testWrite{"\x00\x00", 0, errMissData}, 1233 }, 1234 }, { 1235 maker: makeSparse{makeReg{4, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, 1236 tests: []testFnc{ 1237 testWrite{"\x00\x00", 2, nil}, 1238 testWrite{"abc", 3, nil}, 1239 testWrite{"\x00\x00", 2, errUnrefData}, 1240 }, 1241 }} 1242 1243 for i, v := range vectors { 1244 var wantStr string 1245 bb := new(bytes.Buffer) 1246 w := testNonEmptyWriter{bb} 1247 var fw fileWriter 1248 switch maker := v.maker.(type) { 1249 case makeReg: 1250 fw = ®FileWriter{w, maker.size} 1251 wantStr = maker.wantStr 1252 case makeSparse: 1253 if !validateSparseEntries(maker.sph, maker.size) { 1254 t.Fatalf("invalid sparse map: %v", maker.sph) 1255 } 1256 spd := invertSparseEntries(maker.sph, maker.size) 1257 fw = ®FileWriter{w, maker.makeReg.size} 1258 fw = &sparseFileWriter{fw, spd, 0} 1259 wantStr = maker.makeReg.wantStr 1260 default: 1261 t.Fatalf("test %d, unknown make operation: %T", i, maker) 1262 } 1263 1264 for j, tf := range v.tests { 1265 switch tf := tf.(type) { 1266 case testWrite: 1267 got, err := fw.Write([]byte(tf.str)) 1268 if got != tf.wantCnt || err != tf.wantErr { 1269 t.Errorf("test %d.%d, Write(%s):\ngot (%d, %v)\nwant (%d, %v)", i, j, tf.str, got, err, tf.wantCnt, tf.wantErr) 1270 } 1271 case testReadFrom: 1272 f := &testFile{ops: tf.ops} 1273 got, err := fw.ReadFrom(f) 1274 if _, ok := err.(testError); ok { 1275 t.Errorf("test %d.%d, ReadFrom(): %v", i, j, err) 1276 } else if got != tf.wantCnt || err != tf.wantErr { 1277 t.Errorf("test %d.%d, ReadFrom() = (%d, %v), want (%d, %v)", i, j, got, err, tf.wantCnt, tf.wantErr) 1278 } 1279 if len(f.ops) > 0 { 1280 t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops)) 1281 } 1282 case testRemaining: 1283 if got := fw.LogicalRemaining(); got != tf.wantLCnt { 1284 t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) 1285 } 1286 if got := fw.PhysicalRemaining(); got != tf.wantPCnt { 1287 t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) 1288 } 1289 default: 1290 t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf) 1291 } 1292 } 1293 1294 if got := bb.String(); got != wantStr { 1295 t.Fatalf("test %d, String() = %q, want %q", i, got, wantStr) 1296 } 1297 } 1298 }