mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
As part of #42026, these helpers from io/ioutil were moved to os. (ioutil.TempFile and TempDir became os.CreateTemp and MkdirTemp.) Update the Go tree to use the preferred names. As usual, code compiled with the Go 1.4 bootstrap toolchain and code vendored from other sources is excluded. ReadDir changes are in a separate CL, because they are not a simple search and replace. For #42026. Change-Id: If318df0216d57e95ea0c4093b89f65e5b0ababb3 Reviewed-on: https://go-review.googlesource.com/c/go/+/266365 Trust: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
425 lines
8.7 KiB
Go
425 lines
8.7 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
||
// Use of this source code is governed by a BSD-style
|
||
// license that can be found in the LICENSE file.
|
||
|
||
package zip
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/binary"
|
||
"fmt"
|
||
"io"
|
||
"io/fs"
|
||
"math/rand"
|
||
"os"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
// TODO(adg): a more sophisticated test suite
|
||
|
||
type WriteTest struct {
|
||
Name string
|
||
Data []byte
|
||
Method uint16
|
||
Mode fs.FileMode
|
||
}
|
||
|
||
var writeTests = []WriteTest{
|
||
{
|
||
Name: "foo",
|
||
Data: []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."),
|
||
Method: Store,
|
||
Mode: 0666,
|
||
},
|
||
{
|
||
Name: "bar",
|
||
Data: nil, // large data set in the test
|
||
Method: Deflate,
|
||
Mode: 0644,
|
||
},
|
||
{
|
||
Name: "setuid",
|
||
Data: []byte("setuid file"),
|
||
Method: Deflate,
|
||
Mode: 0755 | fs.ModeSetuid,
|
||
},
|
||
{
|
||
Name: "setgid",
|
||
Data: []byte("setgid file"),
|
||
Method: Deflate,
|
||
Mode: 0755 | fs.ModeSetgid,
|
||
},
|
||
{
|
||
Name: "symlink",
|
||
Data: []byte("../link/target"),
|
||
Method: Deflate,
|
||
Mode: 0755 | fs.ModeSymlink,
|
||
},
|
||
}
|
||
|
||
func TestWriter(t *testing.T) {
|
||
largeData := make([]byte, 1<<17)
|
||
if _, err := rand.Read(largeData); err != nil {
|
||
t.Fatal("rand.Read failed:", err)
|
||
}
|
||
writeTests[1].Data = largeData
|
||
defer func() {
|
||
writeTests[1].Data = nil
|
||
}()
|
||
|
||
// write a zip file
|
||
buf := new(bytes.Buffer)
|
||
w := NewWriter(buf)
|
||
|
||
for _, wt := range writeTests {
|
||
testCreate(t, w, &wt)
|
||
}
|
||
|
||
if err := w.Close(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
// read it back
|
||
r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
for i, wt := range writeTests {
|
||
testReadFile(t, r.File[i], &wt)
|
||
}
|
||
}
|
||
|
||
// TestWriterComment is test for EOCD comment read/write.
|
||
func TestWriterComment(t *testing.T) {
|
||
var tests = []struct {
|
||
comment string
|
||
ok bool
|
||
}{
|
||
{"hi, hello", true},
|
||
{"hi, こんにちわ", true},
|
||
{strings.Repeat("a", uint16max), true},
|
||
{strings.Repeat("a", uint16max+1), false},
|
||
}
|
||
|
||
for _, test := range tests {
|
||
// write a zip file
|
||
buf := new(bytes.Buffer)
|
||
w := NewWriter(buf)
|
||
if err := w.SetComment(test.comment); err != nil {
|
||
if test.ok {
|
||
t.Fatalf("SetComment: unexpected error %v", err)
|
||
}
|
||
continue
|
||
} else {
|
||
if !test.ok {
|
||
t.Fatalf("SetComment: unexpected success, want error")
|
||
}
|
||
}
|
||
|
||
if err := w.Close(); test.ok == (err != nil) {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
if w.closed != test.ok {
|
||
t.Fatalf("Writer.closed: got %v, want %v", w.closed, test.ok)
|
||
}
|
||
|
||
// skip read test in failure cases
|
||
if !test.ok {
|
||
continue
|
||
}
|
||
|
||
// read it back
|
||
r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if r.Comment != test.comment {
|
||
t.Fatalf("Reader.Comment: got %v, want %v", r.Comment, test.comment)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestWriterUTF8(t *testing.T) {
|
||
var utf8Tests = []struct {
|
||
name string
|
||
comment string
|
||
nonUTF8 bool
|
||
flags uint16
|
||
}{
|
||
{
|
||
name: "hi, hello",
|
||
comment: "in the world",
|
||
flags: 0x8,
|
||
},
|
||
{
|
||
name: "hi, こんにちわ",
|
||
comment: "in the world",
|
||
flags: 0x808,
|
||
},
|
||
{
|
||
name: "hi, こんにちわ",
|
||
comment: "in the world",
|
||
nonUTF8: true,
|
||
flags: 0x8,
|
||
},
|
||
{
|
||
name: "hi, hello",
|
||
comment: "in the 世界",
|
||
flags: 0x808,
|
||
},
|
||
{
|
||
name: "hi, こんにちわ",
|
||
comment: "in the 世界",
|
||
flags: 0x808,
|
||
},
|
||
{
|
||
name: "the replacement rune is <20>",
|
||
comment: "the replacement rune is <20>",
|
||
flags: 0x808,
|
||
},
|
||
{
|
||
// Name is Japanese encoded in Shift JIS.
|
||
name: "\x93\xfa\x96{\x8c\xea.txt",
|
||
comment: "in the 世界",
|
||
flags: 0x008, // UTF-8 must not be set
|
||
},
|
||
}
|
||
|
||
// write a zip file
|
||
buf := new(bytes.Buffer)
|
||
w := NewWriter(buf)
|
||
|
||
for _, test := range utf8Tests {
|
||
h := &FileHeader{
|
||
Name: test.name,
|
||
Comment: test.comment,
|
||
NonUTF8: test.nonUTF8,
|
||
Method: Deflate,
|
||
}
|
||
w, err := w.CreateHeader(h)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
w.Write([]byte{})
|
||
}
|
||
|
||
if err := w.Close(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
// read it back
|
||
r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
for i, test := range utf8Tests {
|
||
flags := r.File[i].Flags
|
||
if flags != test.flags {
|
||
t.Errorf("CreateHeader(name=%q comment=%q nonUTF8=%v): flags=%#x, want %#x", test.name, test.comment, test.nonUTF8, flags, test.flags)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestWriterTime(t *testing.T) {
|
||
var buf bytes.Buffer
|
||
h := &FileHeader{
|
||
Name: "test.txt",
|
||
Modified: time.Date(2017, 10, 31, 21, 11, 57, 0, timeZone(-7*time.Hour)),
|
||
}
|
||
w := NewWriter(&buf)
|
||
if _, err := w.CreateHeader(h); err != nil {
|
||
t.Fatalf("unexpected CreateHeader error: %v", err)
|
||
}
|
||
if err := w.Close(); err != nil {
|
||
t.Fatalf("unexpected Close error: %v", err)
|
||
}
|
||
|
||
want, err := os.ReadFile("testdata/time-go.zip")
|
||
if err != nil {
|
||
t.Fatalf("unexpected ReadFile error: %v", err)
|
||
}
|
||
if got := buf.Bytes(); !bytes.Equal(got, want) {
|
||
fmt.Printf("%x\n%x\n", got, want)
|
||
t.Error("contents of time-go.zip differ")
|
||
}
|
||
}
|
||
|
||
func TestWriterOffset(t *testing.T) {
|
||
largeData := make([]byte, 1<<17)
|
||
if _, err := rand.Read(largeData); err != nil {
|
||
t.Fatal("rand.Read failed:", err)
|
||
}
|
||
writeTests[1].Data = largeData
|
||
defer func() {
|
||
writeTests[1].Data = nil
|
||
}()
|
||
|
||
// write a zip file
|
||
buf := new(bytes.Buffer)
|
||
existingData := []byte{1, 2, 3, 1, 2, 3, 1, 2, 3}
|
||
n, _ := buf.Write(existingData)
|
||
w := NewWriter(buf)
|
||
w.SetOffset(int64(n))
|
||
|
||
for _, wt := range writeTests {
|
||
testCreate(t, w, &wt)
|
||
}
|
||
|
||
if err := w.Close(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
// read it back
|
||
r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
for i, wt := range writeTests {
|
||
testReadFile(t, r.File[i], &wt)
|
||
}
|
||
}
|
||
|
||
func TestWriterFlush(t *testing.T) {
|
||
var buf bytes.Buffer
|
||
w := NewWriter(struct{ io.Writer }{&buf})
|
||
_, err := w.Create("foo")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if buf.Len() > 0 {
|
||
t.Fatalf("Unexpected %d bytes already in buffer", buf.Len())
|
||
}
|
||
if err := w.Flush(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if buf.Len() == 0 {
|
||
t.Fatal("No bytes written after Flush")
|
||
}
|
||
}
|
||
|
||
func TestWriterDir(t *testing.T) {
|
||
w := NewWriter(io.Discard)
|
||
dw, err := w.Create("dir/")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if _, err := dw.Write(nil); err != nil {
|
||
t.Errorf("Write(nil) to directory: got %v, want nil", err)
|
||
}
|
||
if _, err := dw.Write([]byte("hello")); err == nil {
|
||
t.Error(`Write("hello") to directory: got nil error, want non-nil`)
|
||
}
|
||
}
|
||
|
||
func TestWriterDirAttributes(t *testing.T) {
|
||
var buf bytes.Buffer
|
||
w := NewWriter(&buf)
|
||
if _, err := w.CreateHeader(&FileHeader{
|
||
Name: "dir/",
|
||
Method: Deflate,
|
||
CompressedSize64: 1234,
|
||
UncompressedSize64: 5678,
|
||
}); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := w.Close(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
b := buf.Bytes()
|
||
|
||
var sig [4]byte
|
||
binary.LittleEndian.PutUint32(sig[:], uint32(fileHeaderSignature))
|
||
|
||
idx := bytes.Index(b, sig[:])
|
||
if idx == -1 {
|
||
t.Fatal("file header not found")
|
||
}
|
||
b = b[idx:]
|
||
|
||
if !bytes.Equal(b[6:10], []byte{0, 0, 0, 0}) { // FileHeader.Flags: 0, FileHeader.Method: 0
|
||
t.Errorf("unexpected method and flags: %v", b[6:10])
|
||
}
|
||
|
||
if !bytes.Equal(b[14:26], make([]byte, 12)) { // FileHeader.{CRC32,CompressSize,UncompressedSize} all zero.
|
||
t.Errorf("unexpected crc, compress and uncompressed size to be 0 was: %v", b[14:26])
|
||
}
|
||
|
||
binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature))
|
||
if bytes.Index(b, sig[:]) != -1 {
|
||
t.Error("there should be no data descriptor")
|
||
}
|
||
}
|
||
|
||
func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
|
||
header := &FileHeader{
|
||
Name: wt.Name,
|
||
Method: wt.Method,
|
||
}
|
||
if wt.Mode != 0 {
|
||
header.SetMode(wt.Mode)
|
||
}
|
||
f, err := w.CreateHeader(header)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
_, err = f.Write(wt.Data)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
|
||
func testReadFile(t *testing.T, f *File, wt *WriteTest) {
|
||
if f.Name != wt.Name {
|
||
t.Fatalf("File name: got %q, want %q", f.Name, wt.Name)
|
||
}
|
||
testFileMode(t, f, wt.Mode)
|
||
rc, err := f.Open()
|
||
if err != nil {
|
||
t.Fatal("opening:", err)
|
||
}
|
||
b, err := io.ReadAll(rc)
|
||
if err != nil {
|
||
t.Fatal("reading:", err)
|
||
}
|
||
err = rc.Close()
|
||
if err != nil {
|
||
t.Fatal("closing:", err)
|
||
}
|
||
if !bytes.Equal(b, wt.Data) {
|
||
t.Errorf("File contents %q, want %q", b, wt.Data)
|
||
}
|
||
}
|
||
|
||
func BenchmarkCompressedZipGarbage(b *testing.B) {
|
||
bigBuf := bytes.Repeat([]byte("a"), 1<<20)
|
||
|
||
runOnce := func(buf *bytes.Buffer) {
|
||
buf.Reset()
|
||
zw := NewWriter(buf)
|
||
for j := 0; j < 3; j++ {
|
||
w, _ := zw.CreateHeader(&FileHeader{
|
||
Name: "foo",
|
||
Method: Deflate,
|
||
})
|
||
w.Write(bigBuf)
|
||
}
|
||
zw.Close()
|
||
}
|
||
|
||
b.ReportAllocs()
|
||
// Run once and then reset the timer.
|
||
// This effectively discards the very large initial flate setup cost,
|
||
// as well as the initialization of bigBuf.
|
||
runOnce(&bytes.Buffer{})
|
||
b.ResetTimer()
|
||
|
||
b.RunParallel(func(pb *testing.PB) {
|
||
var buf bytes.Buffer
|
||
for pb.Next() {
|
||
runOnce(&buf)
|
||
}
|
||
})
|
||
}
|