...
Run Format

Source file src/archive/zip/writer_test.go

Documentation: archive/zip

     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.
     4  
     5  package zip
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"math/rand"
    14  	"os"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  )
    19  
    20  // TODO(adg): a more sophisticated test suite
    21  
    22  type WriteTest struct {
    23  	Name   string
    24  	Data   []byte
    25  	Method uint16
    26  	Mode   os.FileMode
    27  }
    28  
    29  var writeTests = []WriteTest{
    30  	{
    31  		Name:   "foo",
    32  		Data:   []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."),
    33  		Method: Store,
    34  		Mode:   0666,
    35  	},
    36  	{
    37  		Name:   "bar",
    38  		Data:   nil, // large data set in the test
    39  		Method: Deflate,
    40  		Mode:   0644,
    41  	},
    42  	{
    43  		Name:   "setuid",
    44  		Data:   []byte("setuid file"),
    45  		Method: Deflate,
    46  		Mode:   0755 | os.ModeSetuid,
    47  	},
    48  	{
    49  		Name:   "setgid",
    50  		Data:   []byte("setgid file"),
    51  		Method: Deflate,
    52  		Mode:   0755 | os.ModeSetgid,
    53  	},
    54  	{
    55  		Name:   "symlink",
    56  		Data:   []byte("../link/target"),
    57  		Method: Deflate,
    58  		Mode:   0755 | os.ModeSymlink,
    59  	},
    60  }
    61  
    62  func TestWriter(t *testing.T) {
    63  	largeData := make([]byte, 1<<17)
    64  	if _, err := rand.Read(largeData); err != nil {
    65  		t.Fatal("rand.Read failed:", err)
    66  	}
    67  	writeTests[1].Data = largeData
    68  	defer func() {
    69  		writeTests[1].Data = nil
    70  	}()
    71  
    72  	// write a zip file
    73  	buf := new(bytes.Buffer)
    74  	w := NewWriter(buf)
    75  
    76  	for _, wt := range writeTests {
    77  		testCreate(t, w, &wt)
    78  	}
    79  
    80  	if err := w.Close(); err != nil {
    81  		t.Fatal(err)
    82  	}
    83  
    84  	// read it back
    85  	r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
    86  	if err != nil {
    87  		t.Fatal(err)
    88  	}
    89  	for i, wt := range writeTests {
    90  		testReadFile(t, r.File[i], &wt)
    91  	}
    92  }
    93  
    94  // TestWriterComment is test for EOCD comment read/write.
    95  func TestWriterComment(t *testing.T) {
    96  	var tests = []struct {
    97  		comment string
    98  		ok      bool
    99  	}{
   100  		{"hi, hello", true},
   101  		{"hi, こんにちわ", true},
   102  		{strings.Repeat("a", uint16max), true},
   103  		{strings.Repeat("a", uint16max+1), false},
   104  	}
   105  
   106  	for _, test := range tests {
   107  		// write a zip file
   108  		buf := new(bytes.Buffer)
   109  		w := NewWriter(buf)
   110  		if err := w.SetComment(test.comment); err != nil {
   111  			if test.ok {
   112  				t.Fatalf("SetComment: unexpected error %v", err)
   113  			}
   114  			continue
   115  		} else {
   116  			if !test.ok {
   117  				t.Fatalf("SetComment: unexpected success, want error")
   118  			}
   119  		}
   120  
   121  		if err := w.Close(); test.ok == (err != nil) {
   122  			t.Fatal(err)
   123  		}
   124  
   125  		if w.closed != test.ok {
   126  			t.Fatalf("Writer.closed: got %v, want %v", w.closed, test.ok)
   127  		}
   128  
   129  		// skip read test in failure cases
   130  		if !test.ok {
   131  			continue
   132  		}
   133  
   134  		// read it back
   135  		r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
   136  		if err != nil {
   137  			t.Fatal(err)
   138  		}
   139  		if r.Comment != test.comment {
   140  			t.Fatalf("Reader.Comment: got %v, want %v", r.Comment, test.comment)
   141  		}
   142  	}
   143  }
   144  
   145  func TestWriterUTF8(t *testing.T) {
   146  	var utf8Tests = []struct {
   147  		name    string
   148  		comment string
   149  		nonUTF8 bool
   150  		flags   uint16
   151  	}{
   152  		{
   153  			name:    "hi, hello",
   154  			comment: "in the world",
   155  			flags:   0x8,
   156  		},
   157  		{
   158  			name:    "hi, こんにちわ",
   159  			comment: "in the world",
   160  			flags:   0x808,
   161  		},
   162  		{
   163  			name:    "hi, こんにちわ",
   164  			comment: "in the world",
   165  			nonUTF8: true,
   166  			flags:   0x8,
   167  		},
   168  		{
   169  			name:    "hi, hello",
   170  			comment: "in the 世界",
   171  			flags:   0x808,
   172  		},
   173  		{
   174  			name:    "hi, こんにちわ",
   175  			comment: "in the 世界",
   176  			flags:   0x808,
   177  		},
   178  		{
   179  			name:    "the replacement rune is �",
   180  			comment: "the replacement rune is �",
   181  			flags:   0x808,
   182  		},
   183  		{
   184  			// Name is Japanese encoded in Shift JIS.
   185  			name:    "\x93\xfa\x96{\x8c\xea.txt",
   186  			comment: "in the 世界",
   187  			flags:   0x008, // UTF-8 must not be set
   188  		},
   189  	}
   190  
   191  	// write a zip file
   192  	buf := new(bytes.Buffer)
   193  	w := NewWriter(buf)
   194  
   195  	for _, test := range utf8Tests {
   196  		h := &FileHeader{
   197  			Name:    test.name,
   198  			Comment: test.comment,
   199  			NonUTF8: test.nonUTF8,
   200  			Method:  Deflate,
   201  		}
   202  		w, err := w.CreateHeader(h)
   203  		if err != nil {
   204  			t.Fatal(err)
   205  		}
   206  		w.Write([]byte{})
   207  	}
   208  
   209  	if err := w.Close(); err != nil {
   210  		t.Fatal(err)
   211  	}
   212  
   213  	// read it back
   214  	r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
   215  	if err != nil {
   216  		t.Fatal(err)
   217  	}
   218  	for i, test := range utf8Tests {
   219  		flags := r.File[i].Flags
   220  		if flags != test.flags {
   221  			t.Errorf("CreateHeader(name=%q comment=%q nonUTF8=%v): flags=%#x, want %#x", test.name, test.comment, test.nonUTF8, flags, test.flags)
   222  		}
   223  	}
   224  }
   225  
   226  func TestWriterTime(t *testing.T) {
   227  	var buf bytes.Buffer
   228  	h := &FileHeader{
   229  		Name:     "test.txt",
   230  		Modified: time.Date(2017, 10, 31, 21, 11, 57, 0, timeZone(-7*time.Hour)),
   231  	}
   232  	w := NewWriter(&buf)
   233  	if _, err := w.CreateHeader(h); err != nil {
   234  		t.Fatalf("unexpected CreateHeader error: %v", err)
   235  	}
   236  	if err := w.Close(); err != nil {
   237  		t.Fatalf("unexpected Close error: %v", err)
   238  	}
   239  
   240  	want, err := ioutil.ReadFile("testdata/time-go.zip")
   241  	if err != nil {
   242  		t.Fatalf("unexpected ReadFile error: %v", err)
   243  	}
   244  	if got := buf.Bytes(); !bytes.Equal(got, want) {
   245  		fmt.Printf("%x\n%x\n", got, want)
   246  		t.Error("contents of time-go.zip differ")
   247  	}
   248  }
   249  
   250  func TestWriterOffset(t *testing.T) {
   251  	largeData := make([]byte, 1<<17)
   252  	if _, err := rand.Read(largeData); err != nil {
   253  		t.Fatal("rand.Read failed:", err)
   254  	}
   255  	writeTests[1].Data = largeData
   256  	defer func() {
   257  		writeTests[1].Data = nil
   258  	}()
   259  
   260  	// write a zip file
   261  	buf := new(bytes.Buffer)
   262  	existingData := []byte{1, 2, 3, 1, 2, 3, 1, 2, 3}
   263  	n, _ := buf.Write(existingData)
   264  	w := NewWriter(buf)
   265  	w.SetOffset(int64(n))
   266  
   267  	for _, wt := range writeTests {
   268  		testCreate(t, w, &wt)
   269  	}
   270  
   271  	if err := w.Close(); err != nil {
   272  		t.Fatal(err)
   273  	}
   274  
   275  	// read it back
   276  	r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
   277  	if err != nil {
   278  		t.Fatal(err)
   279  	}
   280  	for i, wt := range writeTests {
   281  		testReadFile(t, r.File[i], &wt)
   282  	}
   283  }
   284  
   285  func TestWriterFlush(t *testing.T) {
   286  	var buf bytes.Buffer
   287  	w := NewWriter(struct{ io.Writer }{&buf})
   288  	_, err := w.Create("foo")
   289  	if err != nil {
   290  		t.Fatal(err)
   291  	}
   292  	if buf.Len() > 0 {
   293  		t.Fatalf("Unexpected %d bytes already in buffer", buf.Len())
   294  	}
   295  	if err := w.Flush(); err != nil {
   296  		t.Fatal(err)
   297  	}
   298  	if buf.Len() == 0 {
   299  		t.Fatal("No bytes written after Flush")
   300  	}
   301  }
   302  
   303  func TestWriterDir(t *testing.T) {
   304  	w := NewWriter(ioutil.Discard)
   305  	dw, err := w.Create("dir/")
   306  	if err != nil {
   307  		t.Fatal(err)
   308  	}
   309  	if _, err := dw.Write(nil); err != nil {
   310  		t.Errorf("Write(nil) to directory: got %v, want nil", err)
   311  	}
   312  	if _, err := dw.Write([]byte("hello")); err == nil {
   313  		t.Error(`Write("hello") to directory: got nil error, want non-nil`)
   314  	}
   315  }
   316  
   317  func TestWriterDirAttributes(t *testing.T) {
   318  	var buf bytes.Buffer
   319  	w := NewWriter(&buf)
   320  	if _, err := w.CreateHeader(&FileHeader{
   321  		Name:               "dir/",
   322  		Method:             Deflate,
   323  		CompressedSize64:   1234,
   324  		UncompressedSize64: 5678,
   325  	}); err != nil {
   326  		t.Fatal(err)
   327  	}
   328  	if err := w.Close(); err != nil {
   329  		t.Fatal(err)
   330  	}
   331  	b := buf.Bytes()
   332  
   333  	var sig [4]byte
   334  	binary.LittleEndian.PutUint32(sig[:], uint32(fileHeaderSignature))
   335  
   336  	idx := bytes.Index(b, sig[:])
   337  	if idx == -1 {
   338  		t.Fatal("file header not found")
   339  	}
   340  	b = b[idx:]
   341  
   342  	if !bytes.Equal(b[6:10], []byte{0, 0, 0, 0}) { // FileHeader.Flags: 0, FileHeader.Method: 0
   343  		t.Errorf("unexpected method and flags: %v", b[6:10])
   344  	}
   345  
   346  	if !bytes.Equal(b[14:26], make([]byte, 12)) { // FileHeader.{CRC32,CompressSize,UncompressedSize} all zero.
   347  		t.Errorf("unexpected crc, compress and uncompressed size to be 0 was: %v", b[14:26])
   348  	}
   349  
   350  	binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature))
   351  	if bytes.Index(b, sig[:]) != -1 {
   352  		t.Error("there should be no data descriptor")
   353  	}
   354  }
   355  
   356  func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
   357  	header := &FileHeader{
   358  		Name:   wt.Name,
   359  		Method: wt.Method,
   360  	}
   361  	if wt.Mode != 0 {
   362  		header.SetMode(wt.Mode)
   363  	}
   364  	f, err := w.CreateHeader(header)
   365  	if err != nil {
   366  		t.Fatal(err)
   367  	}
   368  	_, err = f.Write(wt.Data)
   369  	if err != nil {
   370  		t.Fatal(err)
   371  	}
   372  }
   373  
   374  func testReadFile(t *testing.T, f *File, wt *WriteTest) {
   375  	if f.Name != wt.Name {
   376  		t.Fatalf("File name: got %q, want %q", f.Name, wt.Name)
   377  	}
   378  	testFileMode(t, f, wt.Mode)
   379  	rc, err := f.Open()
   380  	if err != nil {
   381  		t.Fatal("opening:", err)
   382  	}
   383  	b, err := ioutil.ReadAll(rc)
   384  	if err != nil {
   385  		t.Fatal("reading:", err)
   386  	}
   387  	err = rc.Close()
   388  	if err != nil {
   389  		t.Fatal("closing:", err)
   390  	}
   391  	if !bytes.Equal(b, wt.Data) {
   392  		t.Errorf("File contents %q, want %q", b, wt.Data)
   393  	}
   394  }
   395  
   396  func BenchmarkCompressedZipGarbage(b *testing.B) {
   397  	bigBuf := bytes.Repeat([]byte("a"), 1<<20)
   398  
   399  	runOnce := func(buf *bytes.Buffer) {
   400  		buf.Reset()
   401  		zw := NewWriter(buf)
   402  		for j := 0; j < 3; j++ {
   403  			w, _ := zw.CreateHeader(&FileHeader{
   404  				Name:   "foo",
   405  				Method: Deflate,
   406  			})
   407  			w.Write(bigBuf)
   408  		}
   409  		zw.Close()
   410  	}
   411  
   412  	b.ReportAllocs()
   413  	// Run once and then reset the timer.
   414  	// This effectively discards the very large initial flate setup cost,
   415  	// as well as the initialization of bigBuf.
   416  	runOnce(&bytes.Buffer{})
   417  	b.ResetTimer()
   418  
   419  	b.RunParallel(func(pb *testing.PB) {
   420  		var buf bytes.Buffer
   421  		for pb.Next() {
   422  			runOnce(&buf)
   423  		}
   424  	})
   425  }
   426  

View as plain text