blob: cb5843607f86034721eb557d7e9af5b50a0b811f [file] [log] [blame]
Colin Cross24860652018-07-14 22:19:14 -07001// 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
15package main
16
17import (
18 "bytes"
19 "fmt"
20 "os"
21 "strconv"
22 "strings"
23 "testing"
24
25 "android/soong/jar"
26 "android/soong/third_party/zip"
27)
28
29type testZipEntry struct {
30 name string
31 mode os.FileMode
32 data []byte
33}
34
35var (
36 A = testZipEntry{"A", 0755, []byte("foo")}
37 a = testZipEntry{"a", 0755, []byte("foo")}
38 a2 = testZipEntry{"a", 0755, []byte("FOO2")}
39 a3 = testZipEntry{"a", 0755, []byte("Foo3")}
40 bDir = testZipEntry{"b/", os.ModeDir | 0755, nil}
41 bbDir = testZipEntry{"b/b/", os.ModeDir | 0755, nil}
42 bbb = testZipEntry{"b/b/b", 0755, nil}
43 ba = testZipEntry{"b/a", 0755, []byte("foob")}
44 bc = testZipEntry{"b/c", 0755, []byte("bar")}
45 bd = testZipEntry{"b/d", 0700, []byte("baz")}
46 be = testZipEntry{"b/e", 0700, []byte("")}
47
48 metainfDir = testZipEntry{jar.MetaDir, os.ModeDir | 0755, nil}
49 manifestFile = testZipEntry{jar.ManifestFile, 0755, []byte("manifest")}
50 manifestFile2 = testZipEntry{jar.ManifestFile, 0755, []byte("manifest2")}
51 moduleInfoFile = testZipEntry{jar.ModuleInfoClass, 0755, []byte("module-info")}
52)
53
Sasha Smundak1459a922019-07-16 18:45:24 -070054type testInputZip struct {
55 name string
56 entries []testZipEntry
57 reader *zip.Reader
58}
59
60func (tiz *testInputZip) Name() string {
61 return tiz.name
62}
63
64func (tiz *testInputZip) Open() error {
65 if tiz.reader == nil {
66 tiz.reader = testZipEntriesToZipReader(tiz.entries)
67 }
68 return nil
69}
70
71func (tiz *testInputZip) Close() error {
72 tiz.reader = nil
73 return nil
74}
75
76func (tiz *testInputZip) Entries() []*zip.File {
77 if tiz.reader == nil {
78 panic(fmt.Errorf("%s: should be open to get entries", tiz.Name()))
79 }
80 return tiz.reader.File
81}
82
83func (tiz *testInputZip) IsOpen() bool {
84 return tiz.reader != nil
85}
86
Colin Cross24860652018-07-14 22:19:14 -070087func TestMergeZips(t *testing.T) {
88 testCases := []struct {
89 name string
90 in [][]testZipEntry
91 stripFiles []string
92 stripDirs []string
93 jar bool
94 sort bool
95 ignoreDuplicates bool
96 stripDirEntries bool
97 zipsToNotStrip map[string]bool
98
99 out []testZipEntry
100 err string
101 }{
102 {
103 name: "duplicates error",
104 in: [][]testZipEntry{
105 {a},
106 {a2},
107 {a3},
108 },
109 out: []testZipEntry{a},
110 err: "duplicate",
111 },
112 {
113 name: "duplicates take first",
114 in: [][]testZipEntry{
115 {a},
116 {a2},
117 {a3},
118 },
119 out: []testZipEntry{a},
120
121 ignoreDuplicates: true,
122 },
123 {
Colin Crossdc1e8292018-10-17 15:05:56 -0700124 name: "duplicates identical",
125 in: [][]testZipEntry{
126 {a},
127 {a},
128 },
129 out: []testZipEntry{a},
130 },
131 {
Colin Cross24860652018-07-14 22:19:14 -0700132 name: "sort",
133 in: [][]testZipEntry{
134 {be, bc, bDir, bbDir, bbb, A, metainfDir, manifestFile},
135 },
136 out: []testZipEntry{A, metainfDir, manifestFile, bDir, bbDir, bbb, bc, be},
137
138 sort: true,
139 },
140 {
141 name: "jar sort",
142 in: [][]testZipEntry{
143 {be, bc, bDir, A, metainfDir, manifestFile},
144 },
145 out: []testZipEntry{metainfDir, manifestFile, A, bDir, bc, be},
146
147 jar: true,
148 },
149 {
150 name: "jar merge",
151 in: [][]testZipEntry{
152 {metainfDir, manifestFile, bDir, be},
153 {metainfDir, manifestFile2, bDir, bc},
154 {metainfDir, manifestFile2, A},
155 },
156 out: []testZipEntry{metainfDir, manifestFile, A, bDir, bc, be},
157
158 jar: true,
159 },
160 {
161 name: "merge",
162 in: [][]testZipEntry{
163 {bDir, be},
164 {bDir, bc},
165 {A},
166 },
167 out: []testZipEntry{bDir, be, bc, A},
168 },
169 {
170 name: "strip dir entries",
171 in: [][]testZipEntry{
172 {a, bDir, bbDir, bbb, bc, bd, be},
173 },
174 out: []testZipEntry{a, bbb, bc, bd, be},
175
176 stripDirEntries: true,
177 },
178 {
Colin Cross4c03f682018-07-15 08:16:31 -0700179 name: "strip files",
180 in: [][]testZipEntry{
181 {a, bDir, bbDir, bbb, bc, bd, be},
182 },
183 out: []testZipEntry{a, bDir, bbDir, bbb, bc},
184
185 stripFiles: []string{"b/d", "b/e"},
186 },
187 {
188 // merge_zips used to treat -stripFile a as stripping any file named a, it now only strips a in the
189 // root of the zip.
Colin Cross24860652018-07-14 22:19:14 -0700190 name: "strip file name",
191 in: [][]testZipEntry{
192 {a, bDir, ba},
193 },
Colin Cross4c03f682018-07-15 08:16:31 -0700194 out: []testZipEntry{bDir, ba},
195
196 stripFiles: []string{"a"},
197 },
198 {
199 name: "strip files glob",
200 in: [][]testZipEntry{
201 {a, bDir, ba},
202 },
Colin Cross24860652018-07-14 22:19:14 -0700203 out: []testZipEntry{bDir},
204
Colin Cross4c03f682018-07-15 08:16:31 -0700205 stripFiles: []string{"**/a"},
Colin Cross24860652018-07-14 22:19:14 -0700206 },
207 {
208 name: "strip dirs",
209 in: [][]testZipEntry{
210 {a, bDir, bbDir, bbb, bc, bd, be},
211 },
212 out: []testZipEntry{a},
213
214 stripDirs: []string{"b"},
215 },
216 {
Colin Cross4c03f682018-07-15 08:16:31 -0700217 name: "strip dirs glob",
218 in: [][]testZipEntry{
219 {a, bDir, bbDir, bbb, bc, bd, be},
220 },
221 out: []testZipEntry{a, bDir, bc, bd, be},
222
223 stripDirs: []string{"b/*"},
224 },
225 {
Colin Cross24860652018-07-14 22:19:14 -0700226 name: "zips to not strip",
227 in: [][]testZipEntry{
228 {a, bDir, bc},
229 {bDir, bd},
230 {bDir, be},
231 },
232 out: []testZipEntry{a, bDir, bd},
233
234 stripDirs: []string{"b"},
235 zipsToNotStrip: map[string]bool{
236 "in1": true,
237 },
238 },
239 }
240
241 for _, test := range testCases {
242 t.Run(test.name, func(t *testing.T) {
Sasha Smundak1459a922019-07-16 18:45:24 -0700243 inputZips := make([]InputZip, len(test.in))
Colin Cross24860652018-07-14 22:19:14 -0700244 for i, in := range test.in {
Sasha Smundak1459a922019-07-16 18:45:24 -0700245 inputZips[i] = &testInputZip{name: "in" + strconv.Itoa(i), entries: in}
Colin Cross24860652018-07-14 22:19:14 -0700246 }
247
248 want := testZipEntriesToBuf(test.out)
249
250 out := &bytes.Buffer{}
251 writer := zip.NewWriter(out)
252
Sasha Smundak1459a922019-07-16 18:45:24 -0700253 err := mergeZips(inputZips, writer, "", "",
Colin Cross24860652018-07-14 22:19:14 -0700254 test.sort, test.jar, false, test.stripDirEntries, test.ignoreDuplicates,
255 test.stripFiles, test.stripDirs, test.zipsToNotStrip)
256
257 closeErr := writer.Close()
258 if closeErr != nil {
259 t.Fatal(err)
260 }
261
262 if test.err != "" {
263 if err == nil {
264 t.Fatal("missing err, expected: ", test.err)
265 } else if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(test.err)) {
266 t.Fatal("incorrect err, want:", test.err, "got:", err)
267 }
268 return
269 }
270
271 if !bytes.Equal(want, out.Bytes()) {
272 t.Error("incorrect zip output")
273 t.Errorf("want:\n%s", dumpZip(want))
274 t.Errorf("got:\n%s", dumpZip(out.Bytes()))
275 }
276 })
277 }
278}
279
280func testZipEntriesToBuf(entries []testZipEntry) []byte {
281 b := &bytes.Buffer{}
282 zw := zip.NewWriter(b)
283
284 for _, e := range entries {
285 fh := zip.FileHeader{
286 Name: e.name,
287 }
288 fh.SetMode(e.mode)
289
290 w, err := zw.CreateHeader(&fh)
291 if err != nil {
292 panic(err)
293 }
294
295 _, err = w.Write(e.data)
296 if err != nil {
297 panic(err)
298 }
299 }
300
301 err := zw.Close()
302 if err != nil {
303 panic(err)
304 }
305
306 return b.Bytes()
307}
308
309func testZipEntriesToZipReader(entries []testZipEntry) *zip.Reader {
310 b := testZipEntriesToBuf(entries)
311 r := bytes.NewReader(b)
312
313 zr, err := zip.NewReader(r, int64(len(b)))
314 if err != nil {
315 panic(err)
316 }
317
318 return zr
319}
320
321func dumpZip(buf []byte) string {
322 r := bytes.NewReader(buf)
323 zr, err := zip.NewReader(r, int64(len(buf)))
324 if err != nil {
325 panic(err)
326 }
327
328 var ret string
329
330 for _, f := range zr.File {
331 ret += fmt.Sprintf("%v: %v %v %08x\n", f.Name, f.Mode(), f.UncompressedSize64, f.CRC32)
332 }
333
334 return ret
335}
Sasha Smundak1459a922019-07-16 18:45:24 -0700336
337type DummyInpuZip struct {
338 isOpen bool
339}
340
341func (diz *DummyInpuZip) Name() string {
342 return "dummy"
343}
344
345func (diz *DummyInpuZip) Open() error {
346 diz.isOpen = true
347 return nil
348}
349
350func (diz *DummyInpuZip) Close() error {
351 diz.isOpen = false
352 return nil
353}
354
355func (DummyInpuZip) Entries() []*zip.File {
356 panic("implement me")
357}
358
359func (diz *DummyInpuZip) IsOpen() bool {
360 return diz.isOpen
361}
362
363func TestInputZipsManager(t *testing.T) {
364 const nInputZips = 20
365 const nMaxOpenZips = 10
366 izm := NewInputZipsManager(20, 10)
367 managedZips := make([]InputZip, nInputZips)
368 for i := 0; i < nInputZips; i++ {
369 managedZips[i] = izm.Manage(&DummyInpuZip{})
370 }
371
372 t.Run("InputZipsManager", func(t *testing.T) {
373 for i, iz := range managedZips {
374 if err := iz.Open(); err != nil {
375 t.Fatalf("Step %d: open failed: %s", i, err)
376 return
377 }
378 if izm.nOpenZips > nMaxOpenZips {
379 t.Errorf("Step %d: should be <=%d open zips", i, nMaxOpenZips)
380 }
381 }
382 if !managedZips[nInputZips-1].IsOpen() {
383 t.Error("The last input should stay open")
384 }
385 for _, iz := range managedZips {
386 iz.Close()
387 }
388 if izm.nOpenZips > 0 {
389 t.Error("Some input zips are still open")
390 }
391 })
392}