| Nan Zhang | 674dd93 | 2018-01-26 18:30:36 -0800 | [diff] [blame] | 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 |  | 
|  | 15 | package zip | 
|  | 16 |  | 
|  | 17 | import ( | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 18 | "bytes" | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 19 | "encoding/hex" | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 20 | "hash/crc32" | 
|  | 21 | "io" | 
|  | 22 | "os" | 
| Nan Zhang | 674dd93 | 2018-01-26 18:30:36 -0800 | [diff] [blame] | 23 | "reflect" | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 24 | "syscall" | 
| Nan Zhang | 674dd93 | 2018-01-26 18:30:36 -0800 | [diff] [blame] | 25 | "testing" | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 26 |  | 
|  | 27 | "android/soong/third_party/zip" | 
|  | 28 |  | 
|  | 29 | "github.com/google/blueprint/pathtools" | 
| Nan Zhang | 674dd93 | 2018-01-26 18:30:36 -0800 | [diff] [blame] | 30 | ) | 
|  | 31 |  | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 32 | var ( | 
|  | 33 | fileA        = []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") | 
|  | 34 | fileB        = []byte("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB") | 
|  | 35 | fileC        = []byte("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC") | 
|  | 36 | fileEmpty    = []byte("") | 
|  | 37 | fileManifest = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\n\n") | 
|  | 38 |  | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 39 | sha256FileA = "d53eda7a637c99cc7fb566d96e9fa109bf15c478410a3f5eb4d4c4e26cd081f6" | 
|  | 40 | sha256FileB = "430c56c5818e62bcb6d478901ef86284e97714c138f3c86aa14fd6a84b7ce5d3" | 
|  | 41 | sha256FileC = "31c5ab6111f1d6aa13c2c4e92bb3c0f7c76b61b42d141af1e846eb7f6586a51c" | 
|  | 42 |  | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 43 | fileCustomManifest  = []byte("Custom manifest: true\n") | 
|  | 44 | customManifestAfter = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\nCustom manifest: true\n\n") | 
|  | 45 | ) | 
|  | 46 |  | 
|  | 47 | var mockFs = pathtools.MockFs(map[string][]byte{ | 
| Colin Cross | 9cb51db | 2019-06-17 14:12:41 -0700 | [diff] [blame] | 48 | "a/a/a":               fileA, | 
|  | 49 | "a/a/b":               fileB, | 
|  | 50 | "a/a/c -> ../../c":    nil, | 
|  | 51 | "dangling -> missing": nil, | 
|  | 52 | "a/a/d -> b":          nil, | 
|  | 53 | "c":                   fileC, | 
| Colin Cross | 7ddd08a | 2022-08-15 15:47:41 -0700 | [diff] [blame] | 54 | "d/a/a":               nil, | 
| Colin Cross | caf4d4c | 2021-02-03 15:15:14 -0800 | [diff] [blame] | 55 | "l_nl":                []byte("a/a/a\na/a/b\nc\n\\[\n"), | 
|  | 56 | "l_sp":                []byte("a/a/a a/a/b c \\["), | 
| Colin Cross | 9cb51db | 2019-06-17 14:12:41 -0700 | [diff] [blame] | 57 | "l2":                  []byte("missing\n"), | 
| Colin Cross | caf4d4c | 2021-02-03 15:15:14 -0800 | [diff] [blame] | 58 | "rsp":                 []byte("'a/a/a'\na/a/b\n'@'\n'foo'\\''bar'\n'['"), | 
| Colin Cross | 053fca1 | 2020-08-19 13:51:47 -0700 | [diff] [blame] | 59 | "@ -> c":              nil, | 
|  | 60 | "foo'bar -> c":        nil, | 
| Colin Cross | 9cb51db | 2019-06-17 14:12:41 -0700 | [diff] [blame] | 61 | "manifest.txt":        fileCustomManifest, | 
| Colin Cross | caf4d4c | 2021-02-03 15:15:14 -0800 | [diff] [blame] | 62 | "[":                   fileEmpty, | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 63 | }) | 
|  | 64 |  | 
|  | 65 | func fh(name string, contents []byte, method uint16) zip.FileHeader { | 
|  | 66 | return zip.FileHeader{ | 
|  | 67 | Name:               name, | 
|  | 68 | Method:             method, | 
|  | 69 | CRC32:              crc32.ChecksumIEEE(contents), | 
|  | 70 | UncompressedSize64: uint64(len(contents)), | 
| Chris Gross | fa5b4e9 | 2021-06-02 12:56:08 -0700 | [diff] [blame] | 71 | ExternalAttrs:      (syscall.S_IFREG | 0644) << 16, | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 72 | } | 
|  | 73 | } | 
|  | 74 |  | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 75 | func fhWithSHA256(name string, contents []byte, method uint16, sha256 string) zip.FileHeader { | 
|  | 76 | h := fh(name, contents, method) | 
|  | 77 | // The extra field contains 38 bytes, including 2 bytes of header ID, 2 bytes | 
|  | 78 | // of size, 2 bytes of signature, and 32 bytes of checksum data block. | 
|  | 79 | var extra [38]byte | 
|  | 80 | // The first 6 bytes contains Sha256HeaderID (0x4967), size (unit(34)) and | 
|  | 81 | // Sha256HeaderSignature (0x9514) | 
|  | 82 | copy(extra[0:], []byte{103, 73, 34, 0, 20, 149}) | 
|  | 83 | sha256Bytes, _ := hex.DecodeString(sha256) | 
|  | 84 | copy(extra[6:], sha256Bytes) | 
|  | 85 | h.Extra = append(h.Extra, extra[:]...) | 
|  | 86 | return h | 
|  | 87 | } | 
|  | 88 |  | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 89 | func fhManifest(contents []byte) zip.FileHeader { | 
|  | 90 | return zip.FileHeader{ | 
|  | 91 | Name:               "META-INF/MANIFEST.MF", | 
|  | 92 | Method:             zip.Store, | 
|  | 93 | CRC32:              crc32.ChecksumIEEE(contents), | 
|  | 94 | UncompressedSize64: uint64(len(contents)), | 
| Chris Gross | fa5b4e9 | 2021-06-02 12:56:08 -0700 | [diff] [blame] | 95 | ExternalAttrs:      (syscall.S_IFREG | 0644) << 16, | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 96 | } | 
|  | 97 | } | 
|  | 98 |  | 
|  | 99 | func fhLink(name string, to string) zip.FileHeader { | 
|  | 100 | return zip.FileHeader{ | 
|  | 101 | Name:               name, | 
|  | 102 | Method:             zip.Store, | 
|  | 103 | CRC32:              crc32.ChecksumIEEE([]byte(to)), | 
|  | 104 | UncompressedSize64: uint64(len(to)), | 
|  | 105 | ExternalAttrs:      (syscall.S_IFLNK | 0777) << 16, | 
|  | 106 | } | 
|  | 107 | } | 
|  | 108 |  | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 109 | type fhDirOptions struct { | 
|  | 110 | extra []byte | 
|  | 111 | } | 
|  | 112 |  | 
|  | 113 | func fhDir(name string, opts fhDirOptions) zip.FileHeader { | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 114 | return zip.FileHeader{ | 
|  | 115 | Name:               name, | 
|  | 116 | Method:             zip.Store, | 
|  | 117 | CRC32:              crc32.ChecksumIEEE(nil), | 
|  | 118 | UncompressedSize64: 0, | 
| Chris Gross | fa5b4e9 | 2021-06-02 12:56:08 -0700 | [diff] [blame] | 119 | ExternalAttrs:      (syscall.S_IFDIR|0755)<<16 | 0x10, | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 120 | Extra:              opts.extra, | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 121 | } | 
|  | 122 | } | 
|  | 123 |  | 
|  | 124 | func fileArgsBuilder() *FileArgsBuilder { | 
|  | 125 | return &FileArgsBuilder{ | 
|  | 126 | fs: mockFs, | 
|  | 127 | } | 
|  | 128 | } | 
|  | 129 |  | 
|  | 130 | func TestZip(t *testing.T) { | 
|  | 131 | testCases := []struct { | 
| Colin Cross | 4be8f9e | 2018-09-28 15:16:48 -0700 | [diff] [blame] | 132 | name               string | 
|  | 133 | args               *FileArgsBuilder | 
|  | 134 | compressionLevel   int | 
|  | 135 | emulateJar         bool | 
|  | 136 | nonDeflatedFiles   map[string]bool | 
|  | 137 | dirEntries         bool | 
|  | 138 | manifest           string | 
|  | 139 | storeSymlinks      bool | 
|  | 140 | ignoreMissingFiles bool | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 141 | sha256Checksum     bool | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 142 |  | 
|  | 143 | files []zip.FileHeader | 
|  | 144 | err   error | 
|  | 145 | }{ | 
|  | 146 | { | 
|  | 147 | name: "empty args", | 
|  | 148 | args: fileArgsBuilder(), | 
|  | 149 |  | 
|  | 150 | files: []zip.FileHeader{}, | 
|  | 151 | }, | 
|  | 152 | { | 
|  | 153 | name: "files", | 
|  | 154 | args: fileArgsBuilder(). | 
|  | 155 | File("a/a/a"). | 
|  | 156 | File("a/a/b"). | 
| Colin Cross | caf4d4c | 2021-02-03 15:15:14 -0800 | [diff] [blame] | 157 | File("c"). | 
|  | 158 | File(`\[`), | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 159 | compressionLevel: 9, | 
|  | 160 |  | 
|  | 161 | files: []zip.FileHeader{ | 
|  | 162 | fh("a/a/a", fileA, zip.Deflate), | 
|  | 163 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 164 | fh("c", fileC, zip.Deflate), | 
| Colin Cross | caf4d4c | 2021-02-03 15:15:14 -0800 | [diff] [blame] | 165 | fh("[", fileEmpty, zip.Store), | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 166 | }, | 
|  | 167 | }, | 
|  | 168 | { | 
| Colin Cross | 1d98ee2 | 2018-09-18 17:05:15 -0700 | [diff] [blame] | 169 | name: "files glob", | 
|  | 170 | args: fileArgsBuilder(). | 
|  | 171 | SourcePrefixToStrip("a"). | 
|  | 172 | File("a/**/*"), | 
|  | 173 | compressionLevel: 9, | 
| Colin Cross | 09f1105 | 2018-09-21 15:12:39 -0700 | [diff] [blame] | 174 | storeSymlinks:    true, | 
| Colin Cross | 1d98ee2 | 2018-09-18 17:05:15 -0700 | [diff] [blame] | 175 |  | 
|  | 176 | files: []zip.FileHeader{ | 
|  | 177 | fh("a/a", fileA, zip.Deflate), | 
|  | 178 | fh("a/b", fileB, zip.Deflate), | 
|  | 179 | fhLink("a/c", "../../c"), | 
|  | 180 | fhLink("a/d", "b"), | 
|  | 181 | }, | 
|  | 182 | }, | 
|  | 183 | { | 
|  | 184 | name: "dir", | 
|  | 185 | args: fileArgsBuilder(). | 
|  | 186 | SourcePrefixToStrip("a"). | 
|  | 187 | Dir("a"), | 
|  | 188 | compressionLevel: 9, | 
| Colin Cross | 09f1105 | 2018-09-21 15:12:39 -0700 | [diff] [blame] | 189 | storeSymlinks:    true, | 
| Colin Cross | 1d98ee2 | 2018-09-18 17:05:15 -0700 | [diff] [blame] | 190 |  | 
|  | 191 | files: []zip.FileHeader{ | 
|  | 192 | fh("a/a", fileA, zip.Deflate), | 
|  | 193 | fh("a/b", fileB, zip.Deflate), | 
|  | 194 | fhLink("a/c", "../../c"), | 
|  | 195 | fhLink("a/d", "b"), | 
|  | 196 | }, | 
|  | 197 | }, | 
|  | 198 | { | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 199 | name: "stored files", | 
|  | 200 | args: fileArgsBuilder(). | 
|  | 201 | File("a/a/a"). | 
|  | 202 | File("a/a/b"). | 
|  | 203 | File("c"), | 
|  | 204 | compressionLevel: 0, | 
|  | 205 |  | 
|  | 206 | files: []zip.FileHeader{ | 
|  | 207 | fh("a/a/a", fileA, zip.Store), | 
|  | 208 | fh("a/a/b", fileB, zip.Store), | 
|  | 209 | fh("c", fileC, zip.Store), | 
|  | 210 | }, | 
|  | 211 | }, | 
|  | 212 | { | 
|  | 213 | name: "symlinks in zip", | 
|  | 214 | args: fileArgsBuilder(). | 
|  | 215 | File("a/a/a"). | 
|  | 216 | File("a/a/b"). | 
|  | 217 | File("a/a/c"). | 
|  | 218 | File("a/a/d"), | 
|  | 219 | compressionLevel: 9, | 
| Colin Cross | 09f1105 | 2018-09-21 15:12:39 -0700 | [diff] [blame] | 220 | storeSymlinks:    true, | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 221 |  | 
|  | 222 | files: []zip.FileHeader{ | 
|  | 223 | fh("a/a/a", fileA, zip.Deflate), | 
|  | 224 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 225 | fhLink("a/a/c", "../../c"), | 
|  | 226 | fhLink("a/a/d", "b"), | 
|  | 227 | }, | 
|  | 228 | }, | 
|  | 229 | { | 
| Colin Cross | 09f1105 | 2018-09-21 15:12:39 -0700 | [diff] [blame] | 230 | name: "follow symlinks", | 
|  | 231 | args: fileArgsBuilder(). | 
|  | 232 | File("a/a/a"). | 
|  | 233 | File("a/a/b"). | 
|  | 234 | File("a/a/c"). | 
|  | 235 | File("a/a/d"), | 
|  | 236 | compressionLevel: 9, | 
|  | 237 | storeSymlinks:    false, | 
|  | 238 |  | 
|  | 239 | files: []zip.FileHeader{ | 
|  | 240 | fh("a/a/a", fileA, zip.Deflate), | 
|  | 241 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 242 | fh("a/a/c", fileC, zip.Deflate), | 
|  | 243 | fh("a/a/d", fileB, zip.Deflate), | 
|  | 244 | }, | 
|  | 245 | }, | 
|  | 246 | { | 
| Colin Cross | 9cb51db | 2019-06-17 14:12:41 -0700 | [diff] [blame] | 247 | name: "dangling symlinks", | 
|  | 248 | args: fileArgsBuilder(). | 
|  | 249 | File("dangling"), | 
|  | 250 | compressionLevel: 9, | 
|  | 251 | storeSymlinks:    true, | 
|  | 252 |  | 
|  | 253 | files: []zip.FileHeader{ | 
|  | 254 | fhLink("dangling", "missing"), | 
|  | 255 | }, | 
|  | 256 | }, | 
|  | 257 | { | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 258 | name: "list", | 
|  | 259 | args: fileArgsBuilder(). | 
| Jiyong Park | 04bbf98 | 2019-11-04 13:18:41 +0900 | [diff] [blame] | 260 | List("l_nl"), | 
|  | 261 | compressionLevel: 9, | 
|  | 262 |  | 
|  | 263 | files: []zip.FileHeader{ | 
|  | 264 | fh("a/a/a", fileA, zip.Deflate), | 
|  | 265 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 266 | fh("c", fileC, zip.Deflate), | 
| Colin Cross | caf4d4c | 2021-02-03 15:15:14 -0800 | [diff] [blame] | 267 | fh("[", fileEmpty, zip.Store), | 
| Jiyong Park | 04bbf98 | 2019-11-04 13:18:41 +0900 | [diff] [blame] | 268 | }, | 
|  | 269 | }, | 
|  | 270 | { | 
|  | 271 | name: "list", | 
|  | 272 | args: fileArgsBuilder(). | 
|  | 273 | List("l_sp"), | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 274 | compressionLevel: 9, | 
|  | 275 |  | 
|  | 276 | files: []zip.FileHeader{ | 
|  | 277 | fh("a/a/a", fileA, zip.Deflate), | 
|  | 278 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 279 | fh("c", fileC, zip.Deflate), | 
| Colin Cross | caf4d4c | 2021-02-03 15:15:14 -0800 | [diff] [blame] | 280 | fh("[", fileEmpty, zip.Store), | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 281 | }, | 
|  | 282 | }, | 
|  | 283 | { | 
| Colin Cross | 053fca1 | 2020-08-19 13:51:47 -0700 | [diff] [blame] | 284 | name: "rsp", | 
|  | 285 | args: fileArgsBuilder(). | 
|  | 286 | RspFile("rsp"), | 
|  | 287 | compressionLevel: 9, | 
|  | 288 |  | 
|  | 289 | files: []zip.FileHeader{ | 
|  | 290 | fh("a/a/a", fileA, zip.Deflate), | 
|  | 291 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 292 | fh("@", fileC, zip.Deflate), | 
|  | 293 | fh("foo'bar", fileC, zip.Deflate), | 
| Colin Cross | caf4d4c | 2021-02-03 15:15:14 -0800 | [diff] [blame] | 294 | fh("[", fileEmpty, zip.Store), | 
| Colin Cross | 053fca1 | 2020-08-19 13:51:47 -0700 | [diff] [blame] | 295 | }, | 
|  | 296 | }, | 
|  | 297 | { | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 298 | name: "prefix in zip", | 
|  | 299 | args: fileArgsBuilder(). | 
|  | 300 | PathPrefixInZip("foo"). | 
|  | 301 | File("a/a/a"). | 
|  | 302 | File("a/a/b"). | 
|  | 303 | File("c"), | 
|  | 304 | compressionLevel: 9, | 
|  | 305 |  | 
|  | 306 | files: []zip.FileHeader{ | 
|  | 307 | fh("foo/a/a/a", fileA, zip.Deflate), | 
|  | 308 | fh("foo/a/a/b", fileB, zip.Deflate), | 
|  | 309 | fh("foo/c", fileC, zip.Deflate), | 
|  | 310 | }, | 
|  | 311 | }, | 
|  | 312 | { | 
|  | 313 | name: "relative root", | 
|  | 314 | args: fileArgsBuilder(). | 
|  | 315 | SourcePrefixToStrip("a"). | 
|  | 316 | File("a/a/a"). | 
|  | 317 | File("a/a/b"), | 
|  | 318 | compressionLevel: 9, | 
|  | 319 |  | 
|  | 320 | files: []zip.FileHeader{ | 
|  | 321 | fh("a/a", fileA, zip.Deflate), | 
|  | 322 | fh("a/b", fileB, zip.Deflate), | 
|  | 323 | }, | 
|  | 324 | }, | 
|  | 325 | { | 
|  | 326 | name: "multiple relative root", | 
|  | 327 | args: fileArgsBuilder(). | 
|  | 328 | SourcePrefixToStrip("a"). | 
|  | 329 | File("a/a/a"). | 
|  | 330 | SourcePrefixToStrip("a/a"). | 
|  | 331 | File("a/a/b"), | 
|  | 332 | compressionLevel: 9, | 
|  | 333 |  | 
|  | 334 | files: []zip.FileHeader{ | 
|  | 335 | fh("a/a", fileA, zip.Deflate), | 
|  | 336 | fh("b", fileB, zip.Deflate), | 
|  | 337 | }, | 
|  | 338 | }, | 
|  | 339 | { | 
|  | 340 | name: "emulate jar", | 
|  | 341 | args: fileArgsBuilder(). | 
|  | 342 | File("a/a/a"). | 
|  | 343 | File("a/a/b"), | 
|  | 344 | compressionLevel: 9, | 
|  | 345 | emulateJar:       true, | 
|  | 346 |  | 
|  | 347 | files: []zip.FileHeader{ | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 348 | fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}), | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 349 | fhManifest(fileManifest), | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 350 | fhDir("a/", fhDirOptions{}), | 
|  | 351 | fhDir("a/a/", fhDirOptions{}), | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 352 | fh("a/a/a", fileA, zip.Deflate), | 
|  | 353 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 354 | }, | 
|  | 355 | }, | 
|  | 356 | { | 
|  | 357 | name: "emulate jar with manifest", | 
|  | 358 | args: fileArgsBuilder(). | 
|  | 359 | File("a/a/a"). | 
|  | 360 | File("a/a/b"), | 
|  | 361 | compressionLevel: 9, | 
|  | 362 | emulateJar:       true, | 
|  | 363 | manifest:         "manifest.txt", | 
|  | 364 |  | 
|  | 365 | files: []zip.FileHeader{ | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 366 | fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}), | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 367 | fhManifest(customManifestAfter), | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 368 | fhDir("a/", fhDirOptions{}), | 
|  | 369 | fhDir("a/a/", fhDirOptions{}), | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 370 | fh("a/a/a", fileA, zip.Deflate), | 
|  | 371 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 372 | }, | 
|  | 373 | }, | 
|  | 374 | { | 
|  | 375 | name: "dir entries", | 
|  | 376 | args: fileArgsBuilder(). | 
|  | 377 | File("a/a/a"). | 
|  | 378 | File("a/a/b"), | 
|  | 379 | compressionLevel: 9, | 
|  | 380 | dirEntries:       true, | 
|  | 381 |  | 
|  | 382 | files: []zip.FileHeader{ | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 383 | fhDir("a/", fhDirOptions{}), | 
|  | 384 | fhDir("a/a/", fhDirOptions{}), | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 385 | fh("a/a/a", fileA, zip.Deflate), | 
|  | 386 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 387 | }, | 
|  | 388 | }, | 
|  | 389 | { | 
|  | 390 | name: "junk paths", | 
|  | 391 | args: fileArgsBuilder(). | 
|  | 392 | JunkPaths(true). | 
|  | 393 | File("a/a/a"). | 
|  | 394 | File("a/a/b"), | 
|  | 395 | compressionLevel: 9, | 
|  | 396 |  | 
|  | 397 | files: []zip.FileHeader{ | 
|  | 398 | fh("a", fileA, zip.Deflate), | 
|  | 399 | fh("b", fileB, zip.Deflate), | 
|  | 400 | }, | 
|  | 401 | }, | 
|  | 402 | { | 
|  | 403 | name: "non deflated files", | 
|  | 404 | args: fileArgsBuilder(). | 
|  | 405 | File("a/a/a"). | 
|  | 406 | File("a/a/b"), | 
|  | 407 | compressionLevel: 9, | 
|  | 408 | nonDeflatedFiles: map[string]bool{"a/a/a": true}, | 
|  | 409 |  | 
|  | 410 | files: []zip.FileHeader{ | 
|  | 411 | fh("a/a/a", fileA, zip.Store), | 
|  | 412 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 413 | }, | 
|  | 414 | }, | 
| Colin Cross | 4be8f9e | 2018-09-28 15:16:48 -0700 | [diff] [blame] | 415 | { | 
|  | 416 | name: "ignore missing files", | 
|  | 417 | args: fileArgsBuilder(). | 
|  | 418 | File("a/a/a"). | 
|  | 419 | File("a/a/b"). | 
|  | 420 | File("missing"), | 
|  | 421 | compressionLevel:   9, | 
|  | 422 | ignoreMissingFiles: true, | 
|  | 423 |  | 
|  | 424 | files: []zip.FileHeader{ | 
|  | 425 | fh("a/a/a", fileA, zip.Deflate), | 
|  | 426 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 427 | }, | 
|  | 428 | }, | 
| Colin Cross | 7ddd08a | 2022-08-15 15:47:41 -0700 | [diff] [blame] | 429 | { | 
|  | 430 | name: "duplicate sources", | 
|  | 431 | args: fileArgsBuilder(). | 
|  | 432 | File("a/a/a"). | 
|  | 433 | File("a/a/a"), | 
|  | 434 | compressionLevel: 9, | 
|  | 435 |  | 
|  | 436 | files: []zip.FileHeader{ | 
|  | 437 | fh("a/a/a", fileA, zip.Deflate), | 
|  | 438 | }, | 
|  | 439 | }, | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 440 | { | 
|  | 441 | name: "generate SHA256 checksum", | 
|  | 442 | args: fileArgsBuilder(). | 
|  | 443 | File("a/a/a"). | 
|  | 444 | File("a/a/b"). | 
|  | 445 | File("a/a/c"). | 
|  | 446 | File("c"), | 
|  | 447 | compressionLevel: 9, | 
|  | 448 | sha256Checksum:   true, | 
|  | 449 |  | 
|  | 450 | files: []zip.FileHeader{ | 
|  | 451 | fhWithSHA256("a/a/a", fileA, zip.Deflate, sha256FileA), | 
|  | 452 | fhWithSHA256("a/a/b", fileB, zip.Deflate, sha256FileB), | 
|  | 453 | fhWithSHA256("a/a/c", fileC, zip.Deflate, sha256FileC), | 
|  | 454 | fhWithSHA256("c", fileC, zip.Deflate, sha256FileC), | 
|  | 455 | }, | 
|  | 456 | }, | 
| Colin Cross | 25ff305 | 2023-05-08 15:05:29 -0700 | [diff] [blame] | 457 | { | 
|  | 458 | name: "explicit path", | 
|  | 459 | args: fileArgsBuilder(). | 
|  | 460 | ExplicitPathInZip("foo"). | 
|  | 461 | File("a/a/a"). | 
|  | 462 | File("a/a/b"), | 
|  | 463 | compressionLevel: 9, | 
|  | 464 |  | 
|  | 465 | files: []zip.FileHeader{ | 
|  | 466 | fh("foo", fileA, zip.Deflate), | 
|  | 467 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 468 | }, | 
|  | 469 | }, | 
|  | 470 | { | 
|  | 471 | name: "explicit path with prefix", | 
|  | 472 | args: fileArgsBuilder(). | 
|  | 473 | PathPrefixInZip("prefix"). | 
|  | 474 | ExplicitPathInZip("foo"). | 
|  | 475 | File("a/a/a"). | 
|  | 476 | File("a/a/b"), | 
|  | 477 | compressionLevel: 9, | 
|  | 478 |  | 
|  | 479 | files: []zip.FileHeader{ | 
|  | 480 | fh("prefix/foo", fileA, zip.Deflate), | 
|  | 481 | fh("prefix/a/a/b", fileB, zip.Deflate), | 
|  | 482 | }, | 
|  | 483 | }, | 
|  | 484 | { | 
|  | 485 | name: "explicit path with glob", | 
|  | 486 | args: fileArgsBuilder(). | 
|  | 487 | ExplicitPathInZip("foo"). | 
|  | 488 | File("a/a/a*"). | 
|  | 489 | File("a/a/b"), | 
|  | 490 | compressionLevel: 9, | 
|  | 491 |  | 
|  | 492 | files: []zip.FileHeader{ | 
|  | 493 | fh("foo", fileA, zip.Deflate), | 
|  | 494 | fh("a/a/b", fileB, zip.Deflate), | 
|  | 495 | }, | 
|  | 496 | }, | 
|  | 497 | { | 
|  | 498 | name: "explicit path with junk paths", | 
|  | 499 | args: fileArgsBuilder(). | 
|  | 500 | JunkPaths(true). | 
|  | 501 | ExplicitPathInZip("foo/bar"). | 
|  | 502 | File("a/a/a*"). | 
|  | 503 | File("a/a/b"), | 
|  | 504 | compressionLevel: 9, | 
|  | 505 |  | 
|  | 506 | files: []zip.FileHeader{ | 
|  | 507 | fh("foo/bar", fileA, zip.Deflate), | 
|  | 508 | fh("b", fileB, zip.Deflate), | 
|  | 509 | }, | 
|  | 510 | }, | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 511 |  | 
|  | 512 | // errors | 
|  | 513 | { | 
|  | 514 | name: "error missing file", | 
|  | 515 | args: fileArgsBuilder(). | 
|  | 516 | File("missing"), | 
|  | 517 | err: os.ErrNotExist, | 
|  | 518 | }, | 
|  | 519 | { | 
| Colin Cross | 1d98ee2 | 2018-09-18 17:05:15 -0700 | [diff] [blame] | 520 | name: "error missing dir", | 
|  | 521 | args: fileArgsBuilder(). | 
|  | 522 | Dir("missing"), | 
|  | 523 | err: os.ErrNotExist, | 
|  | 524 | }, | 
|  | 525 | { | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 526 | name: "error missing file in list", | 
|  | 527 | args: fileArgsBuilder(). | 
|  | 528 | List("l2"), | 
|  | 529 | err: os.ErrNotExist, | 
|  | 530 | }, | 
| Colin Cross | 1d98ee2 | 2018-09-18 17:05:15 -0700 | [diff] [blame] | 531 | { | 
|  | 532 | name: "error incorrect relative root", | 
|  | 533 | args: fileArgsBuilder(). | 
|  | 534 | SourcePrefixToStrip("b"). | 
|  | 535 | File("a/a/a"), | 
|  | 536 | err: IncorrectRelativeRootError{}, | 
|  | 537 | }, | 
| Colin Cross | 7ddd08a | 2022-08-15 15:47:41 -0700 | [diff] [blame] | 538 | { | 
|  | 539 | name: "error conflicting file", | 
|  | 540 | args: fileArgsBuilder(). | 
|  | 541 | SourcePrefixToStrip("a"). | 
|  | 542 | File("a/a/a"). | 
|  | 543 | SourcePrefixToStrip("d"). | 
|  | 544 | File("d/a/a"), | 
|  | 545 | err: ConflictingFileError{}, | 
|  | 546 | }, | 
| Colin Cross | 25ff305 | 2023-05-08 15:05:29 -0700 | [diff] [blame] | 547 | { | 
|  | 548 | name: "error explicit path conflicting", | 
|  | 549 | args: fileArgsBuilder(). | 
|  | 550 | ExplicitPathInZip("foo"). | 
|  | 551 | File("a/a/a"). | 
|  | 552 | ExplicitPathInZip("foo"). | 
|  | 553 | File("a/a/b"), | 
|  | 554 | err: ConflictingFileError{}, | 
|  | 555 | }, | 
|  | 556 | { | 
|  | 557 | name: "error explicit path conflicting glob", | 
|  | 558 | args: fileArgsBuilder(). | 
|  | 559 | ExplicitPathInZip("foo"). | 
|  | 560 | File("a/a/*"), | 
|  | 561 | err: ConflictingFileError{}, | 
|  | 562 | }, | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 563 | } | 
|  | 564 |  | 
|  | 565 | for _, test := range testCases { | 
|  | 566 | t.Run(test.name, func(t *testing.T) { | 
|  | 567 | if test.args.Error() != nil { | 
|  | 568 | t.Fatal(test.args.Error()) | 
|  | 569 | } | 
|  | 570 |  | 
|  | 571 | args := ZipArgs{} | 
|  | 572 | args.FileArgs = test.args.FileArgs() | 
|  | 573 | args.CompressionLevel = test.compressionLevel | 
|  | 574 | args.EmulateJar = test.emulateJar | 
|  | 575 | args.AddDirectoryEntriesToZip = test.dirEntries | 
|  | 576 | args.NonDeflatedFiles = test.nonDeflatedFiles | 
|  | 577 | args.ManifestSourcePath = test.manifest | 
| Colin Cross | 09f1105 | 2018-09-21 15:12:39 -0700 | [diff] [blame] | 578 | args.StoreSymlinks = test.storeSymlinks | 
| Colin Cross | 4be8f9e | 2018-09-28 15:16:48 -0700 | [diff] [blame] | 579 | args.IgnoreMissingFiles = test.ignoreMissingFiles | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 580 | args.Sha256Checksum = test.sha256Checksum | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 581 | args.Filesystem = mockFs | 
| Colin Cross | 4be8f9e | 2018-09-28 15:16:48 -0700 | [diff] [blame] | 582 | args.Stderr = &bytes.Buffer{} | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 583 |  | 
|  | 584 | buf := &bytes.Buffer{} | 
| Sasha Smundak | 8eedba6 | 2020-11-16 19:00:27 -0800 | [diff] [blame] | 585 | err := zipTo(args, buf) | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 586 |  | 
|  | 587 | if (err != nil) != (test.err != nil) { | 
|  | 588 | t.Fatalf("want error %v, got %v", test.err, err) | 
|  | 589 | } else if test.err != nil { | 
|  | 590 | if os.IsNotExist(test.err) { | 
| Colin Cross | 7ddd08a | 2022-08-15 15:47:41 -0700 | [diff] [blame] | 591 | if !os.IsNotExist(err) { | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 592 | t.Fatalf("want error %v, got %v", test.err, err) | 
|  | 593 | } | 
| Colin Cross | 1d98ee2 | 2018-09-18 17:05:15 -0700 | [diff] [blame] | 594 | } else if _, wantRelativeRootErr := test.err.(IncorrectRelativeRootError); wantRelativeRootErr { | 
|  | 595 | if _, gotRelativeRootErr := err.(IncorrectRelativeRootError); !gotRelativeRootErr { | 
|  | 596 | t.Fatalf("want error %v, got %v", test.err, err) | 
|  | 597 | } | 
| Colin Cross | 7ddd08a | 2022-08-15 15:47:41 -0700 | [diff] [blame] | 598 | } else if _, wantConflictingFileError := test.err.(ConflictingFileError); wantConflictingFileError { | 
|  | 599 | if _, gotConflictingFileError := err.(ConflictingFileError); !gotConflictingFileError { | 
|  | 600 | t.Fatalf("want error %v, got %v", test.err, err) | 
|  | 601 | } | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 602 | } else { | 
|  | 603 | t.Fatalf("want error %v, got %v", test.err, err) | 
|  | 604 | } | 
|  | 605 | return | 
|  | 606 | } | 
|  | 607 |  | 
|  | 608 | br := bytes.NewReader(buf.Bytes()) | 
|  | 609 | zr, err := zip.NewReader(br, int64(br.Len())) | 
|  | 610 | if err != nil { | 
|  | 611 | t.Fatal(err) | 
|  | 612 | } | 
|  | 613 |  | 
|  | 614 | var files []zip.FileHeader | 
|  | 615 | for _, f := range zr.File { | 
|  | 616 | r, err := f.Open() | 
|  | 617 | if err != nil { | 
|  | 618 | t.Fatalf("error when opening %s: %s", f.Name, err) | 
|  | 619 | } | 
|  | 620 |  | 
|  | 621 | crc := crc32.NewIEEE() | 
|  | 622 | len, err := io.Copy(crc, r) | 
|  | 623 | r.Close() | 
|  | 624 | if err != nil { | 
|  | 625 | t.Fatalf("error when reading %s: %s", f.Name, err) | 
|  | 626 | } | 
|  | 627 |  | 
|  | 628 | if uint64(len) != f.UncompressedSize64 { | 
|  | 629 | t.Errorf("incorrect length for %s, want %d got %d", f.Name, f.UncompressedSize64, len) | 
|  | 630 | } | 
|  | 631 |  | 
|  | 632 | if crc.Sum32() != f.CRC32 { | 
|  | 633 | t.Errorf("incorrect crc for %s, want %x got %x", f.Name, f.CRC32, crc) | 
|  | 634 | } | 
|  | 635 |  | 
|  | 636 | files = append(files, f.FileHeader) | 
|  | 637 | } | 
|  | 638 |  | 
|  | 639 | if len(files) != len(test.files) { | 
|  | 640 | t.Fatalf("want %d files, got %d", len(test.files), len(files)) | 
|  | 641 | } | 
|  | 642 |  | 
|  | 643 | for i := range files { | 
|  | 644 | want := test.files[i] | 
|  | 645 | got := files[i] | 
|  | 646 |  | 
|  | 647 | if want.Name != got.Name { | 
|  | 648 | t.Errorf("incorrect file %d want %q got %q", i, want.Name, got.Name) | 
|  | 649 | continue | 
|  | 650 | } | 
|  | 651 |  | 
|  | 652 | if want.UncompressedSize64 != got.UncompressedSize64 { | 
|  | 653 | t.Errorf("incorrect file %s length want %v got %v", want.Name, | 
|  | 654 | want.UncompressedSize64, got.UncompressedSize64) | 
|  | 655 | } | 
|  | 656 |  | 
|  | 657 | if want.ExternalAttrs != got.ExternalAttrs { | 
|  | 658 | t.Errorf("incorrect file %s attrs want %x got %x", want.Name, | 
|  | 659 | want.ExternalAttrs, got.ExternalAttrs) | 
|  | 660 | } | 
|  | 661 |  | 
|  | 662 | if want.CRC32 != got.CRC32 { | 
|  | 663 | t.Errorf("incorrect file %s crc want %v got %v", want.Name, | 
|  | 664 | want.CRC32, got.CRC32) | 
|  | 665 | } | 
|  | 666 |  | 
|  | 667 | if want.Method != got.Method { | 
|  | 668 | t.Errorf("incorrect file %s method want %v got %v", want.Name, | 
|  | 669 | want.Method, got.Method) | 
|  | 670 | } | 
| Zhenhuang Wang | b8451b8 | 2023-01-06 20:58:01 +0800 | [diff] [blame] | 671 |  | 
|  | 672 | if !bytes.Equal(want.Extra, got.Extra) { | 
|  | 673 | t.Errorf("incorrect file %s extra want %v got %v", want.Name, | 
|  | 674 | want.Extra, got.Extra) | 
|  | 675 | } | 
| Colin Cross | 05518bc | 2018-09-27 15:06:19 -0700 | [diff] [blame] | 676 | } | 
|  | 677 | }) | 
|  | 678 | } | 
|  | 679 | } | 
|  | 680 |  | 
| Colin Cross | 9cb51db | 2019-06-17 14:12:41 -0700 | [diff] [blame] | 681 | func TestSrcJar(t *testing.T) { | 
|  | 682 | mockFs := pathtools.MockFs(map[string][]byte{ | 
|  | 683 | "wrong_package.java":       []byte("package foo;"), | 
|  | 684 | "foo/correct_package.java": []byte("package foo;"), | 
|  | 685 | "src/no_package.java":      nil, | 
|  | 686 | "src2/parse_error.java":    []byte("error"), | 
|  | 687 | }) | 
|  | 688 |  | 
|  | 689 | want := []string{ | 
|  | 690 | "foo/", | 
|  | 691 | "foo/wrong_package.java", | 
|  | 692 | "foo/correct_package.java", | 
|  | 693 | "no_package.java", | 
|  | 694 | "src2/", | 
|  | 695 | "src2/parse_error.java", | 
|  | 696 | } | 
|  | 697 |  | 
|  | 698 | args := ZipArgs{} | 
|  | 699 | args.FileArgs = NewFileArgsBuilder().File("**/*.java").FileArgs() | 
|  | 700 |  | 
|  | 701 | args.SrcJar = true | 
|  | 702 | args.AddDirectoryEntriesToZip = true | 
|  | 703 | args.Filesystem = mockFs | 
|  | 704 | args.Stderr = &bytes.Buffer{} | 
|  | 705 |  | 
|  | 706 | buf := &bytes.Buffer{} | 
| Sasha Smundak | 8eedba6 | 2020-11-16 19:00:27 -0800 | [diff] [blame] | 707 | err := zipTo(args, buf) | 
| Colin Cross | 9cb51db | 2019-06-17 14:12:41 -0700 | [diff] [blame] | 708 | if err != nil { | 
|  | 709 | t.Fatalf("got error %v", err) | 
|  | 710 | } | 
|  | 711 |  | 
|  | 712 | br := bytes.NewReader(buf.Bytes()) | 
|  | 713 | zr, err := zip.NewReader(br, int64(br.Len())) | 
|  | 714 | if err != nil { | 
|  | 715 | t.Fatal(err) | 
|  | 716 | } | 
|  | 717 |  | 
|  | 718 | var got []string | 
|  | 719 | for _, f := range zr.File { | 
|  | 720 | r, err := f.Open() | 
|  | 721 | if err != nil { | 
|  | 722 | t.Fatalf("error when opening %s: %s", f.Name, err) | 
|  | 723 | } | 
|  | 724 |  | 
|  | 725 | crc := crc32.NewIEEE() | 
|  | 726 | len, err := io.Copy(crc, r) | 
|  | 727 | r.Close() | 
|  | 728 | if err != nil { | 
|  | 729 | t.Fatalf("error when reading %s: %s", f.Name, err) | 
|  | 730 | } | 
|  | 731 |  | 
|  | 732 | if uint64(len) != f.UncompressedSize64 { | 
|  | 733 | t.Errorf("incorrect length for %s, want %d got %d", f.Name, f.UncompressedSize64, len) | 
|  | 734 | } | 
|  | 735 |  | 
|  | 736 | if crc.Sum32() != f.CRC32 { | 
|  | 737 | t.Errorf("incorrect crc for %s, want %x got %x", f.Name, f.CRC32, crc) | 
|  | 738 | } | 
|  | 739 |  | 
|  | 740 | got = append(got, f.Name) | 
|  | 741 | } | 
|  | 742 |  | 
|  | 743 | if !reflect.DeepEqual(want, got) { | 
|  | 744 | t.Errorf("want files %q, got %q", want, got) | 
|  | 745 | } | 
|  | 746 | } |