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