1 // Copyright 2011 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.
5 // Tests that involve both reading and writing.
25 func TestOver65kFiles(t
*testing
.T
) {
26 if testing
.Short() && testenv
.Builder() == "" {
27 t
.Skip("skipping in short mode")
29 buf
:= new(bytes
.Buffer
)
31 const nFiles
= (1 << 16) + 42
32 for i
:= 0; i
< nFiles
; i
++ {
33 _
, err
:= w
.CreateHeader(&FileHeader
{
34 Name
: fmt
.Sprintf("%d.dat", i
),
35 Method
: Store
, // avoid Issue 6136 and Issue 6138
38 t
.Fatalf("creating file %d: %v", i
, err
)
41 if err
:= w
.Close(); err
!= nil {
42 t
.Fatalf("Writer.Close: %v", err
)
45 zr
, err
:= NewReader(strings
.NewReader(s
), int64(len(s
)))
47 t
.Fatalf("NewReader: %v", err
)
49 if got
:= len(zr
.File
); got
!= nFiles
{
50 t
.Fatalf("File contains %d files, want %d", got
, nFiles
)
52 for i
:= 0; i
< nFiles
; i
++ {
53 want
:= fmt
.Sprintf("%d.dat", i
)
54 if zr
.File
[i
].Name
!= want
{
55 t
.Fatalf("File(%d) = %q, want %q", i
, zr
.File
[i
].Name
, want
)
60 func TestModTime(t
*testing
.T
) {
61 var testTime
= time
.Date(2009, time
.November
, 10, 23, 45, 58, 0, time
.UTC
)
63 fh
.SetModTime(testTime
)
64 outTime
:= fh
.ModTime()
65 if !outTime
.Equal(testTime
) {
66 t
.Errorf("times don't match: got %s, want %s", outTime
, testTime
)
70 func testHeaderRoundTrip(fh
*FileHeader
, wantUncompressedSize
uint32, wantUncompressedSize64
uint64, t
*testing
.T
) {
72 fh2
, err
:= FileInfoHeader(fi
)
76 if got
, want
:= fh2
.Name
, fh
.Name
; got
!= want
{
77 t
.Errorf("Name: got %s, want %s\n", got
, want
)
79 if got
, want
:= fh2
.UncompressedSize
, wantUncompressedSize
; got
!= want
{
80 t
.Errorf("UncompressedSize: got %d, want %d\n", got
, want
)
82 if got
, want
:= fh2
.UncompressedSize64
, wantUncompressedSize64
; got
!= want
{
83 t
.Errorf("UncompressedSize64: got %d, want %d\n", got
, want
)
85 if got
, want
:= fh2
.ModifiedTime
, fh
.ModifiedTime
; got
!= want
{
86 t
.Errorf("ModifiedTime: got %d, want %d\n", got
, want
)
88 if got
, want
:= fh2
.ModifiedDate
, fh
.ModifiedDate
; got
!= want
{
89 t
.Errorf("ModifiedDate: got %d, want %d\n", got
, want
)
92 if sysfh
, ok
:= fi
.Sys().(*FileHeader
); !ok
&& sysfh
!= fh
{
93 t
.Errorf("Sys didn't return original *FileHeader")
97 func TestFileHeaderRoundTrip(t
*testing
.T
) {
100 UncompressedSize
: 987654321,
104 testHeaderRoundTrip(fh
, fh
.UncompressedSize
, uint64(fh
.UncompressedSize
), t
)
107 func TestFileHeaderRoundTrip64(t
*testing
.T
) {
110 UncompressedSize64
: 9876543210,
114 testHeaderRoundTrip(fh
, uint32max
, fh
.UncompressedSize64
, t
)
117 type repeatedByte
struct {
123 // rleBuffer is a run-length-encoded byte buffer.
124 // It's an io.Writer (like a bytes.Buffer) and also an io.ReaderAt,
125 // allowing random-access reads.
126 type rleBuffer
struct {
130 func (r
*rleBuffer
) Size() int64 {
134 last
:= &r
.buf
[len(r
.buf
)-1]
135 return last
.off
+ last
.n
138 func (r
*rleBuffer
) Write(p
[]byte) (n
int, err error
) {
141 rp
= &r
.buf
[len(r
.buf
)-1]
142 // Fast path, if p is entirely the same byte repeated.
143 if lastByte
:= rp
.b
; len(p
) > 0 && p
[0] == lastByte
{
144 if bytes
.Count(p
, []byte{lastByte
}) == len(p
) {
145 rp
.n
+= int64(len(p
))
151 for _
, b
:= range p
{
152 if rp
== nil || rp
.b
!= b
{
153 r
.buf
= append(r
.buf
, repeatedByte
{r
.Size(), b
, 1})
154 rp
= &r
.buf
[len(r
.buf
)-1]
162 func min(x
, y
int) int {
169 func memset(a
[]byte, b
byte) {
173 // Double, until we reach power of 2 >= len(a), same as bytes.Repeat,
174 // but without allocation.
176 for i
, l
:= 1, len(a
); i
< l
; i
*= 2 {
181 func (r
*rleBuffer
) ReadAt(p
[]byte, off
int64) (n
int, err error
) {
185 skipParts
:= sort
.Search(len(r
.buf
), func(i
int) bool {
187 return part
.off
+part
.n
> off
189 parts
:= r
.buf
[skipParts
:]
191 skipBytes
:= off
- parts
[0].off
192 for _
, part
:= range parts
{
193 repeat
:= min(int(part
.n
-skipBytes
), len(p
)-n
)
194 memset(p
[n
:n
+repeat
], part
.b
)
203 err
= io
.ErrUnexpectedEOF
208 // Just testing the rleBuffer used in the Zip64 test above. Not used by the zip code.
209 func TestRLEBuffer(t
*testing
.T
) {
212 writes
:= []string{"abcdeee", "eeeeeee", "eeeefghaaiii"}
213 for _
, w
:= range writes
{
215 all
= append(all
, w
...)
217 if len(b
.buf
) != 10 {
218 t
.Fatalf("len(b.buf) = %d; want 10", len(b
.buf
))
221 for i
:= 0; i
< len(all
); i
++ {
222 for j
:= 0; j
< len(all
)-i
; j
++ {
223 buf
:= make([]byte, j
)
224 n
, err
:= b
.ReadAt(buf
, int64(i
))
225 if err
!= nil || n
!= len(buf
) {
226 t
.Errorf("ReadAt(%d, %d) = %d, %v; want %d, nil", i
, j
, n
, err
, len(buf
))
228 if !bytes
.Equal(buf
, all
[i
:i
+j
]) {
229 t
.Errorf("ReadAt(%d, %d) = %q; want %q", i
, j
, buf
, all
[i
:i
+j
])
235 // fakeHash32 is a dummy Hash32 that always returns 0.
236 type fakeHash32
struct {
240 func (fakeHash32
) Write(p
[]byte) (int, error
) { return len(p
), nil }
241 func (fakeHash32
) Sum32() uint32 { return 0 }
243 func TestZip64(t
*testing
.T
) {
245 t
.Skip("slow test; skipping")
248 const size
= 1 << 32 // before the "END\n" part
249 buf
:= testZip64(t
, size
)
250 testZip64DirectoryRecordLength(buf
, t
)
253 func TestZip64EdgeCase(t
*testing
.T
) {
255 t
.Skip("slow test; skipping")
258 // Test a zip file with uncompressed size 0xFFFFFFFF.
259 // That's the magic marker for a 64-bit file, so even though
260 // it fits in a 32-bit field we must use the 64-bit field.
261 // Go 1.5 and earlier got this wrong,
262 // writing an invalid zip file.
263 const size
= 1<<32 - 1 - int64(len("END\n")) // before the "END\n" part
264 buf
:= testZip64(t
, size
)
265 testZip64DirectoryRecordLength(buf
, t
)
268 // Tests that we generate a zip64 file if the directory at offset
269 // 0xFFFFFFFF, but not before.
270 func TestZip64DirectoryOffset(t
*testing
.T
) {
271 if testing
.Short() && race
.Enabled
{
272 t
.Skip("skipping in short mode")
275 const filename
= "huge.txt"
276 gen
:= func(wantOff
uint64) func(*Writer
) {
277 return func(w
*Writer
) {
278 w
.testHookCloseSizeOffset
= func(size
, off
uint64) {
280 t
.Errorf("central directory offset = %d (%x); want %d", off
, off
, wantOff
)
283 f
, err
:= w
.CreateHeader(&FileHeader
{
290 f
.(*fileWriter
).crc32
= fakeHash32
{}
291 size
:= wantOff
- fileHeaderLen
- uint64(len(filename
)) - dataDescriptorLen
292 if _
, err
:= io
.CopyN(f
, zeros
{}, int64(size
)); err
!= nil {
295 if err
:= w
.Close(); err
!= nil {
300 t
.Run("uint32max-2_NoZip64", func(t
*testing
.T
) {
302 if generatesZip64(t
, gen(0xfffffffe)) {
303 t
.Error("unexpected zip64")
306 t
.Run("uint32max-1_Zip64", func(t
*testing
.T
) {
308 if !generatesZip64(t
, gen(0xffffffff)) {
309 t
.Error("expected zip64")
314 // At 16k records, we need to generate a zip64 file.
315 func TestZip64ManyRecords(t
*testing
.T
) {
316 if testing
.Short() && race
.Enabled
{
317 t
.Skip("skipping in short mode")
320 gen
:= func(numRec
int) func(*Writer
) {
321 return func(w
*Writer
) {
322 for i
:= 0; i
< numRec
; i
++ {
323 _
, err
:= w
.CreateHeader(&FileHeader
{
331 if err
:= w
.Close(); err
!= nil {
336 // 16k-1 records shouldn't make a zip64:
337 t
.Run("uint16max-1_NoZip64", func(t
*testing
.T
) {
339 if generatesZip64(t
, gen(0xfffe)) {
340 t
.Error("unexpected zip64")
343 // 16k records should make a zip64:
344 t
.Run("uint16max_Zip64", func(t
*testing
.T
) {
346 if !generatesZip64(t
, gen(0xffff)) {
347 t
.Error("expected zip64")
352 // suffixSaver is an io.Writer & io.ReaderAt that remembers the last 0
353 // to 'keep' bytes of data written to it. Call Suffix to get the
355 type suffixSaver
struct {
362 func (ss
*suffixSaver
) Size() int64 { return ss
.size
}
364 var errDiscardedBytes
= errors
.New("ReadAt of discarded bytes")
366 func (ss
*suffixSaver
) ReadAt(p
[]byte, off
int64) (n
int, err error
) {
367 back
:= ss
.size
- off
368 if back
> int64(ss
.keep
) {
369 return 0, errDiscardedBytes
372 n
= copy(p
, suf
[len(suf
)-int(back
):])
379 func (ss
*suffixSaver
) Suffix() []byte {
380 if len(ss
.buf
) < ss
.keep
{
383 buf
:= make([]byte, ss
.keep
)
384 n
:= copy(buf
, ss
.buf
[ss
.start
:])
385 copy(buf
[n
:], ss
.buf
[:])
389 func (ss
*suffixSaver
) Write(p
[]byte) (n
int, err error
) {
391 ss
.size
+= int64(len(p
))
392 if len(ss
.buf
) < ss
.keep
{
393 space
:= ss
.keep
- len(ss
.buf
)
398 ss
.buf
= append(ss
.buf
, p
[:add
]...)
402 n
:= copy(ss
.buf
[ss
.start
:], p
)
405 if ss
.start
== ss
.keep
{
412 // generatesZip64 reports whether f wrote a zip64 file.
413 // f is also responsible for closing w.
414 func generatesZip64(t
*testing
.T
, f
func(w
*Writer
)) bool {
415 ss
:= &suffixSaver
{keep
: 10 << 20}
418 return suffixIsZip64(t
, ss
)
421 type sizedReaderAt
interface {
426 func suffixIsZip64(t
*testing
.T
, zip sizedReaderAt
) bool {
427 d
:= make([]byte, 1024)
428 if _
, err
:= zip
.ReadAt(d
, zip
.Size()-int64(len(d
))); err
!= nil {
429 t
.Fatalf("ReadAt: %v", err
)
432 sigOff
:= findSignatureInBlock(d
)
434 t
.Errorf("failed to find signature in block")
438 dirOff
, err
:= findDirectory64End(zip
, zip
.Size()-int64(len(d
))+int64(sigOff
))
440 t
.Fatalf("findDirectory64End: %v", err
)
446 d
= make([]byte, directory64EndLen
)
447 if _
, err
:= zip
.ReadAt(d
, dirOff
); err
!= nil {
448 t
.Fatalf("ReadAt(off=%d): %v", dirOff
, err
)
452 if sig
:= b
.uint32(); sig
!= directory64EndSignature
{
457 if size
!= directory64EndLen
-12 {
458 t
.Errorf("expected length of %d, got %d", directory64EndLen
-12, size
)
463 // Zip64 is required if the total size of the records is uint32max.
464 func TestZip64LargeDirectory(t
*testing
.T
) {
465 if runtime
.GOARCH
== "wasm" {
466 t
.Skip("too slow on wasm")
469 t
.Skip("skipping in short mode")
472 // gen returns a func that writes a zip with a wantLen bytes
473 // of central directory.
474 gen
:= func(wantLen
int64) func(*Writer
) {
475 return func(w
*Writer
) {
476 w
.testHookCloseSizeOffset
= func(size
, off
uint64) {
477 if size
!= uint64(wantLen
) {
478 t
.Errorf("Close central directory size = %d; want %d", size
, wantLen
)
482 uint16string
:= strings
.Repeat(".", uint16max
)
485 commentLen
:= int(uint16max
) - directoryHeaderLen
- 1
486 thisRecLen
:= directoryHeaderLen
+ int(uint16max
) + commentLen
487 if int64(thisRecLen
) > remain
{
488 remove
:= thisRecLen
- int(remain
)
492 remain
-= int64(thisRecLen
)
493 f
, err
:= w
.CreateHeader(&FileHeader
{
495 Comment
: uint16string
[:commentLen
],
498 t
.Fatalf("CreateHeader: %v", err
)
500 f
.(*fileWriter
).crc32
= fakeHash32
{}
502 if err
:= w
.Close(); err
!= nil {
503 t
.Fatalf("Close: %v", err
)
507 t
.Run("uint32max-1_NoZip64", func(t
*testing
.T
) {
509 if generatesZip64(t
, gen(uint32max
-1)) {
510 t
.Error("unexpected zip64")
513 t
.Run("uint32max_HasZip64", func(t
*testing
.T
) {
515 if !generatesZip64(t
, gen(uint32max
)) {
516 t
.Error("expected zip64")
521 func testZip64(t testing
.TB
, size
int64) *rleBuffer
{
522 const chunkSize
= 1024
523 chunks
:= int(size
/ chunkSize
)
524 // write size bytes plus "END\n" to a zip file
525 buf
:= new(rleBuffer
)
527 f
, err
:= w
.CreateHeader(&FileHeader
{
534 f
.(*fileWriter
).crc32
= fakeHash32
{}
535 chunk
:= make([]byte, chunkSize
)
536 for i
:= range chunk
{
539 for i
:= 0; i
< chunks
; i
++ {
540 _
, err
:= f
.Write(chunk
)
542 t
.Fatal("write chunk:", err
)
545 if frag
:= int(size
% chunkSize
); frag
> 0 {
546 _
, err
:= f
.Write(chunk
[:frag
])
548 t
.Fatal("write chunk:", err
)
551 end
:= []byte("END\n")
552 _
, err
= f
.Write(end
)
554 t
.Fatal("write end:", err
)
556 if err
:= w
.Close(); err
!= nil {
560 // read back zip file and check that we get to the end of it
561 r
, err
:= NewReader(buf
, int64(buf
.Size()))
563 t
.Fatal("reader:", err
)
568 t
.Fatal("opening:", err
)
570 rc
.(*checksumReader
).hash
= fakeHash32
{}
571 for i
:= 0; i
< chunks
; i
++ {
572 _
, err
:= io
.ReadFull(rc
, chunk
)
574 t
.Fatal("read:", err
)
577 if frag
:= int(size
% chunkSize
); frag
> 0 {
578 _
, err
:= io
.ReadFull(rc
, chunk
[:frag
])
580 t
.Fatal("read:", err
)
583 gotEnd
, err
:= ioutil
.ReadAll(rc
)
585 t
.Fatal("read end:", err
)
587 if !bytes
.Equal(gotEnd
, end
) {
588 t
.Errorf("End of zip64 archive %q, want %q", gotEnd
, end
)
592 t
.Fatal("closing:", err
)
594 if size
+int64(len("END\n")) >= 1<<32-1 {
595 if got
, want
:= f0
.UncompressedSize
, uint32(uint32max
); got
!= want
{
596 t
.Errorf("UncompressedSize %#x, want %#x", got
, want
)
600 if got
, want
:= f0
.UncompressedSize64
, uint64(size
)+uint64(len(end
)); got
!= want
{
601 t
.Errorf("UncompressedSize64 %#x, want %#x", got
, want
)
608 func testZip64DirectoryRecordLength(buf
*rleBuffer
, t
*testing
.T
) {
609 if !suffixIsZip64(t
, buf
) {
610 t
.Fatal("not a zip64")
614 func testValidHeader(h
*FileHeader
, t
*testing
.T
) {
618 f
, err
:= z
.CreateHeader(h
)
620 t
.Fatalf("error creating header: %v", err
)
622 if _
, err
:= f
.Write([]byte("hi")); err
!= nil {
623 t
.Fatalf("error writing content: %v", err
)
625 if err
:= z
.Close(); err
!= nil {
626 t
.Fatalf("error closing zip writer: %v", err
)
630 zf
, err
:= NewReader(bytes
.NewReader(b
), int64(len(b
)))
632 t
.Fatalf("got %v, expected nil", err
)
634 zh
:= zf
.File
[0].FileHeader
635 if zh
.Name
!= h
.Name || zh
.Method
!= h
.Method || zh
.UncompressedSize64
!= uint64(len("hi")) {
636 t
.Fatalf("got %q/%d/%d expected %q/%d/%d", zh
.Name
, zh
.Method
, zh
.UncompressedSize64
, h
.Name
, h
.Method
, len("hi"))
641 func TestHeaderInvalidTagAndSize(t
*testing
.T
) {
642 const timeFormat
= "20060102T150405.000.txt"
645 filename
:= ts
.Format(timeFormat
)
650 Extra
: []byte(ts
.Format(time
.RFC3339Nano
)), // missing tag and len, but Extra is best-effort parsing
654 testValidHeader(&h
, t
)
657 func TestHeaderTooShort(t
*testing
.T
) {
661 Extra
: []byte{zip64ExtraID
}, // missing size and second half of tag, but Extra is best-effort parsing
663 testValidHeader(&h
, t
)
666 func TestHeaderTooLongErr(t
*testing
.T
) {
667 var headerTests
= []struct {
673 name
: strings
.Repeat("x", 1<<16),
675 wanterr
: errLongName
,
679 extra
: bytes
.Repeat([]byte{0xff}, 1<<16),
680 wanterr
: errLongExtra
,
685 buf
:= new(bytes
.Buffer
)
688 for _
, test
:= range headerTests
{
693 _
, err
:= w
.CreateHeader(h
)
694 if err
!= test
.wanterr
{
695 t
.Errorf("error=%v, want %v", err
, test
.wanterr
)
699 if err
:= w
.Close(); err
!= nil {
704 func TestHeaderIgnoredSize(t
*testing
.T
) {
708 Extra
: []byte{zip64ExtraID
& 0xFF, zip64ExtraID
>> 8, 24, 0, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}, // bad size but shouldn't be consulted
710 testValidHeader(&h
, t
)
713 // Issue 4393. It is valid to have an extra data header
714 // which contains no body.
715 func TestZeroLengthHeader(t
*testing
.T
) {
717 Name
: "extadata.txt",
720 85, 84, 5, 0, 3, 154, 144, 195, 77, // tag 21589 size 5
721 85, 120, 0, 0, // tag 30805 size 0
724 testValidHeader(&h
, t
)
727 // Just benchmarking how fast the Zip64 test above is. Not related to
728 // our zip performance, since the test above disabled CRC32 and flate.
729 func BenchmarkZip64Test(b
*testing
.B
) {
730 for i
:= 0; i
< b
.N
; i
++ {
735 func BenchmarkZip64TestSizes(b
*testing
.B
) {
736 for _
, size
:= range []int64{1 << 12, 1 << 20, 1 << 26} {
737 b
.Run(fmt
.Sprint(size
), func(b
*testing
.B
) {
738 b
.RunParallel(func(pb
*testing
.PB
) {
747 func TestSuffixSaver(t
*testing
.T
) {
749 ss
:= &suffixSaver
{keep
: keep
}
750 ss
.Write([]byte("abc"))
751 if got
:= string(ss
.Suffix()); got
!= "abc" {
752 t
.Errorf("got = %q; want abc", got
)
754 ss
.Write([]byte("defghijklmno"))
755 if got
:= string(ss
.Suffix()); got
!= "fghijklmno" {
756 t
.Errorf("got = %q; want fghijklmno", got
)
758 if got
, want
:= ss
.Size(), int64(len("abc")+len("defghijklmno")); got
!= want
{
759 t
.Errorf("Size = %d; want %d", got
, want
)
761 buf
:= make([]byte, ss
.Size())
762 for off
:= int64(0); off
< ss
.Size(); off
++ {
763 for size
:= 1; size
<= int(ss
.Size()-off
); size
++ {
764 readBuf
:= buf
[:size
]
765 n
, err
:= ss
.ReadAt(readBuf
, off
)
766 if off
< ss
.Size()-keep
{
767 if err
!= errDiscardedBytes
{
768 t
.Errorf("off %d, size %d = %v, %v (%q); want errDiscardedBytes", off
, size
, n
, err
, readBuf
[:n
])
772 want
:= "abcdefghijklmno"[off
: off
+int64(size
)]
773 got
:= string(readBuf
[:n
])
774 if err
!= nil || got
!= want
{
775 t
.Errorf("off %d, size %d = %v, %v (%q); want %q", off
, size
, n
, err
, got
, want
)
784 func (zeros
) Read(p
[]byte) (int, error
) {