• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
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
54func TestMergeZips(t *testing.T) {
55	testCases := []struct {
56		name             string
57		in               [][]testZipEntry
58		stripFiles       []string
59		stripDirs        []string
60		jar              bool
61		sort             bool
62		ignoreDuplicates bool
63		stripDirEntries  bool
64		zipsToNotStrip   map[string]bool
65
66		out []testZipEntry
67		err string
68	}{
69		{
70			name: "duplicates error",
71			in: [][]testZipEntry{
72				{a},
73				{a2},
74				{a3},
75			},
76			out: []testZipEntry{a},
77			err: "duplicate",
78		},
79		{
80			name: "duplicates take first",
81			in: [][]testZipEntry{
82				{a},
83				{a2},
84				{a3},
85			},
86			out: []testZipEntry{a},
87
88			ignoreDuplicates: true,
89		},
90		{
91			name: "duplicates identical",
92			in: [][]testZipEntry{
93				{a},
94				{a},
95			},
96			out: []testZipEntry{a},
97		},
98		{
99			name: "sort",
100			in: [][]testZipEntry{
101				{be, bc, bDir, bbDir, bbb, A, metainfDir, manifestFile},
102			},
103			out: []testZipEntry{A, metainfDir, manifestFile, bDir, bbDir, bbb, bc, be},
104
105			sort: true,
106		},
107		{
108			name: "jar sort",
109			in: [][]testZipEntry{
110				{be, bc, bDir, A, metainfDir, manifestFile},
111			},
112			out: []testZipEntry{metainfDir, manifestFile, A, bDir, bc, be},
113
114			jar: true,
115		},
116		{
117			name: "jar merge",
118			in: [][]testZipEntry{
119				{metainfDir, manifestFile, bDir, be},
120				{metainfDir, manifestFile2, bDir, bc},
121				{metainfDir, manifestFile2, A},
122			},
123			out: []testZipEntry{metainfDir, manifestFile, A, bDir, bc, be},
124
125			jar: true,
126		},
127		{
128			name: "merge",
129			in: [][]testZipEntry{
130				{bDir, be},
131				{bDir, bc},
132				{A},
133			},
134			out: []testZipEntry{bDir, be, bc, A},
135		},
136		{
137			name: "strip dir entries",
138			in: [][]testZipEntry{
139				{a, bDir, bbDir, bbb, bc, bd, be},
140			},
141			out: []testZipEntry{a, bbb, bc, bd, be},
142
143			stripDirEntries: true,
144		},
145		{
146			name: "strip files",
147			in: [][]testZipEntry{
148				{a, bDir, bbDir, bbb, bc, bd, be},
149			},
150			out: []testZipEntry{a, bDir, bbDir, bbb, bc},
151
152			stripFiles: []string{"b/d", "b/e"},
153		},
154		{
155			// merge_zips used to treat -stripFile a as stripping any file named a, it now only strips a in the
156			// root of the zip.
157			name: "strip file name",
158			in: [][]testZipEntry{
159				{a, bDir, ba},
160			},
161			out: []testZipEntry{bDir, ba},
162
163			stripFiles: []string{"a"},
164		},
165		{
166			name: "strip files glob",
167			in: [][]testZipEntry{
168				{a, bDir, ba},
169			},
170			out: []testZipEntry{bDir},
171
172			stripFiles: []string{"**/a"},
173		},
174		{
175			name: "strip dirs",
176			in: [][]testZipEntry{
177				{a, bDir, bbDir, bbb, bc, bd, be},
178			},
179			out: []testZipEntry{a},
180
181			stripDirs: []string{"b"},
182		},
183		{
184			name: "strip dirs glob",
185			in: [][]testZipEntry{
186				{a, bDir, bbDir, bbb, bc, bd, be},
187			},
188			out: []testZipEntry{a, bDir, bc, bd, be},
189
190			stripDirs: []string{"b/*"},
191		},
192		{
193			name: "zips to not strip",
194			in: [][]testZipEntry{
195				{a, bDir, bc},
196				{bDir, bd},
197				{bDir, be},
198			},
199			out: []testZipEntry{a, bDir, bd},
200
201			stripDirs: []string{"b"},
202			zipsToNotStrip: map[string]bool{
203				"in1": true,
204			},
205		},
206	}
207
208	for _, test := range testCases {
209		t.Run(test.name, func(t *testing.T) {
210			var readers []namedZipReader
211			for i, in := range test.in {
212				r := testZipEntriesToZipReader(in)
213				readers = append(readers, namedZipReader{
214					path:   "in" + strconv.Itoa(i),
215					reader: r,
216				})
217			}
218
219			want := testZipEntriesToBuf(test.out)
220
221			out := &bytes.Buffer{}
222			writer := zip.NewWriter(out)
223
224			err := mergeZips(readers, writer, "", "",
225				test.sort, test.jar, false, test.stripDirEntries, test.ignoreDuplicates,
226				test.stripFiles, test.stripDirs, test.zipsToNotStrip)
227
228			closeErr := writer.Close()
229			if closeErr != nil {
230				t.Fatal(err)
231			}
232
233			if test.err != "" {
234				if err == nil {
235					t.Fatal("missing err, expected: ", test.err)
236				} else if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(test.err)) {
237					t.Fatal("incorrect err, want:", test.err, "got:", err)
238				}
239				return
240			}
241
242			if !bytes.Equal(want, out.Bytes()) {
243				t.Error("incorrect zip output")
244				t.Errorf("want:\n%s", dumpZip(want))
245				t.Errorf("got:\n%s", dumpZip(out.Bytes()))
246			}
247		})
248	}
249}
250
251func testZipEntriesToBuf(entries []testZipEntry) []byte {
252	b := &bytes.Buffer{}
253	zw := zip.NewWriter(b)
254
255	for _, e := range entries {
256		fh := zip.FileHeader{
257			Name: e.name,
258		}
259		fh.SetMode(e.mode)
260
261		w, err := zw.CreateHeader(&fh)
262		if err != nil {
263			panic(err)
264		}
265
266		_, err = w.Write(e.data)
267		if err != nil {
268			panic(err)
269		}
270	}
271
272	err := zw.Close()
273	if err != nil {
274		panic(err)
275	}
276
277	return b.Bytes()
278}
279
280func testZipEntriesToZipReader(entries []testZipEntry) *zip.Reader {
281	b := testZipEntriesToBuf(entries)
282	r := bytes.NewReader(b)
283
284	zr, err := zip.NewReader(r, int64(len(b)))
285	if err != nil {
286		panic(err)
287	}
288
289	return zr
290}
291
292func dumpZip(buf []byte) string {
293	r := bytes.NewReader(buf)
294	zr, err := zip.NewReader(r, int64(len(buf)))
295	if err != nil {
296		panic(err)
297	}
298
299	var ret string
300
301	for _, f := range zr.File {
302		ret += fmt.Sprintf("%v: %v %v %08x\n", f.Name, f.Mode(), f.UncompressedSize64, f.CRC32)
303	}
304
305	return ret
306}
307