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 54type 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 87func 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 { 124 name: "duplicates identical", 125 in: [][]testZipEntry{ 126 {a}, 127 {a}, 128 }, 129 out: []testZipEntry{a}, 130 }, 131 { 132 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 { 179 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. 190 name: "strip file name", 191 in: [][]testZipEntry{ 192 {a, bDir, ba}, 193 }, 194 out: []testZipEntry{bDir, ba}, 195 196 stripFiles: []string{"a"}, 197 }, 198 { 199 name: "strip files glob", 200 in: [][]testZipEntry{ 201 {a, bDir, ba}, 202 }, 203 out: []testZipEntry{bDir}, 204 205 stripFiles: []string{"**/a"}, 206 }, 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 { 217 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 { 226 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) { 243 inputZips := make([]InputZip, len(test.in)) 244 for i, in := range test.in { 245 inputZips[i] = &testInputZip{name: "in" + strconv.Itoa(i), entries: in} 246 } 247 248 want := testZipEntriesToBuf(test.out) 249 250 out := &bytes.Buffer{} 251 writer := zip.NewWriter(out) 252 253 err := mergeZips(inputZips, writer, "", "", 254 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} 336 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} 393