blob: 85a69efeb3a183b332a55af03cb02614ed55433a [file] [log] [blame]
Dan Willemsen82218f22017-06-19 16:35:00 -07001// Copyright 2017 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
15package main
16
17import (
18 "bytes"
19 "fmt"
20 "reflect"
21 "testing"
22
23 "android/soong/third_party/zip"
24)
25
26var testCases = []struct {
27 name string
28
Colin Crossb1a5e9c2018-10-07 21:30:12 -070029 inputFiles []string
30 sortGlobs bool
31 sortJava bool
32 args []string
33 excludes []string
Colin Cross38247712018-10-09 10:19:54 -070034 includes []string
Colin Crossb1a5e9c2018-10-07 21:30:12 -070035 uncompresses []string
Dan Willemsen82218f22017-06-19 16:35:00 -070036
37 outputFiles []string
Colin Crossb1a5e9c2018-10-07 21:30:12 -070038 storedFiles []string
Dan Willemsen82218f22017-06-19 16:35:00 -070039 err error
40}{
Dan Willemsen82218f22017-06-19 16:35:00 -070041 { // This is modelled after the update package build rules in build/make/core/Makefile
42 name: "filter globs",
43
44 inputFiles: []string{
45 "RADIO/a",
46 "IMAGES/system.img",
47 "IMAGES/b.txt",
48 "IMAGES/recovery.img",
49 "IMAGES/vendor.img",
50 "OTA/android-info.txt",
51 "OTA/b",
52 },
53 args: []string{"OTA/android-info.txt:android-info.txt", "IMAGES/*.img:."},
54
55 outputFiles: []string{
56 "android-info.txt",
57 "system.img",
58 "recovery.img",
59 "vendor.img",
60 },
61 },
62 {
63 name: "sorted filter globs",
64
65 inputFiles: []string{
66 "RADIO/a",
67 "IMAGES/system.img",
68 "IMAGES/b.txt",
69 "IMAGES/recovery.img",
70 "IMAGES/vendor.img",
71 "OTA/android-info.txt",
72 "OTA/b",
73 },
74 sortGlobs: true,
75 args: []string{"IMAGES/*.img:.", "OTA/android-info.txt:android-info.txt"},
76
77 outputFiles: []string{
78 "recovery.img",
79 "system.img",
80 "vendor.img",
81 "android-info.txt",
82 },
83 },
84 {
85 name: "sort all",
86
87 inputFiles: []string{
Colin Crossf3831d02017-11-22 12:53:08 -080088 "RADIO/",
Dan Willemsen82218f22017-06-19 16:35:00 -070089 "RADIO/a",
Colin Crossf3831d02017-11-22 12:53:08 -080090 "IMAGES/",
Dan Willemsen82218f22017-06-19 16:35:00 -070091 "IMAGES/system.img",
92 "IMAGES/b.txt",
93 "IMAGES/recovery.img",
94 "IMAGES/vendor.img",
Colin Crossf3831d02017-11-22 12:53:08 -080095 "OTA/",
Dan Willemsen82218f22017-06-19 16:35:00 -070096 "OTA/b",
97 "OTA/android-info.txt",
98 },
99 sortGlobs: true,
Colin Crossf3831d02017-11-22 12:53:08 -0800100 args: []string{"**/*"},
Dan Willemsen82218f22017-06-19 16:35:00 -0700101
102 outputFiles: []string{
103 "IMAGES/b.txt",
104 "IMAGES/recovery.img",
105 "IMAGES/system.img",
106 "IMAGES/vendor.img",
107 "OTA/android-info.txt",
108 "OTA/b",
109 "RADIO/a",
110 },
111 },
112 {
Colin Cross06382992017-06-23 14:08:42 -0700113 name: "sort all implicit",
114
115 inputFiles: []string{
Colin Crossf3831d02017-11-22 12:53:08 -0800116 "RADIO/",
Colin Cross06382992017-06-23 14:08:42 -0700117 "RADIO/a",
Colin Crossf3831d02017-11-22 12:53:08 -0800118 "IMAGES/",
Colin Cross06382992017-06-23 14:08:42 -0700119 "IMAGES/system.img",
120 "IMAGES/b.txt",
121 "IMAGES/recovery.img",
122 "IMAGES/vendor.img",
Colin Crossf3831d02017-11-22 12:53:08 -0800123 "OTA/",
Colin Cross06382992017-06-23 14:08:42 -0700124 "OTA/b",
125 "OTA/android-info.txt",
126 },
127 sortGlobs: true,
128 args: nil,
129
130 outputFiles: []string{
Colin Crossf3831d02017-11-22 12:53:08 -0800131 "IMAGES/",
Colin Cross06382992017-06-23 14:08:42 -0700132 "IMAGES/b.txt",
133 "IMAGES/recovery.img",
134 "IMAGES/system.img",
135 "IMAGES/vendor.img",
Colin Crossf3831d02017-11-22 12:53:08 -0800136 "OTA/",
Colin Cross06382992017-06-23 14:08:42 -0700137 "OTA/android-info.txt",
138 "OTA/b",
Colin Crossf3831d02017-11-22 12:53:08 -0800139 "RADIO/",
Colin Cross06382992017-06-23 14:08:42 -0700140 "RADIO/a",
141 },
142 },
143 {
Colin Cross8936b022017-06-23 13:00:17 -0700144 name: "sort jar",
145
146 inputFiles: []string{
147 "MANIFEST.MF",
148 "META-INF/MANIFEST.MF",
149 "META-INF/aaa/",
150 "META-INF/aaa/aaa",
151 "META-INF/AAA",
152 "META-INF.txt",
153 "META-INF/",
154 "AAA",
155 "aaa",
156 },
157 sortJava: true,
158 args: nil,
159
160 outputFiles: []string{
161 "META-INF/",
162 "META-INF/MANIFEST.MF",
163 "META-INF/AAA",
164 "META-INF/aaa/",
165 "META-INF/aaa/aaa",
166 "AAA",
167 "MANIFEST.MF",
168 "META-INF.txt",
169 "aaa",
170 },
171 },
172 {
Dan Willemsen82218f22017-06-19 16:35:00 -0700173 name: "double input",
174
175 inputFiles: []string{
176 "b",
177 "a",
178 },
Colin Crossf3831d02017-11-22 12:53:08 -0800179 args: []string{"a:a2", "**/*"},
Dan Willemsen82218f22017-06-19 16:35:00 -0700180
181 outputFiles: []string{
182 "a2",
183 "b",
184 "a",
185 },
186 },
Colin Crossf3831d02017-11-22 12:53:08 -0800187 {
188 name: "multiple matches",
189
190 inputFiles: []string{
191 "a/a",
192 },
193 args: []string{"a/a", "a/*"},
194
195 outputFiles: []string{
196 "a/a",
197 },
198 },
199 {
200 name: "multiple conflicting matches",
201
202 inputFiles: []string{
203 "a/a",
204 "a/b",
205 },
206 args: []string{"a/b:a/a", "a/*"},
207
208 err: fmt.Errorf(`multiple entries for "a/a" with different contents`),
209 },
210 {
211 name: "excludes",
212
213 inputFiles: []string{
214 "a/a",
215 "a/b",
216 },
217 args: nil,
218 excludes: []string{"a/a"},
219
220 outputFiles: []string{
221 "a/b",
222 },
223 },
224 {
Colin Cross38247712018-10-09 10:19:54 -0700225 name: "excludes with args",
Colin Crossf3831d02017-11-22 12:53:08 -0800226
227 inputFiles: []string{
228 "a/a",
229 "a/b",
230 },
231 args: []string{"a/*"},
232 excludes: []string{"a/a"},
233
234 outputFiles: []string{
235 "a/b",
236 },
237 },
238 {
Colin Cross38247712018-10-09 10:19:54 -0700239 name: "excludes over args",
240
241 inputFiles: []string{
242 "a/a",
243 "a/b",
244 },
245 args: []string{"a/a"},
246 excludes: []string{"a/*"},
247
248 outputFiles: nil,
249 },
250 {
251 name: "excludes with includes",
252
253 inputFiles: []string{
254 "a/a",
255 "a/b",
256 },
257 args: nil,
258 excludes: []string{"a/*"},
259 includes: []string{"a/b"},
260
261 outputFiles: []string{"a/b"},
262 },
263 {
Colin Crossf3831d02017-11-22 12:53:08 -0800264 name: "excludes with glob",
265
266 inputFiles: []string{
267 "a/a",
268 "a/b",
269 },
270 args: []string{"a/*"},
271 excludes: []string{"a/*"},
272
273 outputFiles: nil,
274 },
Colin Crossb1a5e9c2018-10-07 21:30:12 -0700275 {
276 name: "uncompress one",
277
278 inputFiles: []string{
279 "a/a",
280 "a/b",
281 },
282 uncompresses: []string{"a/a"},
283
284 outputFiles: []string{
285 "a/a",
286 "a/b",
287 },
288 storedFiles: []string{
289 "a/a",
290 },
291 },
292 {
293 name: "uncompress two",
294
295 inputFiles: []string{
296 "a/a",
297 "a/b",
298 },
299 uncompresses: []string{"a/a", "a/b"},
300
301 outputFiles: []string{
302 "a/a",
303 "a/b",
304 },
305 storedFiles: []string{
306 "a/a",
307 "a/b",
308 },
309 },
310 {
311 name: "uncompress glob",
312
313 inputFiles: []string{
314 "a/a",
315 "a/b",
316 "a/c.so",
317 "a/d.so",
318 },
319 uncompresses: []string{"a/*.so"},
320
321 outputFiles: []string{
322 "a/a",
323 "a/b",
324 "a/c.so",
325 "a/d.so",
326 },
327 storedFiles: []string{
328 "a/c.so",
329 "a/d.so",
330 },
331 },
332 {
333 name: "uncompress rename",
334
335 inputFiles: []string{
336 "a/a",
337 },
338 args: []string{"a/a:a/b"},
339 uncompresses: []string{"a/b"},
340
341 outputFiles: []string{
342 "a/b",
343 },
344 storedFiles: []string{
345 "a/b",
346 },
347 },
Colin Cross714614c2018-11-01 13:27:58 -0700348 {
349 name: "recursive glob",
350
351 inputFiles: []string{
352 "a/a/a",
353 "a/a/b",
354 },
355 args: []string{"a/**/*:b"},
356 outputFiles: []string{
357 "b/a/a",
358 "b/a/b",
359 },
360 },
361 {
362 name: "glob",
363
364 inputFiles: []string{
365 "a/a/a",
366 "a/a/b",
367 "a/b",
368 "a/c",
369 },
370 args: []string{"a/*:b"},
371 outputFiles: []string{
372 "b/b",
373 "b/c",
374 },
375 },
376 {
377 name: "top level glob",
378
379 inputFiles: []string{
380 "a",
381 "b",
382 },
383 args: []string{"*:b"},
384 outputFiles: []string{
385 "b/a",
386 "b/b",
387 },
388 },
389 {
390 name: "multilple glob",
391
392 inputFiles: []string{
393 "a/a/a",
394 "a/a/b",
395 },
396 args: []string{"a/*/*:b"},
397 outputFiles: []string{
398 "b/a/a",
399 "b/a/b",
400 },
401 },
Alixc6a918a2023-04-07 19:42:02 +0000402 {
403 name: "escaping",
404
405 inputFiles: []string{"a"},
406 args: []string{"\\a"},
407 outputFiles: []string{"a"},
408 },
Dan Willemsen82218f22017-06-19 16:35:00 -0700409}
410
411func errorString(e error) string {
412 if e == nil {
413 return ""
414 }
415 return e.Error()
416}
417
418func TestZip2Zip(t *testing.T) {
419 for _, testCase := range testCases {
420 t.Run(testCase.name, func(t *testing.T) {
421 inputBuf := &bytes.Buffer{}
422 outputBuf := &bytes.Buffer{}
423
424 inputWriter := zip.NewWriter(inputBuf)
425 for _, file := range testCase.inputFiles {
426 w, err := inputWriter.Create(file)
427 if err != nil {
428 t.Fatal(err)
429 }
430 fmt.Fprintln(w, "test")
431 }
432 inputWriter.Close()
433 inputBytes := inputBuf.Bytes()
434 inputReader, err := zip.NewReader(bytes.NewReader(inputBytes), int64(len(inputBytes)))
435 if err != nil {
436 t.Fatal(err)
437 }
438
439 outputWriter := zip.NewWriter(outputBuf)
Colin Crossb1a5e9c2018-10-07 21:30:12 -0700440 err = zip2zip(inputReader, outputWriter, testCase.sortGlobs, testCase.sortJava, false,
Colin Cross38247712018-10-09 10:19:54 -0700441 testCase.args, testCase.excludes, testCase.includes, testCase.uncompresses)
Dan Willemsen82218f22017-06-19 16:35:00 -0700442 if errorString(testCase.err) != errorString(err) {
443 t.Fatalf("Unexpected error:\n got: %q\nwant: %q", errorString(err), errorString(testCase.err))
444 }
445
446 outputWriter.Close()
447 outputBytes := outputBuf.Bytes()
448 outputReader, err := zip.NewReader(bytes.NewReader(outputBytes), int64(len(outputBytes)))
449 if err != nil {
450 t.Fatal(err)
451 }
452 var outputFiles []string
Colin Crossb1a5e9c2018-10-07 21:30:12 -0700453 var storedFiles []string
Dan Willemsen82218f22017-06-19 16:35:00 -0700454 if len(outputReader.File) > 0 {
455 outputFiles = make([]string, len(outputReader.File))
456 for i, file := range outputReader.File {
457 outputFiles[i] = file.Name
Colin Crossb1a5e9c2018-10-07 21:30:12 -0700458 if file.Method == zip.Store {
459 storedFiles = append(storedFiles, file.Name)
460 }
Dan Willemsen82218f22017-06-19 16:35:00 -0700461 }
462 }
463
464 if !reflect.DeepEqual(testCase.outputFiles, outputFiles) {
Colin Crossb1a5e9c2018-10-07 21:30:12 -0700465 t.Fatalf("Output file list does not match:\nwant: %v\n got: %v", testCase.outputFiles, outputFiles)
466 }
467 if !reflect.DeepEqual(testCase.storedFiles, storedFiles) {
468 t.Fatalf("Stored file list does not match:\nwant: %v\n got: %v", testCase.storedFiles, storedFiles)
Dan Willemsen82218f22017-06-19 16:35:00 -0700469 }
470 })
471 }
472}
Colin Cross714614c2018-11-01 13:27:58 -0700473
Colin Crossc2a62d42023-08-22 14:22:28 -0700474// TestZip2Zip64 tests that zip2zip on zip file larger than 4GB produces a valid zip file.
475func TestZip2Zip64(t *testing.T) {
476 if testing.Short() {
477 t.Skip("skipping slow test in short mode")
478 }
479 inputBuf := &bytes.Buffer{}
480 outputBuf := &bytes.Buffer{}
481
482 inputWriter := zip.NewWriter(inputBuf)
483 w, err := inputWriter.CreateHeaderAndroid(&zip.FileHeader{
484 Name: "a",
485 Method: zip.Store,
486 })
487 if err != nil {
488 t.Fatal(err)
489 }
490 buf := make([]byte, 4*1024*1024)
491 for i := 0; i < 1025; i++ {
492 w.Write(buf)
493 }
494 w, err = inputWriter.CreateHeaderAndroid(&zip.FileHeader{
495 Name: "b",
496 Method: zip.Store,
497 })
498 for i := 0; i < 1025; i++ {
499 w.Write(buf)
500 }
501 inputWriter.Close()
502 inputBytes := inputBuf.Bytes()
503
504 inputReader, err := zip.NewReader(bytes.NewReader(inputBytes), int64(len(inputBytes)))
505 if err != nil {
506 t.Fatal(err)
507 }
508
509 outputWriter := zip.NewWriter(outputBuf)
510 err = zip2zip(inputReader, outputWriter, false, false, false,
511 nil, nil, nil, nil)
512 if err != nil {
513 t.Fatal(err)
514 }
515
516 outputWriter.Close()
517 outputBytes := outputBuf.Bytes()
518 _, err = zip.NewReader(bytes.NewReader(outputBytes), int64(len(outputBytes)))
519 if err != nil {
520 t.Fatal(err)
521 }
522}
523
Colin Cross714614c2018-11-01 13:27:58 -0700524func TestConstantPartOfPattern(t *testing.T) {
525 testCases := []struct{ in, out string }{
526 {
527 in: "",
528 out: "",
529 },
530 {
531 in: "a",
532 out: "a",
533 },
534 {
535 in: "*",
536 out: "",
537 },
538 {
539 in: "a/a",
540 out: "a/a",
541 },
542 {
543 in: "a/*",
544 out: "a",
545 },
546 {
547 in: "a/*/a",
548 out: "a",
549 },
550 {
551 in: "a/**/*",
552 out: "a",
553 },
554 }
555
556 for _, test := range testCases {
557 t.Run(test.in, func(t *testing.T) {
558 got := constantPartOfPattern(test.in)
559 if got != test.out {
560 t.Errorf("want %q, got %q", test.out, got)
561 }
562 })
563 }
564}