writer_test.go 10.0 KB

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