Home | History | Annotate | Download | only in merge_zips
      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 main
     16 
     17 import (
     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 
     29 type testZipEntry struct {
     30 	name string
     31 	mode os.FileMode
     32 	data []byte
     33 }
     34 
     35 var (
     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 
     54 func 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 
    251 func 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 
    280 func 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 
    292 func 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