1
2
3
4
5 package zip
6
7 import (
8 "bytes"
9 "compress/flate"
10 "encoding/binary"
11 "fmt"
12 "hash/crc32"
13 "io"
14 "io/fs"
15 "math/rand"
16 "os"
17 "strings"
18 "testing"
19 "time"
20 )
21
22
23
24 type WriteTest struct {
25 Name string
26 Data []byte
27 Method uint16
28 Mode fs.FileMode
29 }
30
31 var writeTests = []WriteTest{
32 {
33 Name: "foo",
34 Data: []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."),
35 Method: Store,
36 Mode: 0666,
37 },
38 {
39 Name: "bar",
40 Data: nil,
41 Method: Deflate,
42 Mode: 0644,
43 },
44 {
45 Name: "setuid",
46 Data: []byte("setuid file"),
47 Method: Deflate,
48 Mode: 0755 | fs.ModeSetuid,
49 },
50 {
51 Name: "setgid",
52 Data: []byte("setgid file"),
53 Method: Deflate,
54 Mode: 0755 | fs.ModeSetgid,
55 },
56 {
57 Name: "symlink",
58 Data: []byte("../link/target"),
59 Method: Deflate,
60 Mode: 0755 | fs.ModeSymlink,
61 },
62 {
63 Name: "device",
64 Data: []byte("device file"),
65 Method: Deflate,
66 Mode: 0755 | fs.ModeDevice,
67 },
68 {
69 Name: "chardevice",
70 Data: []byte("char device file"),
71 Method: Deflate,
72 Mode: 0755 | fs.ModeDevice | fs.ModeCharDevice,
73 },
74 }
75
76 func TestWriter(t *testing.T) {
77 largeData := make([]byte, 1<<17)
78 if _, err := rand.Read(largeData); err != nil {
79 t.Fatal("rand.Read failed:", err)
80 }
81 writeTests[1].Data = largeData
82 defer func() {
83 writeTests[1].Data = nil
84 }()
85
86
87 buf := new(bytes.Buffer)
88 w := NewWriter(buf)
89
90 for _, wt := range writeTests {
91 testCreate(t, w, &wt)
92 }
93
94 if err := w.Close(); err != nil {
95 t.Fatal(err)
96 }
97
98
99 r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
100 if err != nil {
101 t.Fatal(err)
102 }
103 for i, wt := range writeTests {
104 testReadFile(t, r.File[i], &wt)
105 }
106 }
107
108
109 func TestWriterComment(t *testing.T) {
110 var tests = []struct {
111 comment string
112 ok bool
113 }{
114 {"hi, hello", true},
115 {"hi, こんにちわ", true},
116 {strings.Repeat("a", uint16max), true},
117 {strings.Repeat("a", uint16max+1), false},
118 }
119
120 for _, test := range tests {
121
122 buf := new(bytes.Buffer)
123 w := NewWriter(buf)
124 if err := w.SetComment(test.comment); err != nil {
125 if test.ok {
126 t.Fatalf("SetComment: unexpected error %v", err)
127 }
128 continue
129 } else {
130 if !test.ok {
131 t.Fatalf("SetComment: unexpected success, want error")
132 }
133 }
134
135 if err := w.Close(); test.ok == (err != nil) {
136 t.Fatal(err)
137 }
138
139 if w.closed != test.ok {
140 t.Fatalf("Writer.closed: got %v, want %v", w.closed, test.ok)
141 }
142
143
144 if !test.ok {
145 continue
146 }
147
148
149 r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
150 if err != nil {
151 t.Fatal(err)
152 }
153 if r.Comment != test.comment {
154 t.Fatalf("Reader.Comment: got %v, want %v", r.Comment, test.comment)
155 }
156 }
157 }
158
159 func TestWriterUTF8(t *testing.T) {
160 var utf8Tests = []struct {
161 name string
162 comment string
163 nonUTF8 bool
164 flags uint16
165 }{
166 {
167 name: "hi, hello",
168 comment: "in the world",
169 flags: 0x8,
170 },
171 {
172 name: "hi, こんにちわ",
173 comment: "in the world",
174 flags: 0x808,
175 },
176 {
177 name: "hi, こんにちわ",
178 comment: "in the world",
179 nonUTF8: true,
180 flags: 0x8,
181 },
182 {
183 name: "hi, hello",
184 comment: "in the 世界",
185 flags: 0x808,
186 },
187 {
188 name: "hi, こんにちわ",
189 comment: "in the 世界",
190 flags: 0x808,
191 },
192 {
193 name: "the replacement rune is �",
194 comment: "the replacement rune is �",
195 flags: 0x808,
196 },
197 {
198
199 name: "\x93\xfa\x96{\x8c\xea.txt",
200 comment: "in the 世界",
201 flags: 0x008,
202 },
203 }
204
205
206 buf := new(bytes.Buffer)
207 w := NewWriter(buf)
208
209 for _, test := range utf8Tests {
210 h := &FileHeader{
211 Name: test.name,
212 Comment: test.comment,
213 NonUTF8: test.nonUTF8,
214 Method: Deflate,
215 }
216 w, err := w.CreateHeader(h)
217 if err != nil {
218 t.Fatal(err)
219 }
220 w.Write([]byte{})
221 }
222
223 if err := w.Close(); err != nil {
224 t.Fatal(err)
225 }
226
227
228 r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
229 if err != nil {
230 t.Fatal(err)
231 }
232 for i, test := range utf8Tests {
233 flags := r.File[i].Flags
234 if flags != test.flags {
235 t.Errorf("CreateHeader(name=%q comment=%q nonUTF8=%v): flags=%#x, want %#x", test.name, test.comment, test.nonUTF8, flags, test.flags)
236 }
237 }
238 }
239
240 func TestWriterTime(t *testing.T) {
241 var buf bytes.Buffer
242 h := &FileHeader{
243 Name: "test.txt",
244 Modified: time.Date(2017, 10, 31, 21, 11, 57, 0, timeZone(-7*time.Hour)),
245 }
246 w := NewWriter(&buf)
247 if _, err := w.CreateHeader(h); err != nil {
248 t.Fatalf("unexpected CreateHeader error: %v", err)
249 }
250 if err := w.Close(); err != nil {
251 t.Fatalf("unexpected Close error: %v", err)
252 }
253
254 want, err := os.ReadFile("testdata/time-go.zip")
255 if err != nil {
256 t.Fatalf("unexpected ReadFile error: %v", err)
257 }
258 if got := buf.Bytes(); !bytes.Equal(got, want) {
259 fmt.Printf("%x\n%x\n", got, want)
260 t.Error("contents of time-go.zip differ")
261 }
262 }
263
264 func TestWriterOffset(t *testing.T) {
265 largeData := make([]byte, 1<<17)
266 if _, err := rand.Read(largeData); err != nil {
267 t.Fatal("rand.Read failed:", err)
268 }
269 writeTests[1].Data = largeData
270 defer func() {
271 writeTests[1].Data = nil
272 }()
273
274
275 buf := new(bytes.Buffer)
276 existingData := []byte{1, 2, 3, 1, 2, 3, 1, 2, 3}
277 n, _ := buf.Write(existingData)
278 w := NewWriter(buf)
279 w.SetOffset(int64(n))
280
281 for _, wt := range writeTests {
282 testCreate(t, w, &wt)
283 }
284
285 if err := w.Close(); err != nil {
286 t.Fatal(err)
287 }
288
289
290 r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
291 if err != nil {
292 t.Fatal(err)
293 }
294 for i, wt := range writeTests {
295 testReadFile(t, r.File[i], &wt)
296 }
297 }
298
299 func TestWriterFlush(t *testing.T) {
300 var buf bytes.Buffer
301 w := NewWriter(struct{ io.Writer }{&buf})
302 _, err := w.Create("foo")
303 if err != nil {
304 t.Fatal(err)
305 }
306 if buf.Len() > 0 {
307 t.Fatalf("Unexpected %d bytes already in buffer", buf.Len())
308 }
309 if err := w.Flush(); err != nil {
310 t.Fatal(err)
311 }
312 if buf.Len() == 0 {
313 t.Fatal("No bytes written after Flush")
314 }
315 }
316
317 func TestWriterDir(t *testing.T) {
318 w := NewWriter(io.Discard)
319 dw, err := w.Create("dir/")
320 if err != nil {
321 t.Fatal(err)
322 }
323 if _, err := dw.Write(nil); err != nil {
324 t.Errorf("Write(nil) to directory: got %v, want nil", err)
325 }
326 if _, err := dw.Write([]byte("hello")); err == nil {
327 t.Error(`Write("hello") to directory: got nil error, want non-nil`)
328 }
329 }
330
331 func TestWriterDirAttributes(t *testing.T) {
332 var buf bytes.Buffer
333 w := NewWriter(&buf)
334 if _, err := w.CreateHeader(&FileHeader{
335 Name: "dir/",
336 Method: Deflate,
337 CompressedSize64: 1234,
338 UncompressedSize64: 5678,
339 }); err != nil {
340 t.Fatal(err)
341 }
342 if err := w.Close(); err != nil {
343 t.Fatal(err)
344 }
345 b := buf.Bytes()
346
347 var sig [4]byte
348 binary.LittleEndian.PutUint32(sig[:], uint32(fileHeaderSignature))
349
350 idx := bytes.Index(b, sig[:])
351 if idx == -1 {
352 t.Fatal("file header not found")
353 }
354 b = b[idx:]
355
356 if !bytes.Equal(b[6:10], []byte{0, 0, 0, 0}) {
357 t.Errorf("unexpected method and flags: %v", b[6:10])
358 }
359
360 if !bytes.Equal(b[14:26], make([]byte, 12)) {
361 t.Errorf("unexpected crc, compress and uncompressed size to be 0 was: %v", b[14:26])
362 }
363
364 binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature))
365 if bytes.Contains(b, sig[:]) {
366 t.Error("there should be no data descriptor")
367 }
368 }
369
370 func TestWriterCopy(t *testing.T) {
371 want, err := os.ReadFile("testdata/test.zip")
372 if err != nil {
373 t.Fatalf("unexpected ReadFile error: %v", err)
374 }
375 r, err := NewReader(bytes.NewReader(want), int64(len(want)))
376 if err != nil {
377 t.Fatalf("unexpected NewReader error: %v", err)
378 }
379 var buf bytes.Buffer
380 w := NewWriter(&buf)
381 for _, f := range r.File {
382 err := w.Copy(f)
383 if err != nil {
384 t.Fatalf("unexpected Copy error: %v", err)
385 }
386 }
387 if err := w.Close(); err != nil {
388 t.Fatalf("unexpected Close error: %v", err)
389 }
390
391
392 got := buf.Bytes()
393 r2, err := NewReader(bytes.NewReader(got), int64(len(got)))
394 if err != nil {
395 t.Fatalf("unexpected NewReader error: %v", err)
396 }
397 for i, fWAnt := range r.File {
398 wantR, err := fWAnt.Open()
399 if err != nil {
400 t.Fatalf("unexpected Open error: %v", err)
401 }
402 want, err := io.ReadAll(wantR)
403 if err != nil {
404 t.Fatalf("unexpected Copy error: %v", err)
405 }
406
407 fGot := r2.File[i]
408 gotR, err := fGot.Open()
409 if err != nil {
410 t.Fatalf("unexpected Open error: %v", err)
411 }
412 got, err := io.ReadAll(gotR)
413 if err != nil {
414 t.Fatalf("unexpected Copy error: %v", err)
415 }
416 if !bytes.Equal(got, want) {
417 fmt.Printf("%x\n%x\n", got, want)
418 t.Error("contents of copied mismatch")
419 }
420 }
421 }
422
423 func TestWriterCopy2(t *testing.T) {
424
425 buf := new(bytes.Buffer)
426 w := NewWriter(buf)
427 for _, wt := range writeTests {
428 testCreate(t, w, &wt)
429 }
430 if err := w.Close(); err != nil {
431 t.Fatal(err)
432 }
433
434
435 src, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
436 if err != nil {
437 t.Fatal(err)
438 }
439 for i, wt := range writeTests {
440 testReadFile(t, src.File[i], &wt)
441 }
442
443
444 buf2 := new(bytes.Buffer)
445 dst := NewWriter(buf2)
446 for _, f := range src.File {
447 if err := dst.Copy(f); err != nil {
448 t.Fatal(err)
449 }
450 }
451 if err := dst.Close(); err != nil {
452 t.Fatal(err)
453 }
454
455
456 r, err := NewReader(bytes.NewReader(buf2.Bytes()), int64(buf2.Len()))
457 if err != nil {
458 t.Fatal(err)
459 }
460 for i, wt := range writeTests {
461 testReadFile(t, r.File[i], &wt)
462 }
463 }
464
465 func TestWriterCreateRaw(t *testing.T) {
466 files := []struct {
467 name string
468 content []byte
469 method uint16
470 flags uint16
471 crc32 uint32
472 uncompressedSize uint64
473 compressedSize uint64
474 }{
475 {
476 name: "small store w desc",
477 content: []byte("gophers"),
478 method: Store,
479 flags: 0x8,
480 },
481 {
482 name: "small deflate wo desc",
483 content: bytes.Repeat([]byte("abcdefg"), 2048),
484 method: Deflate,
485 },
486 }
487
488
489 archive := new(bytes.Buffer)
490 w := NewWriter(archive)
491
492 for i := range files {
493 f := &files[i]
494 f.crc32 = crc32.ChecksumIEEE(f.content)
495 size := uint64(len(f.content))
496 f.uncompressedSize = size
497 f.compressedSize = size
498
499 var compressedContent []byte
500 if f.method == Deflate {
501 var buf bytes.Buffer
502 w, err := flate.NewWriter(&buf, flate.BestSpeed)
503 if err != nil {
504 t.Fatalf("flate.NewWriter err = %v", err)
505 }
506 _, err = w.Write(f.content)
507 if err != nil {
508 t.Fatalf("flate Write err = %v", err)
509 }
510 err = w.Close()
511 if err != nil {
512 t.Fatalf("flate Writer.Close err = %v", err)
513 }
514 compressedContent = buf.Bytes()
515 f.compressedSize = uint64(len(compressedContent))
516 }
517
518 h := &FileHeader{
519 Name: f.name,
520 Method: f.method,
521 Flags: f.flags,
522 CRC32: f.crc32,
523 CompressedSize64: f.compressedSize,
524 UncompressedSize64: f.uncompressedSize,
525 }
526 w, err := w.CreateRaw(h)
527 if err != nil {
528 t.Fatal(err)
529 }
530 if compressedContent != nil {
531 _, err = w.Write(compressedContent)
532 } else {
533 _, err = w.Write(f.content)
534 }
535 if err != nil {
536 t.Fatalf("%s Write got %v; want nil", f.name, err)
537 }
538 }
539
540 if err := w.Close(); err != nil {
541 t.Fatal(err)
542 }
543
544
545 r, err := NewReader(bytes.NewReader(archive.Bytes()), int64(archive.Len()))
546 if err != nil {
547 t.Fatal(err)
548 }
549 for i, want := range files {
550 got := r.File[i]
551 if got.Name != want.name {
552 t.Errorf("got Name %s; want %s", got.Name, want.name)
553 }
554 if got.Method != want.method {
555 t.Errorf("%s: got Method %#x; want %#x", want.name, got.Method, want.method)
556 }
557 if got.Flags != want.flags {
558 t.Errorf("%s: got Flags %#x; want %#x", want.name, got.Flags, want.flags)
559 }
560 if got.CRC32 != want.crc32 {
561 t.Errorf("%s: got CRC32 %#x; want %#x", want.name, got.CRC32, want.crc32)
562 }
563 if got.CompressedSize64 != want.compressedSize {
564 t.Errorf("%s: got CompressedSize64 %d; want %d", want.name, got.CompressedSize64, want.compressedSize)
565 }
566 if got.UncompressedSize64 != want.uncompressedSize {
567 t.Errorf("%s: got UncompressedSize64 %d; want %d", want.name, got.UncompressedSize64, want.uncompressedSize)
568 }
569
570 r, err := got.Open()
571 if err != nil {
572 t.Errorf("%s: Open err = %v", got.Name, err)
573 continue
574 }
575
576 buf, err := io.ReadAll(r)
577 if err != nil {
578 t.Errorf("%s: ReadAll err = %v", got.Name, err)
579 continue
580 }
581
582 if !bytes.Equal(buf, want.content) {
583 t.Errorf("%v: ReadAll returned unexpected bytes", got.Name)
584 }
585 }
586 }
587
588 func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
589 header := &FileHeader{
590 Name: wt.Name,
591 Method: wt.Method,
592 }
593 if wt.Mode != 0 {
594 header.SetMode(wt.Mode)
595 }
596 f, err := w.CreateHeader(header)
597 if err != nil {
598 t.Fatal(err)
599 }
600 _, err = f.Write(wt.Data)
601 if err != nil {
602 t.Fatal(err)
603 }
604 }
605
606 func testReadFile(t *testing.T, f *File, wt *WriteTest) {
607 if f.Name != wt.Name {
608 t.Fatalf("File name: got %q, want %q", f.Name, wt.Name)
609 }
610 testFileMode(t, f, wt.Mode)
611 rc, err := f.Open()
612 if err != nil {
613 t.Fatalf("opening %s: %v", f.Name, err)
614 }
615 b, err := io.ReadAll(rc)
616 if err != nil {
617 t.Fatalf("reading %s: %v", f.Name, err)
618 }
619 err = rc.Close()
620 if err != nil {
621 t.Fatalf("closing %s: %v", f.Name, err)
622 }
623 if !bytes.Equal(b, wt.Data) {
624 t.Errorf("File contents %q, want %q", b, wt.Data)
625 }
626 }
627
628 func BenchmarkCompressedZipGarbage(b *testing.B) {
629 bigBuf := bytes.Repeat([]byte("a"), 1<<20)
630
631 runOnce := func(buf *bytes.Buffer) {
632 buf.Reset()
633 zw := NewWriter(buf)
634 for j := 0; j < 3; j++ {
635 w, _ := zw.CreateHeader(&FileHeader{
636 Name: "foo",
637 Method: Deflate,
638 })
639 w.Write(bigBuf)
640 }
641 zw.Close()
642 }
643
644 b.ReportAllocs()
645
646
647
648 runOnce(&bytes.Buffer{})
649 b.ResetTimer()
650
651 b.RunParallel(func(pb *testing.PB) {
652 var buf bytes.Buffer
653 for pb.Next() {
654 runOnce(&buf)
655 }
656 })
657 }
658
View as plain text