zip_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. // Copyright 2018 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package zip
  15. import (
  16. "bytes"
  17. "hash/crc32"
  18. "io"
  19. "os"
  20. "reflect"
  21. "syscall"
  22. "testing"
  23. "android/soong/third_party/zip"
  24. "github.com/google/blueprint/pathtools"
  25. )
  26. var (
  27. fileA = []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
  28. fileB = []byte("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB")
  29. fileC = []byte("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC")
  30. fileEmpty = []byte("")
  31. fileManifest = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\n\n")
  32. fileCustomManifest = []byte("Custom manifest: true\n")
  33. customManifestAfter = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\nCustom manifest: true\n\n")
  34. )
  35. var mockFs = pathtools.MockFs(map[string][]byte{
  36. "a/a/a": fileA,
  37. "a/a/b": fileB,
  38. "a/a/c -> ../../c": nil,
  39. "dangling -> missing": nil,
  40. "a/a/d -> b": nil,
  41. "c": fileC,
  42. "l_nl": []byte("a/a/a\na/a/b\nc\n\\[\n"),
  43. "l_sp": []byte("a/a/a a/a/b c \\["),
  44. "l2": []byte("missing\n"),
  45. "rsp": []byte("'a/a/a'\na/a/b\n'@'\n'foo'\\''bar'\n'['"),
  46. "@ -> c": nil,
  47. "foo'bar -> c": nil,
  48. "manifest.txt": fileCustomManifest,
  49. "[": fileEmpty,
  50. })
  51. func fh(name string, contents []byte, method uint16) zip.FileHeader {
  52. return zip.FileHeader{
  53. Name: name,
  54. Method: method,
  55. CRC32: crc32.ChecksumIEEE(contents),
  56. UncompressedSize64: uint64(len(contents)),
  57. ExternalAttrs: (syscall.S_IFREG | 0644) << 16,
  58. }
  59. }
  60. func fhManifest(contents []byte) zip.FileHeader {
  61. return zip.FileHeader{
  62. Name: "META-INF/MANIFEST.MF",
  63. Method: zip.Store,
  64. CRC32: crc32.ChecksumIEEE(contents),
  65. UncompressedSize64: uint64(len(contents)),
  66. ExternalAttrs: (syscall.S_IFREG | 0644) << 16,
  67. }
  68. }
  69. func fhLink(name string, to string) zip.FileHeader {
  70. return zip.FileHeader{
  71. Name: name,
  72. Method: zip.Store,
  73. CRC32: crc32.ChecksumIEEE([]byte(to)),
  74. UncompressedSize64: uint64(len(to)),
  75. ExternalAttrs: (syscall.S_IFLNK | 0777) << 16,
  76. }
  77. }
  78. func fhDir(name string) zip.FileHeader {
  79. return zip.FileHeader{
  80. Name: name,
  81. Method: zip.Store,
  82. CRC32: crc32.ChecksumIEEE(nil),
  83. UncompressedSize64: 0,
  84. ExternalAttrs: (syscall.S_IFDIR|0755)<<16 | 0x10,
  85. }
  86. }
  87. func fileArgsBuilder() *FileArgsBuilder {
  88. return &FileArgsBuilder{
  89. fs: mockFs,
  90. }
  91. }
  92. func TestZip(t *testing.T) {
  93. testCases := []struct {
  94. name string
  95. args *FileArgsBuilder
  96. compressionLevel int
  97. emulateJar bool
  98. nonDeflatedFiles map[string]bool
  99. dirEntries bool
  100. manifest string
  101. storeSymlinks bool
  102. ignoreMissingFiles bool
  103. files []zip.FileHeader
  104. err error
  105. }{
  106. {
  107. name: "empty args",
  108. args: fileArgsBuilder(),
  109. files: []zip.FileHeader{},
  110. },
  111. {
  112. name: "files",
  113. args: fileArgsBuilder().
  114. File("a/a/a").
  115. File("a/a/b").
  116. File("c").
  117. File(`\[`),
  118. compressionLevel: 9,
  119. files: []zip.FileHeader{
  120. fh("a/a/a", fileA, zip.Deflate),
  121. fh("a/a/b", fileB, zip.Deflate),
  122. fh("c", fileC, zip.Deflate),
  123. fh("[", fileEmpty, zip.Store),
  124. },
  125. },
  126. {
  127. name: "files glob",
  128. args: fileArgsBuilder().
  129. SourcePrefixToStrip("a").
  130. File("a/**/*"),
  131. compressionLevel: 9,
  132. storeSymlinks: true,
  133. files: []zip.FileHeader{
  134. fh("a/a", fileA, zip.Deflate),
  135. fh("a/b", fileB, zip.Deflate),
  136. fhLink("a/c", "../../c"),
  137. fhLink("a/d", "b"),
  138. },
  139. },
  140. {
  141. name: "dir",
  142. args: fileArgsBuilder().
  143. SourcePrefixToStrip("a").
  144. Dir("a"),
  145. compressionLevel: 9,
  146. storeSymlinks: true,
  147. files: []zip.FileHeader{
  148. fh("a/a", fileA, zip.Deflate),
  149. fh("a/b", fileB, zip.Deflate),
  150. fhLink("a/c", "../../c"),
  151. fhLink("a/d", "b"),
  152. },
  153. },
  154. {
  155. name: "stored files",
  156. args: fileArgsBuilder().
  157. File("a/a/a").
  158. File("a/a/b").
  159. File("c"),
  160. compressionLevel: 0,
  161. files: []zip.FileHeader{
  162. fh("a/a/a", fileA, zip.Store),
  163. fh("a/a/b", fileB, zip.Store),
  164. fh("c", fileC, zip.Store),
  165. },
  166. },
  167. {
  168. name: "symlinks in zip",
  169. args: fileArgsBuilder().
  170. File("a/a/a").
  171. File("a/a/b").
  172. File("a/a/c").
  173. File("a/a/d"),
  174. compressionLevel: 9,
  175. storeSymlinks: true,
  176. files: []zip.FileHeader{
  177. fh("a/a/a", fileA, zip.Deflate),
  178. fh("a/a/b", fileB, zip.Deflate),
  179. fhLink("a/a/c", "../../c"),
  180. fhLink("a/a/d", "b"),
  181. },
  182. },
  183. {
  184. name: "follow symlinks",
  185. args: fileArgsBuilder().
  186. File("a/a/a").
  187. File("a/a/b").
  188. File("a/a/c").
  189. File("a/a/d"),
  190. compressionLevel: 9,
  191. storeSymlinks: false,
  192. files: []zip.FileHeader{
  193. fh("a/a/a", fileA, zip.Deflate),
  194. fh("a/a/b", fileB, zip.Deflate),
  195. fh("a/a/c", fileC, zip.Deflate),
  196. fh("a/a/d", fileB, zip.Deflate),
  197. },
  198. },
  199. {
  200. name: "dangling symlinks",
  201. args: fileArgsBuilder().
  202. File("dangling"),
  203. compressionLevel: 9,
  204. storeSymlinks: true,
  205. files: []zip.FileHeader{
  206. fhLink("dangling", "missing"),
  207. },
  208. },
  209. {
  210. name: "list",
  211. args: fileArgsBuilder().
  212. List("l_nl"),
  213. compressionLevel: 9,
  214. files: []zip.FileHeader{
  215. fh("a/a/a", fileA, zip.Deflate),
  216. fh("a/a/b", fileB, zip.Deflate),
  217. fh("c", fileC, zip.Deflate),
  218. fh("[", fileEmpty, zip.Store),
  219. },
  220. },
  221. {
  222. name: "list",
  223. args: fileArgsBuilder().
  224. List("l_sp"),
  225. compressionLevel: 9,
  226. files: []zip.FileHeader{
  227. fh("a/a/a", fileA, zip.Deflate),
  228. fh("a/a/b", fileB, zip.Deflate),
  229. fh("c", fileC, zip.Deflate),
  230. fh("[", fileEmpty, zip.Store),
  231. },
  232. },
  233. {
  234. name: "rsp",
  235. args: fileArgsBuilder().
  236. RspFile("rsp"),
  237. compressionLevel: 9,
  238. files: []zip.FileHeader{
  239. fh("a/a/a", fileA, zip.Deflate),
  240. fh("a/a/b", fileB, zip.Deflate),
  241. fh("@", fileC, zip.Deflate),
  242. fh("foo'bar", fileC, zip.Deflate),
  243. fh("[", fileEmpty, zip.Store),
  244. },
  245. },
  246. {
  247. name: "prefix in zip",
  248. args: fileArgsBuilder().
  249. PathPrefixInZip("foo").
  250. File("a/a/a").
  251. File("a/a/b").
  252. File("c"),
  253. compressionLevel: 9,
  254. files: []zip.FileHeader{
  255. fh("foo/a/a/a", fileA, zip.Deflate),
  256. fh("foo/a/a/b", fileB, zip.Deflate),
  257. fh("foo/c", fileC, zip.Deflate),
  258. },
  259. },
  260. {
  261. name: "relative root",
  262. args: fileArgsBuilder().
  263. SourcePrefixToStrip("a").
  264. File("a/a/a").
  265. File("a/a/b"),
  266. compressionLevel: 9,
  267. files: []zip.FileHeader{
  268. fh("a/a", fileA, zip.Deflate),
  269. fh("a/b", fileB, zip.Deflate),
  270. },
  271. },
  272. {
  273. name: "multiple relative root",
  274. args: fileArgsBuilder().
  275. SourcePrefixToStrip("a").
  276. File("a/a/a").
  277. SourcePrefixToStrip("a/a").
  278. File("a/a/b"),
  279. compressionLevel: 9,
  280. files: []zip.FileHeader{
  281. fh("a/a", fileA, zip.Deflate),
  282. fh("b", fileB, zip.Deflate),
  283. },
  284. },
  285. {
  286. name: "emulate jar",
  287. args: fileArgsBuilder().
  288. File("a/a/a").
  289. File("a/a/b"),
  290. compressionLevel: 9,
  291. emulateJar: true,
  292. files: []zip.FileHeader{
  293. fhDir("META-INF/"),
  294. fhManifest(fileManifest),
  295. fhDir("a/"),
  296. fhDir("a/a/"),
  297. fh("a/a/a", fileA, zip.Deflate),
  298. fh("a/a/b", fileB, zip.Deflate),
  299. },
  300. },
  301. {
  302. name: "emulate jar with manifest",
  303. args: fileArgsBuilder().
  304. File("a/a/a").
  305. File("a/a/b"),
  306. compressionLevel: 9,
  307. emulateJar: true,
  308. manifest: "manifest.txt",
  309. files: []zip.FileHeader{
  310. fhDir("META-INF/"),
  311. fhManifest(customManifestAfter),
  312. fhDir("a/"),
  313. fhDir("a/a/"),
  314. fh("a/a/a", fileA, zip.Deflate),
  315. fh("a/a/b", fileB, zip.Deflate),
  316. },
  317. },
  318. {
  319. name: "dir entries",
  320. args: fileArgsBuilder().
  321. File("a/a/a").
  322. File("a/a/b"),
  323. compressionLevel: 9,
  324. dirEntries: true,
  325. files: []zip.FileHeader{
  326. fhDir("a/"),
  327. fhDir("a/a/"),
  328. fh("a/a/a", fileA, zip.Deflate),
  329. fh("a/a/b", fileB, zip.Deflate),
  330. },
  331. },
  332. {
  333. name: "junk paths",
  334. args: fileArgsBuilder().
  335. JunkPaths(true).
  336. File("a/a/a").
  337. File("a/a/b"),
  338. compressionLevel: 9,
  339. files: []zip.FileHeader{
  340. fh("a", fileA, zip.Deflate),
  341. fh("b", fileB, zip.Deflate),
  342. },
  343. },
  344. {
  345. name: "non deflated files",
  346. args: fileArgsBuilder().
  347. File("a/a/a").
  348. File("a/a/b"),
  349. compressionLevel: 9,
  350. nonDeflatedFiles: map[string]bool{"a/a/a": true},
  351. files: []zip.FileHeader{
  352. fh("a/a/a", fileA, zip.Store),
  353. fh("a/a/b", fileB, zip.Deflate),
  354. },
  355. },
  356. {
  357. name: "ignore missing files",
  358. args: fileArgsBuilder().
  359. File("a/a/a").
  360. File("a/a/b").
  361. File("missing"),
  362. compressionLevel: 9,
  363. ignoreMissingFiles: true,
  364. files: []zip.FileHeader{
  365. fh("a/a/a", fileA, zip.Deflate),
  366. fh("a/a/b", fileB, zip.Deflate),
  367. },
  368. },
  369. // errors
  370. {
  371. name: "error missing file",
  372. args: fileArgsBuilder().
  373. File("missing"),
  374. err: os.ErrNotExist,
  375. },
  376. {
  377. name: "error missing dir",
  378. args: fileArgsBuilder().
  379. Dir("missing"),
  380. err: os.ErrNotExist,
  381. },
  382. {
  383. name: "error missing file in list",
  384. args: fileArgsBuilder().
  385. List("l2"),
  386. err: os.ErrNotExist,
  387. },
  388. {
  389. name: "error incorrect relative root",
  390. args: fileArgsBuilder().
  391. SourcePrefixToStrip("b").
  392. File("a/a/a"),
  393. err: IncorrectRelativeRootError{},
  394. },
  395. }
  396. for _, test := range testCases {
  397. t.Run(test.name, func(t *testing.T) {
  398. if test.args.Error() != nil {
  399. t.Fatal(test.args.Error())
  400. }
  401. args := ZipArgs{}
  402. args.FileArgs = test.args.FileArgs()
  403. args.CompressionLevel = test.compressionLevel
  404. args.EmulateJar = test.emulateJar
  405. args.AddDirectoryEntriesToZip = test.dirEntries
  406. args.NonDeflatedFiles = test.nonDeflatedFiles
  407. args.ManifestSourcePath = test.manifest
  408. args.StoreSymlinks = test.storeSymlinks
  409. args.IgnoreMissingFiles = test.ignoreMissingFiles
  410. args.Filesystem = mockFs
  411. args.Stderr = &bytes.Buffer{}
  412. buf := &bytes.Buffer{}
  413. err := zipTo(args, buf)
  414. if (err != nil) != (test.err != nil) {
  415. t.Fatalf("want error %v, got %v", test.err, err)
  416. } else if test.err != nil {
  417. if os.IsNotExist(test.err) {
  418. if !os.IsNotExist(test.err) {
  419. t.Fatalf("want error %v, got %v", test.err, err)
  420. }
  421. } else if _, wantRelativeRootErr := test.err.(IncorrectRelativeRootError); wantRelativeRootErr {
  422. if _, gotRelativeRootErr := err.(IncorrectRelativeRootError); !gotRelativeRootErr {
  423. t.Fatalf("want error %v, got %v", test.err, err)
  424. }
  425. } else {
  426. t.Fatalf("want error %v, got %v", test.err, err)
  427. }
  428. return
  429. }
  430. br := bytes.NewReader(buf.Bytes())
  431. zr, err := zip.NewReader(br, int64(br.Len()))
  432. if err != nil {
  433. t.Fatal(err)
  434. }
  435. var files []zip.FileHeader
  436. for _, f := range zr.File {
  437. r, err := f.Open()
  438. if err != nil {
  439. t.Fatalf("error when opening %s: %s", f.Name, err)
  440. }
  441. crc := crc32.NewIEEE()
  442. len, err := io.Copy(crc, r)
  443. r.Close()
  444. if err != nil {
  445. t.Fatalf("error when reading %s: %s", f.Name, err)
  446. }
  447. if uint64(len) != f.UncompressedSize64 {
  448. t.Errorf("incorrect length for %s, want %d got %d", f.Name, f.UncompressedSize64, len)
  449. }
  450. if crc.Sum32() != f.CRC32 {
  451. t.Errorf("incorrect crc for %s, want %x got %x", f.Name, f.CRC32, crc)
  452. }
  453. files = append(files, f.FileHeader)
  454. }
  455. if len(files) != len(test.files) {
  456. t.Fatalf("want %d files, got %d", len(test.files), len(files))
  457. }
  458. for i := range files {
  459. want := test.files[i]
  460. got := files[i]
  461. if want.Name != got.Name {
  462. t.Errorf("incorrect file %d want %q got %q", i, want.Name, got.Name)
  463. continue
  464. }
  465. if want.UncompressedSize64 != got.UncompressedSize64 {
  466. t.Errorf("incorrect file %s length want %v got %v", want.Name,
  467. want.UncompressedSize64, got.UncompressedSize64)
  468. }
  469. if want.ExternalAttrs != got.ExternalAttrs {
  470. t.Errorf("incorrect file %s attrs want %x got %x", want.Name,
  471. want.ExternalAttrs, got.ExternalAttrs)
  472. }
  473. if want.CRC32 != got.CRC32 {
  474. t.Errorf("incorrect file %s crc want %v got %v", want.Name,
  475. want.CRC32, got.CRC32)
  476. }
  477. if want.Method != got.Method {
  478. t.Errorf("incorrect file %s method want %v got %v", want.Name,
  479. want.Method, got.Method)
  480. }
  481. }
  482. })
  483. }
  484. }
  485. func TestSrcJar(t *testing.T) {
  486. mockFs := pathtools.MockFs(map[string][]byte{
  487. "wrong_package.java": []byte("package foo;"),
  488. "foo/correct_package.java": []byte("package foo;"),
  489. "src/no_package.java": nil,
  490. "src2/parse_error.java": []byte("error"),
  491. })
  492. want := []string{
  493. "foo/",
  494. "foo/wrong_package.java",
  495. "foo/correct_package.java",
  496. "no_package.java",
  497. "src2/",
  498. "src2/parse_error.java",
  499. }
  500. args := ZipArgs{}
  501. args.FileArgs = NewFileArgsBuilder().File("**/*.java").FileArgs()
  502. args.SrcJar = true
  503. args.AddDirectoryEntriesToZip = true
  504. args.Filesystem = mockFs
  505. args.Stderr = &bytes.Buffer{}
  506. buf := &bytes.Buffer{}
  507. err := zipTo(args, buf)
  508. if err != nil {
  509. t.Fatalf("got error %v", err)
  510. }
  511. br := bytes.NewReader(buf.Bytes())
  512. zr, err := zip.NewReader(br, int64(br.Len()))
  513. if err != nil {
  514. t.Fatal(err)
  515. }
  516. var got []string
  517. for _, f := range zr.File {
  518. r, err := f.Open()
  519. if err != nil {
  520. t.Fatalf("error when opening %s: %s", f.Name, err)
  521. }
  522. crc := crc32.NewIEEE()
  523. len, err := io.Copy(crc, r)
  524. r.Close()
  525. if err != nil {
  526. t.Fatalf("error when reading %s: %s", f.Name, err)
  527. }
  528. if uint64(len) != f.UncompressedSize64 {
  529. t.Errorf("incorrect length for %s, want %d got %d", f.Name, f.UncompressedSize64, len)
  530. }
  531. if crc.Sum32() != f.CRC32 {
  532. t.Errorf("incorrect crc for %s, want %x got %x", f.Name, f.CRC32, crc)
  533. }
  534. got = append(got, f.Name)
  535. }
  536. if !reflect.DeepEqual(want, got) {
  537. t.Errorf("want files %q, got %q", want, got)
  538. }
  539. }