Source file
src/archive/zip/writer_test.go
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.Index(b, sig[:]) != -1 {
366 t.Error("there should be no data descriptor")
367 }
368 }
369
370 func TestWriterCopy(t *testing.T) {
371
372 buf := new(bytes.Buffer)
373 w := NewWriter(buf)
374 for _, wt := range writeTests {
375 testCreate(t, w, &wt)
376 }
377 if err := w.Close(); err != nil {
378 t.Fatal(err)
379 }
380
381
382 src, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
383 if err != nil {
384 t.Fatal(err)
385 }
386 for i, wt := range writeTests {
387 testReadFile(t, src.File[i], &wt)
388 }
389
390
391 buf2 := new(bytes.Buffer)
392 dst := NewWriter(buf2)
393 for _, f := range src.File {
394 if err := dst.Copy(f); err != nil {
395 t.Fatal(err)
396 }
397 }
398 if err := dst.Close(); err != nil {
399 t.Fatal(err)
400 }
401
402
403 r, err := NewReader(bytes.NewReader(buf2.Bytes()), int64(buf2.Len()))
404 if err != nil {
405 t.Fatal(err)
406 }
407 for i, wt := range writeTests {
408 testReadFile(t, r.File[i], &wt)
409 }
410 }
411
412 func TestWriterCreateRaw(t *testing.T) {
413 files := []struct {
414 name string
415 content []byte
416 method uint16
417 flags uint16
418 crc32 uint32
419 uncompressedSize uint64
420 compressedSize uint64
421 }{
422 {
423 name: "small store w desc",
424 content: []byte("gophers"),
425 method: Store,
426 flags: 0x8,
427 },
428 {
429 name: "small deflate wo desc",
430 content: bytes.Repeat([]byte("abcdefg"), 2048),
431 method: Deflate,
432 },
433 }
434
435
436 archive := new(bytes.Buffer)
437 w := NewWriter(archive)
438
439 for i := range files {
440 f := &files[i]
441 f.crc32 = crc32.ChecksumIEEE(f.content)
442 size := uint64(len(f.content))
443 f.uncompressedSize = size
444 f.compressedSize = size
445
446 var compressedContent []byte
447 if f.method == Deflate {
448 var buf bytes.Buffer
449 w, err := flate.NewWriter(&buf, flate.BestSpeed)
450 if err != nil {
451 t.Fatalf("flate.NewWriter err = %v", err)
452 }
453 _, err = w.Write(f.content)
454 if err != nil {
455 t.Fatalf("flate Write err = %v", err)
456 }
457 err = w.Close()
458 if err != nil {
459 t.Fatalf("flate Writer.Close err = %v", err)
460 }
461 compressedContent = buf.Bytes()
462 f.compressedSize = uint64(len(compressedContent))
463 }
464
465 h := &FileHeader{
466 Name: f.name,
467 Method: f.method,
468 Flags: f.flags,
469 CRC32: f.crc32,
470 CompressedSize64: f.compressedSize,
471 UncompressedSize64: f.uncompressedSize,
472 }
473 w, err := w.CreateRaw(h)
474 if err != nil {
475 t.Fatal(err)
476 }
477 if compressedContent != nil {
478 _, err = w.Write(compressedContent)
479 } else {
480 _, err = w.Write(f.content)
481 }
482 if err != nil {
483 t.Fatalf("%s Write got %v; want nil", f.name, err)
484 }
485 }
486
487 if err := w.Close(); err != nil {
488 t.Fatal(err)
489 }
490
491
492 r, err := NewReader(bytes.NewReader(archive.Bytes()), int64(archive.Len()))
493 if err != nil {
494 t.Fatal(err)
495 }
496 for i, want := range files {
497 got := r.File[i]
498 if got.Name != want.name {
499 t.Errorf("got Name %s; want %s", got.Name, want.name)
500 }
501 if got.Method != want.method {
502 t.Errorf("%s: got Method %#x; want %#x", want.name, got.Method, want.method)
503 }
504 if got.Flags != want.flags {
505 t.Errorf("%s: got Flags %#x; want %#x", want.name, got.Flags, want.flags)
506 }
507 if got.CRC32 != want.crc32 {
508 t.Errorf("%s: got CRC32 %#x; want %#x", want.name, got.CRC32, want.crc32)
509 }
510 if got.CompressedSize64 != want.compressedSize {
511 t.Errorf("%s: got CompressedSize64 %d; want %d", want.name, got.CompressedSize64, want.compressedSize)
512 }
513 if got.UncompressedSize64 != want.uncompressedSize {
514 t.Errorf("%s: got UncompressedSize64 %d; want %d", want.name, got.UncompressedSize64, want.uncompressedSize)
515 }
516
517 r, err := got.Open()
518 if err != nil {
519 t.Errorf("%s: Open err = %v", got.Name, err)
520 continue
521 }
522
523 buf, err := io.ReadAll(r)
524 if err != nil {
525 t.Errorf("%s: ReadAll err = %v", got.Name, err)
526 continue
527 }
528
529 if !bytes.Equal(buf, want.content) {
530 t.Errorf("%v: ReadAll returned unexpected bytes", got.Name)
531 }
532 }
533 }
534
535 func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
536 header := &FileHeader{
537 Name: wt.Name,
538 Method: wt.Method,
539 }
540 if wt.Mode != 0 {
541 header.SetMode(wt.Mode)
542 }
543 f, err := w.CreateHeader(header)
544 if err != nil {
545 t.Fatal(err)
546 }
547 _, err = f.Write(wt.Data)
548 if err != nil {
549 t.Fatal(err)
550 }
551 }
552
553 func testReadFile(t *testing.T, f *File, wt *WriteTest) {
554 if f.Name != wt.Name {
555 t.Fatalf("File name: got %q, want %q", f.Name, wt.Name)
556 }
557 testFileMode(t, f, wt.Mode)
558 rc, err := f.Open()
559 if err != nil {
560 t.Fatalf("opening %s: %v", f.Name, err)
561 }
562 b, err := io.ReadAll(rc)
563 if err != nil {
564 t.Fatalf("reading %s: %v", f.Name, err)
565 }
566 err = rc.Close()
567 if err != nil {
568 t.Fatalf("closing %s: %v", f.Name, err)
569 }
570 if !bytes.Equal(b, wt.Data) {
571 t.Errorf("File contents %q, want %q", b, wt.Data)
572 }
573 }
574
575 func BenchmarkCompressedZipGarbage(b *testing.B) {
576 bigBuf := bytes.Repeat([]byte("a"), 1<<20)
577
578 runOnce := func(buf *bytes.Buffer) {
579 buf.Reset()
580 zw := NewWriter(buf)
581 for j := 0; j < 3; j++ {
582 w, _ := zw.CreateHeader(&FileHeader{
583 Name: "foo",
584 Method: Deflate,
585 })
586 w.Write(bigBuf)
587 }
588 zw.Close()
589 }
590
591 b.ReportAllocs()
592
593
594
595 runOnce(&bytes.Buffer{})
596 b.ResetTimer()
597
598 b.RunParallel(func(pb *testing.PB) {
599 var buf bytes.Buffer
600 for pb.Next() {
601 runOnce(&buf)
602 }
603 })
604 }
605
View as plain text