1// Copyright 2017 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 "reflect" 21 "testing" 22 23 "android/soong/third_party/zip" 24) 25 26var testCases = []struct { 27 name string 28 29 inputFiles []string 30 sortGlobs bool 31 sortJava bool 32 args []string 33 excludes []string 34 includes []string 35 uncompresses []string 36 37 outputFiles []string 38 storedFiles []string 39 err error 40}{ 41 { 42 name: "unsupported \\", 43 44 args: []string{"a\\b:b"}, 45 46 err: fmt.Errorf("\\ characters are not currently supported"), 47 }, 48 { // This is modelled after the update package build rules in build/make/core/Makefile 49 name: "filter globs", 50 51 inputFiles: []string{ 52 "RADIO/a", 53 "IMAGES/system.img", 54 "IMAGES/b.txt", 55 "IMAGES/recovery.img", 56 "IMAGES/vendor.img", 57 "OTA/android-info.txt", 58 "OTA/b", 59 }, 60 args: []string{"OTA/android-info.txt:android-info.txt", "IMAGES/*.img:."}, 61 62 outputFiles: []string{ 63 "android-info.txt", 64 "system.img", 65 "recovery.img", 66 "vendor.img", 67 }, 68 }, 69 { 70 name: "sorted filter globs", 71 72 inputFiles: []string{ 73 "RADIO/a", 74 "IMAGES/system.img", 75 "IMAGES/b.txt", 76 "IMAGES/recovery.img", 77 "IMAGES/vendor.img", 78 "OTA/android-info.txt", 79 "OTA/b", 80 }, 81 sortGlobs: true, 82 args: []string{"IMAGES/*.img:.", "OTA/android-info.txt:android-info.txt"}, 83 84 outputFiles: []string{ 85 "recovery.img", 86 "system.img", 87 "vendor.img", 88 "android-info.txt", 89 }, 90 }, 91 { 92 name: "sort all", 93 94 inputFiles: []string{ 95 "RADIO/", 96 "RADIO/a", 97 "IMAGES/", 98 "IMAGES/system.img", 99 "IMAGES/b.txt", 100 "IMAGES/recovery.img", 101 "IMAGES/vendor.img", 102 "OTA/", 103 "OTA/b", 104 "OTA/android-info.txt", 105 }, 106 sortGlobs: true, 107 args: []string{"**/*"}, 108 109 outputFiles: []string{ 110 "IMAGES/b.txt", 111 "IMAGES/recovery.img", 112 "IMAGES/system.img", 113 "IMAGES/vendor.img", 114 "OTA/android-info.txt", 115 "OTA/b", 116 "RADIO/a", 117 }, 118 }, 119 { 120 name: "sort all implicit", 121 122 inputFiles: []string{ 123 "RADIO/", 124 "RADIO/a", 125 "IMAGES/", 126 "IMAGES/system.img", 127 "IMAGES/b.txt", 128 "IMAGES/recovery.img", 129 "IMAGES/vendor.img", 130 "OTA/", 131 "OTA/b", 132 "OTA/android-info.txt", 133 }, 134 sortGlobs: true, 135 args: nil, 136 137 outputFiles: []string{ 138 "IMAGES/", 139 "IMAGES/b.txt", 140 "IMAGES/recovery.img", 141 "IMAGES/system.img", 142 "IMAGES/vendor.img", 143 "OTA/", 144 "OTA/android-info.txt", 145 "OTA/b", 146 "RADIO/", 147 "RADIO/a", 148 }, 149 }, 150 { 151 name: "sort jar", 152 153 inputFiles: []string{ 154 "MANIFEST.MF", 155 "META-INF/MANIFEST.MF", 156 "META-INF/aaa/", 157 "META-INF/aaa/aaa", 158 "META-INF/AAA", 159 "META-INF.txt", 160 "META-INF/", 161 "AAA", 162 "aaa", 163 }, 164 sortJava: true, 165 args: nil, 166 167 outputFiles: []string{ 168 "META-INF/", 169 "META-INF/MANIFEST.MF", 170 "META-INF/AAA", 171 "META-INF/aaa/", 172 "META-INF/aaa/aaa", 173 "AAA", 174 "MANIFEST.MF", 175 "META-INF.txt", 176 "aaa", 177 }, 178 }, 179 { 180 name: "double input", 181 182 inputFiles: []string{ 183 "b", 184 "a", 185 }, 186 args: []string{"a:a2", "**/*"}, 187 188 outputFiles: []string{ 189 "a2", 190 "b", 191 "a", 192 }, 193 }, 194 { 195 name: "multiple matches", 196 197 inputFiles: []string{ 198 "a/a", 199 }, 200 args: []string{"a/a", "a/*"}, 201 202 outputFiles: []string{ 203 "a/a", 204 }, 205 }, 206 { 207 name: "multiple conflicting matches", 208 209 inputFiles: []string{ 210 "a/a", 211 "a/b", 212 }, 213 args: []string{"a/b:a/a", "a/*"}, 214 215 err: fmt.Errorf(`multiple entries for "a/a" with different contents`), 216 }, 217 { 218 name: "excludes", 219 220 inputFiles: []string{ 221 "a/a", 222 "a/b", 223 }, 224 args: nil, 225 excludes: []string{"a/a"}, 226 227 outputFiles: []string{ 228 "a/b", 229 }, 230 }, 231 { 232 name: "excludes with args", 233 234 inputFiles: []string{ 235 "a/a", 236 "a/b", 237 }, 238 args: []string{"a/*"}, 239 excludes: []string{"a/a"}, 240 241 outputFiles: []string{ 242 "a/b", 243 }, 244 }, 245 { 246 name: "excludes over args", 247 248 inputFiles: []string{ 249 "a/a", 250 "a/b", 251 }, 252 args: []string{"a/a"}, 253 excludes: []string{"a/*"}, 254 255 outputFiles: nil, 256 }, 257 { 258 name: "excludes with includes", 259 260 inputFiles: []string{ 261 "a/a", 262 "a/b", 263 }, 264 args: nil, 265 excludes: []string{"a/*"}, 266 includes: []string{"a/b"}, 267 268 outputFiles: []string{"a/b"}, 269 }, 270 { 271 name: "excludes with glob", 272 273 inputFiles: []string{ 274 "a/a", 275 "a/b", 276 }, 277 args: []string{"a/*"}, 278 excludes: []string{"a/*"}, 279 280 outputFiles: nil, 281 }, 282 { 283 name: "uncompress one", 284 285 inputFiles: []string{ 286 "a/a", 287 "a/b", 288 }, 289 uncompresses: []string{"a/a"}, 290 291 outputFiles: []string{ 292 "a/a", 293 "a/b", 294 }, 295 storedFiles: []string{ 296 "a/a", 297 }, 298 }, 299 { 300 name: "uncompress two", 301 302 inputFiles: []string{ 303 "a/a", 304 "a/b", 305 }, 306 uncompresses: []string{"a/a", "a/b"}, 307 308 outputFiles: []string{ 309 "a/a", 310 "a/b", 311 }, 312 storedFiles: []string{ 313 "a/a", 314 "a/b", 315 }, 316 }, 317 { 318 name: "uncompress glob", 319 320 inputFiles: []string{ 321 "a/a", 322 "a/b", 323 "a/c.so", 324 "a/d.so", 325 }, 326 uncompresses: []string{"a/*.so"}, 327 328 outputFiles: []string{ 329 "a/a", 330 "a/b", 331 "a/c.so", 332 "a/d.so", 333 }, 334 storedFiles: []string{ 335 "a/c.so", 336 "a/d.so", 337 }, 338 }, 339 { 340 name: "uncompress rename", 341 342 inputFiles: []string{ 343 "a/a", 344 }, 345 args: []string{"a/a:a/b"}, 346 uncompresses: []string{"a/b"}, 347 348 outputFiles: []string{ 349 "a/b", 350 }, 351 storedFiles: []string{ 352 "a/b", 353 }, 354 }, 355 { 356 name: "recursive glob", 357 358 inputFiles: []string{ 359 "a/a/a", 360 "a/a/b", 361 }, 362 args: []string{"a/**/*:b"}, 363 outputFiles: []string{ 364 "b/a/a", 365 "b/a/b", 366 }, 367 }, 368 { 369 name: "glob", 370 371 inputFiles: []string{ 372 "a/a/a", 373 "a/a/b", 374 "a/b", 375 "a/c", 376 }, 377 args: []string{"a/*:b"}, 378 outputFiles: []string{ 379 "b/b", 380 "b/c", 381 }, 382 }, 383 { 384 name: "top level glob", 385 386 inputFiles: []string{ 387 "a", 388 "b", 389 }, 390 args: []string{"*:b"}, 391 outputFiles: []string{ 392 "b/a", 393 "b/b", 394 }, 395 }, 396 { 397 name: "multilple glob", 398 399 inputFiles: []string{ 400 "a/a/a", 401 "a/a/b", 402 }, 403 args: []string{"a/*/*:b"}, 404 outputFiles: []string{ 405 "b/a/a", 406 "b/a/b", 407 }, 408 }, 409} 410 411func errorString(e error) string { 412 if e == nil { 413 return "" 414 } 415 return e.Error() 416} 417 418func TestZip2Zip(t *testing.T) { 419 for _, testCase := range testCases { 420 t.Run(testCase.name, func(t *testing.T) { 421 inputBuf := &bytes.Buffer{} 422 outputBuf := &bytes.Buffer{} 423 424 inputWriter := zip.NewWriter(inputBuf) 425 for _, file := range testCase.inputFiles { 426 w, err := inputWriter.Create(file) 427 if err != nil { 428 t.Fatal(err) 429 } 430 fmt.Fprintln(w, "test") 431 } 432 inputWriter.Close() 433 inputBytes := inputBuf.Bytes() 434 inputReader, err := zip.NewReader(bytes.NewReader(inputBytes), int64(len(inputBytes))) 435 if err != nil { 436 t.Fatal(err) 437 } 438 439 outputWriter := zip.NewWriter(outputBuf) 440 err = zip2zip(inputReader, outputWriter, testCase.sortGlobs, testCase.sortJava, false, 441 testCase.args, testCase.excludes, testCase.includes, testCase.uncompresses) 442 if errorString(testCase.err) != errorString(err) { 443 t.Fatalf("Unexpected error:\n got: %q\nwant: %q", errorString(err), errorString(testCase.err)) 444 } 445 446 outputWriter.Close() 447 outputBytes := outputBuf.Bytes() 448 outputReader, err := zip.NewReader(bytes.NewReader(outputBytes), int64(len(outputBytes))) 449 if err != nil { 450 t.Fatal(err) 451 } 452 var outputFiles []string 453 var storedFiles []string 454 if len(outputReader.File) > 0 { 455 outputFiles = make([]string, len(outputReader.File)) 456 for i, file := range outputReader.File { 457 outputFiles[i] = file.Name 458 if file.Method == zip.Store { 459 storedFiles = append(storedFiles, file.Name) 460 } 461 } 462 } 463 464 if !reflect.DeepEqual(testCase.outputFiles, outputFiles) { 465 t.Fatalf("Output file list does not match:\nwant: %v\n got: %v", testCase.outputFiles, outputFiles) 466 } 467 if !reflect.DeepEqual(testCase.storedFiles, storedFiles) { 468 t.Fatalf("Stored file list does not match:\nwant: %v\n got: %v", testCase.storedFiles, storedFiles) 469 } 470 }) 471 } 472} 473 474func TestConstantPartOfPattern(t *testing.T) { 475 testCases := []struct{ in, out string }{ 476 { 477 in: "", 478 out: "", 479 }, 480 { 481 in: "a", 482 out: "a", 483 }, 484 { 485 in: "*", 486 out: "", 487 }, 488 { 489 in: "a/a", 490 out: "a/a", 491 }, 492 { 493 in: "a/*", 494 out: "a", 495 }, 496 { 497 in: "a/*/a", 498 out: "a", 499 }, 500 { 501 in: "a/**/*", 502 out: "a", 503 }, 504 } 505 506 for _, test := range testCases { 507 t.Run(test.in, func(t *testing.T) { 508 got := constantPartOfPattern(test.in) 509 if got != test.out { 510 t.Errorf("want %q, got %q", test.out, got) 511 } 512 }) 513 } 514} 515