1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.cts; 17 18 import java.io.BufferedOutputStream; 19 import java.io.File; 20 import java.io.FileNotFoundException; 21 import java.io.FileOutputStream; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.io.OutputStream; 25 import java.security.NoSuchAlgorithmException; 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.Collection; 29 import java.util.Enumeration; 30 import java.util.HashMap; 31 import java.util.List; 32 import java.util.zip.ZipEntry; 33 import java.util.zip.ZipFile; 34 35 import javax.xml.parsers.DocumentBuilderFactory; 36 import javax.xml.parsers.ParserConfigurationException; 37 38 import org.w3c.dom.Document; 39 import org.w3c.dom.Node; 40 import org.w3c.dom.NodeList; 41 import org.xml.sax.SAXException; 42 43 /** 44 * Holds CTS host configuration information, such as: 45 * <ul> 46 * <li> test case repository 47 * <li> test plan repository 48 * <li> test result repository 49 * </ul> 50 */ 51 public class HostConfig extends XMLResourceHandler { 52 public static boolean DEBUG = false; 53 54 public static final String ALL = "all"; 55 56 static final String SIGNATURE_TEST_PACKAGE_NAME = "SignatureTest"; 57 static final String DEFAULT_HOST_CONFIG_FILE_NAME = "host_config.xml"; 58 static final String FILE_SUFFIX_XML = ".xml"; 59 static final String FILE_SUFFIX_APK = ".apk"; 60 static final String FILE_SUFFIX_ZIP = ".zip"; 61 static final String FILE_SUFFIX_JAR = ".jar"; 62 static final String[] CTS_RESULT_RESOURCES = {"cts_result.xsl", "cts_result.css", 63 "logo.gif", "newrule-green.png"}; 64 65 private String mConfigRoot; 66 private CaseRepository mCaseRepos; 67 private ResultRepository mResultRepos; 68 private PlanRepository mPlanRepos; 69 70 // key: app package name 71 // value: TestPackage 72 private HashMap<String, TestPackage> mTestPackageMap; 73 74 enum Ints { 75 // Number of tests executed between reboots. A value <= 0 disables reboots. 76 maxTestCount (200), 77 // Max size [tests] for a package to be run in batch mode 78 maxTestsInBatchMode (0), 79 // Max time [ms] between test status updates for both individual and batch mode. 80 testStatusTimeoutMs (5 * 60 * 1000), 81 // Max time [ms] from start of package in batch mode and the first test status update. 82 batchStartTimeoutMs (30 * 60 * 1000), 83 // Max time [ms] from start of test in individual mode to the first test status update. 84 individualStartTimeoutMs (5 * 60 * 1000), 85 // Timeout [ms] for the signature check 86 signatureTestTimeoutMs (10 * 60 * 1000), 87 // Timeout [ms] for package installations 88 packageInstallTimeoutMs (2 * 60 * 1000), 89 // Time to wait [ms] after a package installation or removal 90 postInstallWaitMs (30 * 1000); 91 92 private int value; 93 94 Ints(int value) { 95 this.value = value; 96 } 97 98 int value() { 99 return value; 100 } 101 102 void setValue(int value) { 103 this.value = value; 104 } 105 } 106 107 private final static HostConfig sInstance = new HostConfig(); 108 109 private HostConfig() { 110 mTestPackageMap = new HashMap<String, TestPackage>(); 111 } 112 113 public static HostConfig getInstance() { 114 return sInstance; 115 } 116 117 /** 118 * Returns the max number of tests to run between reboots. A value of 0 or smaller indicates 119 * that reboots should not be used. 120 */ 121 public static int getMaxTestCount() { 122 return Ints.maxTestCount.value(); 123 } 124 125 /** 126 * Load configuration. 127 * 128 * @param configPath The configuration path. 129 * @return If succeed in loading, return true; else, return false. 130 */ 131 public boolean load(String configPath) throws SAXException, IOException, 132 ParserConfigurationException { 133 134 String fileName = null; 135 String[] subDirs = configPath.split("\\" + File.separator); 136 for (String d : subDirs) { 137 if (d.contains(FILE_SUFFIX_XML)) { 138 fileName = d; 139 } 140 } 141 142 String configFile = null; 143 if (fileName == null) { 144 //remove the possible trailing "/" of the path 145 if (File.separatorChar == configPath.charAt(configPath.length() - 1)) { 146 configPath = configPath.substring(0, configPath.length() - 1); 147 } 148 mConfigRoot = configPath; 149 fileName = DEFAULT_HOST_CONFIG_FILE_NAME; 150 } else { 151 mConfigRoot = configPath.substring(0, configPath.length() - fileName.length() - 1); 152 } 153 configFile = mConfigRoot + File.separator + fileName; 154 155 Document doc = DocumentBuilderFactory.newInstance() 156 .newDocumentBuilder().parse(new File(configFile)); 157 158 String repositoryRoot = getStringAttributeValue(doc 159 .getElementsByTagName("Repository").item(0), "root"); 160 if ((null == repositoryRoot) || (repositoryRoot.length() == 0)) { 161 repositoryRoot = mConfigRoot; 162 } 163 164 String caseCfg = getStringAttributeValue(doc, "TestCase", "path", fileName); 165 String planCfg = getStringAttributeValue(doc, "TestPlan", "path", fileName); 166 String resCfg = getStringAttributeValue(doc, "TestResult", "path", fileName); 167 if ((caseCfg == null) || (planCfg == null) || (resCfg == null)) { 168 return false; 169 } 170 171 getConfigValues(doc); 172 173 String caseRoot = repositoryRoot + File.separator + caseCfg; 174 String planRoot = repositoryRoot + File.separator + planCfg; 175 String resRoot = repositoryRoot + File.separator + resCfg; 176 177 boolean validCase = true; 178 if (!validateDirectory(caseRoot)) { 179 validCase = new File(caseRoot).mkdirs(); 180 } 181 boolean validRes = true; 182 if (!validateDirectory(resRoot)) { 183 validRes = new File(resRoot).mkdirs(); 184 } 185 if (validRes) { 186 extractResultResources(resRoot); 187 } 188 boolean validPlan = true; 189 if (!validateDirectory(planRoot)) { 190 validPlan = new File(planRoot).mkdirs(); 191 } 192 193 mCaseRepos = new CaseRepository(caseRoot); 194 mResultRepos = new ResultRepository(resRoot); 195 mPlanRepos = new PlanRepository(planRoot); 196 197 return validCase && validRes && validPlan; 198 } 199 200 /** 201 * Extract the result resources into the specified directory. 202 * 203 * @param resRoot the directory to extract the resources into. 204 */ 205 public void extractResultResources(String resRoot) { 206 for (String res: CTS_RESULT_RESOURCES) { 207 extractResource(res, resRoot); 208 } 209 } 210 211 /** 212 * Get the test packages. 213 * 214 * @return The test packages. 215 */ 216 public Collection<TestPackage> getTestPackages() { 217 return mTestPackageMap.values(); 218 } 219 220 /** 221 * Get the test package by the JAVA package name of the test package. 222 * 223 * @param packageName The JAVA package name. 224 * @return The test package. 225 */ 226 public TestPackage getTestPackage(final String packageName) { 227 return mTestPackageMap.get(packageName); 228 } 229 230 /** 231 * Load repositories. 232 */ 233 public void loadRepositories() throws NoSuchAlgorithmException { 234 loadTestPackages(); 235 loadTestResults(); 236 } 237 238 /** 239 * Load test results to create session accordingly. 240 */ 241 private void loadTestResults() { 242 getResultRepository().loadTestResults(); 243 } 244 245 /** 246 * Load all of the test packages. 247 */ 248 public void loadTestPackages() throws NoSuchAlgorithmException { 249 if (mTestPackageMap.size() == 0) { 250 mCaseRepos.loadTestPackages(); 251 } 252 } 253 254 /** 255 * Remove all of the test packages. 256 */ 257 public void removeTestPacakges() { 258 mTestPackageMap.clear(); 259 } 260 261 /** 262 * Get the package binary name. 263 * 264 * @param appPackageName The JAVA package name. 265 * @return The binary name of the package. 266 */ 267 public String getPackageBinaryName(String appPackageName) { 268 269 for (TestPackage pkg : mTestPackageMap.values()) { 270 if (appPackageName.equals(pkg.getAppPackageName())) { 271 return pkg.getAppBinaryName(); 272 } 273 } 274 275 return null; 276 } 277 278 /** 279 * Get the root directory of configuration. 280 * 281 * @return The root directory of configuration. 282 */ 283 public String getConfigRoot() { 284 return mConfigRoot; 285 } 286 287 /** 288 * Get string attribute value. 289 * 290 * @param doc The document. 291 * @param tagName The tag name. 292 * @param attrName The attribute name. 293 * @param fileName The file name. 294 * @return The attribute value. 295 */ 296 private String getStringAttributeValue(final Document doc, 297 final String tagName, final String attrName, final String fileName) { 298 299 String cfgStr = null; 300 try { 301 cfgStr = getStringAttributeValue(doc 302 .getElementsByTagName(tagName).item(0), attrName); 303 if ((null == cfgStr) || (cfgStr.length() == 0)) { 304 Log.e("Configure error (in " + fileName 305 + "), pls make sure <" + tagName + ">'s attribute <" 306 + attrName + ">'s value is correctly set.", null); 307 return null; 308 } 309 } catch (Exception e) { 310 Log.e("Configure error (in " + fileName 311 + "), pls make sure <" + tagName 312 + ">'s value is correctly set.", null); 313 return null; 314 } 315 316 return cfgStr; 317 } 318 319 /** 320 * Load configuration values from config file. 321 * 322 * @param doc The document from which to load the values. 323 */ 324 private void getConfigValues(final Document doc) { 325 NodeList intValues = doc.getElementsByTagName("IntValue"); 326 for (int i = 0; i < intValues.getLength(); i++) { 327 Node n = intValues.item(i); 328 String name = getStringAttributeValue(n, "name"); 329 String value = getStringAttributeValue(n, "value"); 330 try { 331 Integer v = Integer.parseInt(value); 332 Ints.valueOf(name).setValue(v); 333 } catch (NumberFormatException e) { 334 Log.e("Configuration error. Illegal value for " + name, e); 335 } catch (IllegalArgumentException e) { 336 Log.e("Unknown configuration value " + name, e); 337 } 338 } 339 } 340 341 /** 342 * Validate the directory. 343 * 344 * @param path The path to be validated. 345 * @return If valid directory, return true; else, return false. 346 */ 347 private boolean validateDirectory(final String path) { 348 349 File pathFile = new File(path); 350 if ((null == pathFile) || (pathFile.exists() == false) 351 || (pathFile.isDirectory() == false)) { 352 return false; 353 } 354 355 return true; 356 } 357 358 /** 359 * Extract a resource into the given destination directory. This is used 360 * for resource files such as images and CSS. 361 * 362 * @param name The name of the resource. 363 * @param dest The directory the resource should be extracted to. 364 * @return true, if successful, or the file already existed. 365 */ 366 private boolean extractResource(String name, String dest) { 367 File file = new File(dest, name); 368 if (!file.exists()) { 369 // do not extract again if the file is already there 370 InputStream in = getClass().getResourceAsStream(File.separator + name); 371 if (in != null) { 372 try { 373 FileOutputStream fout = new FileOutputStream(file); 374 byte[] data = new byte[512]; 375 int len = in.read(data); 376 while (len > 0) { 377 fout.write(data, 0, len); 378 len = in.read(data); 379 } 380 fout.flush(); 381 fout.close(); 382 in.close(); 383 } catch (FileNotFoundException e) { 384 return false; 385 } catch (IOException e) { 386 return false; 387 } 388 } 389 } 390 return true; 391 } 392 393 /** 394 * Get the case repository. 395 * 396 * @return The case repository. 397 */ 398 public CaseRepository getCaseRepository() { 399 return mCaseRepos; 400 } 401 402 /** 403 * Get the plan repository. 404 * 405 * @return The plan repository. 406 */ 407 public PlanRepository getPlanRepository() { 408 return mPlanRepos; 409 } 410 411 /** 412 * Get the result repository. 413 * 414 * @return The result repository. 415 */ 416 public ResultRepository getResultRepository() { 417 return mResultRepos; 418 } 419 420 /** 421 * Storing the root information of some repository. 422 * 423 */ 424 class Repository { 425 protected String mRoot; 426 427 Repository(String root) { 428 mRoot = root; 429 } 430 431 /** 432 * Get the root of the repository. 433 * 434 * @return The root of the repository. 435 */ 436 public String getRoot() { 437 return mRoot; 438 } 439 440 /** 441 * Check if the specified file is a valid XML file. 442 * 443 * @param f The file to be valid. 444 * @return If valid XML file, return true; else, return false. 445 */ 446 public boolean isValidXmlFile(File f) { 447 if (f.getPath().endsWith(FILE_SUFFIX_XML)) { 448 return true; 449 } 450 451 return false; 452 } 453 } 454 455 /** 456 * Storing the information of result repository. 457 */ 458 class ResultRepository extends Repository { 459 460 ResultRepository(String root) { 461 super(root); 462 } 463 464 /** 465 * Load test results to create session accordingly. 466 */ 467 public void loadTestResults() { 468 469 for (File f : new File(mRoot).listFiles()) { 470 if (f.isDirectory()) { 471 String pathName = mRoot + File.separator + f.getName() 472 + File.separator + TestSessionLog.CTS_RESULT_FILE_NAME; 473 if (HostUtils.isFileExist(pathName)) { 474 try { 475 TestSessionLog log = 476 TestSessionLogBuilder.getInstance().build(pathName); 477 TestSession ts = TestSessionBuilder.getInstance().build(log); 478 if (ts != null) { 479 TestHost.getInstance().addSession(ts); 480 } 481 } catch (Exception e) { 482 Log.e("Error importing existing result from " + pathName, e); 483 } 484 } 485 } 486 } 487 } 488 } 489 490 /** 491 * Storing the information of case repository. 492 */ 493 class CaseRepository extends Repository { 494 CaseRepository(String root) { 495 super(root); 496 } 497 498 /** 499 * Get package names. 500 * 501 * @return The JAVA package names. 502 */ 503 public ArrayList<String> getPackageNames() { 504 ArrayList<String> packageNames = new ArrayList<String>(); 505 for (TestPackage pkg : mTestPackageMap.values()) { 506 String binaryName = pkg.getAppBinaryName(); 507 if (binaryName.equals(SIGNATURE_TEST_PACKAGE_NAME)) { 508 packageNames.add(0, binaryName); 509 } else { 510 packageNames.add(pkg.getAppPackageName()); 511 } 512 } 513 514 return packageNames; 515 } 516 517 /** 518 * Get package binary names. 519 * 520 * @return The package binary names. 521 */ 522 public ArrayList<String> getPackageBinaryNames() { 523 ArrayList<String> pkgBinaryNames = new ArrayList<String>(); 524 for (TestPackage pkg : mTestPackageMap.values()) { 525 String pkgBinaryName = pkg.getAppBinaryName(); 526 if (pkgBinaryName.equals(SIGNATURE_TEST_PACKAGE_NAME)) { 527 pkgBinaryNames.add(0, pkgBinaryName); 528 } else { 529 pkgBinaryNames.add(pkg.getAppBinaryName()); 530 } 531 } 532 533 return pkgBinaryNames; 534 } 535 536 /** 537 * Load package XML file names. 538 * 539 * @return The package XML file names. 540 */ 541 public List<String> loadPackageXmlFileNames() { 542 ArrayList<String> packageXmlFileNames = new ArrayList<String>(); 543 544 for (File f : new File(mRoot).listFiles()) { 545 if (isValidXmlFile(f)) { 546 String fileName = f.getName(); 547 String name = fileName.substring(0, fileName.lastIndexOf(".")); 548 packageXmlFileNames.add(name); 549 } 550 } 551 552 return packageXmlFileNames; 553 } 554 555 /** 556 * Load test packages. 557 */ 558 public void loadTestPackages() throws NoSuchAlgorithmException { 559 List<String> pkgXmlFileNameList = loadPackageXmlFileNames(); 560 for (String pkgXmlFileName : pkgXmlFileNameList) { 561 String xmlPath = getRoot() + File.separator 562 + pkgXmlFileName + FILE_SUFFIX_XML; 563 TestPackage pkg = loadPackage(xmlPath); 564 if (isValidPackage(pkg)) { 565 mTestPackageMap.put(pkg.getAppPackageName(), pkg); 566 } 567 } 568 } 569 570 /** 571 * Get package binary name. 572 * 573 * @param packagePath The package path. 574 * @return The package binary name. 575 */ 576 private String getPackageBinaryName(String packagePath) { 577 return packagePath.substring(packagePath.lastIndexOf(File.separator) + 1, 578 packagePath.lastIndexOf(".")); 579 } 580 581 /** 582 * Check if the specified package is a valid package. 583 * 584 * @param pkg The specified package to be checked. 585 * @return If valid package, return true; else, return false. 586 */ 587 private boolean isValidPackage(TestPackage pkg) { 588 if (pkg == null) { 589 return false; 590 } 591 592 String pkgFileName = pkg.getAppBinaryName(); 593 String apkFilePath = mRoot + File.separator + pkgFileName + FILE_SUFFIX_APK; 594 String xmlFilePath = mRoot + File.separator + pkgFileName + FILE_SUFFIX_XML; 595 File xmlFile = new File(xmlFilePath); 596 if (pkg.isHostSideOnly()) { 597 if (xmlFile.exists() && xmlFile.isFile()) { 598 return true; 599 } 600 } else { 601 File apkFile = new File(apkFilePath); 602 if (xmlFile.exists() && xmlFile.isFile() 603 && apkFile.exists() && apkFile.isFile()) { 604 return true; 605 } 606 } 607 608 return false; 609 } 610 611 /** 612 * Add package to case repository. 613 * 614 * @param packagePath The package to be added. 615 * @return If valid package and succeed in add it, return true; else, return false. 616 */ 617 public boolean addPackage(String packagePath) throws FileNotFoundException, 618 IOException, NoSuchAlgorithmException { 619 ZipFile zipFile = new ZipFile(packagePath); 620 Enumeration<? extends ZipEntry> entries = zipFile.entries(); 621 622 ArrayList<String> filePathList = new ArrayList<String>(); 623 String xmlFilePath = null; 624 while (entries.hasMoreElements()) { 625 ZipEntry entry = entries.nextElement(); 626 627 String name = entry.getName(); 628 if (name.endsWith(FILE_SUFFIX_APK) 629 || name.endsWith(FILE_SUFFIX_XML) 630 || name.endsWith(FILE_SUFFIX_JAR)) { 631 int index = name.lastIndexOf(File.separator); 632 String fileName = name; 633 if (index != -1) { 634 fileName = name.substring(index + 1); 635 } 636 String filePath = mRoot + File.separator + fileName; 637 writeToFile(zipFile.getInputStream(entry), filePath); 638 filePathList.add(filePath); 639 if (name.endsWith(FILE_SUFFIX_XML)) { 640 xmlFilePath = filePath; 641 } 642 } 643 } 644 645 String packageName = getPackageBinaryName(packagePath); 646 PackageZipFileValidator zipValidator = new PackageZipFileValidator(); 647 if (zipValidator.validate(filePathList, packageName, xmlFilePath) == false) { 648 for (String filePath : filePathList) { 649 deleteFile(filePath); 650 } 651 return false; 652 } 653 654 TestPackage pkg = loadPackage(xmlFilePath); 655 if (pkg != null) { 656 mTestPackageMap.put(pkg.getAppPackageName(), pkg); 657 } 658 659 return true; 660 } 661 662 /** 663 * Load the package from the package description XML file. 664 * 665 * @param xmlFileName The package description XML file. 666 * @return The TestPackage. 667 */ 668 private TestPackage loadPackage(String xmlFileName) throws NoSuchAlgorithmException { 669 if ((xmlFileName == null) || (xmlFileName.length() == 0)) { 670 return null; 671 } 672 673 File xmlFile = new File(xmlFileName); 674 TestSessionBuilder sessionBuilder; 675 TestPackage pkg = null; 676 try { 677 sessionBuilder = TestSessionBuilder.getInstance(); 678 pkg = sessionBuilder.loadPackage(xmlFile, null); 679 } catch (ParserConfigurationException e) { 680 } catch (SAXException e) { 681 } catch (IOException e) { 682 } 683 return pkg; 684 } 685 686 /** 687 * check if the packagePath is valid against the case repository 688 * 689 * @param packagePath the path to be checked 690 * @return if the path isn't suffixed with .zip, return false; 691 * if the package name exists in case repository, return false; 692 * for other conditions, return true; 693 */ 694 public boolean isValidPackageName(String packagePath) { 695 if (!packagePath.endsWith(FILE_SUFFIX_ZIP)) { 696 Log.e("Package error: package name " + packagePath + " is not a zip file.", null); 697 return false; 698 } 699 700 String fileName = packagePath.substring(packagePath.lastIndexOf(File.separator) + 1, 701 packagePath.length() - FILE_SUFFIX_ZIP.length()); 702 703 String path = mRoot + File.separator + fileName; 704 if (HostUtils.isFileExist(path + FILE_SUFFIX_APK) 705 || HostUtils.isFileExist(path + FILE_SUFFIX_XML)) { 706 Log.e("Package error: package name " + fileName + " exists already.", null); 707 return false; 708 } 709 710 return true; 711 } 712 713 /** 714 * Validate zipped package file against package logic 715 */ 716 class PackageZipFileValidator { 717 /** 718 * validate the package content to see if it contains enough data 719 ** 720 * @param filePathList The file path list contained in the package zip file. 721 * @param packageName The package name. 722 * @param xmlFilePath The description XML file path. 723 * @return If valid, return true; else, return false. 724 */ 725 public boolean validate(ArrayList<String> filePathList, String packageName, 726 String xmlFilePath) throws NoSuchAlgorithmException { 727 if (xmlFilePath == null) { 728 Log.e("Package error: package doesn't contain XML file: " 729 + packageName + FILE_SUFFIX_XML, null); 730 return false; 731 } else { 732 TestPackage pkg = loadPackage(xmlFilePath); 733 if (pkg == null) { 734 Log.e("Package error: the description XML file contained in : " 735 + packageName + FILE_SUFFIX_APK + " is invalid.", null); 736 return false; 737 } else { 738 if (!validateTargetApk(filePathList, pkg.getTargetBinaryName())) { 739 return false; 740 } 741 742 if (!validateHostControllerJar(filePathList, pkg.getJarPath())) { 743 return false; 744 } 745 746 String apkFilePath = mRoot + File.separator 747 + packageName + FILE_SUFFIX_APK; 748 if (!filePathList.contains(apkFilePath)) { 749 Log.e("Package error: package doesn't contain APK file: " 750 + packageName + FILE_SUFFIX_APK, null); 751 return false; 752 } 753 } 754 } 755 756 return true; 757 } 758 759 /** 760 * Validate host controller jar file described in the package description XML file. 761 * 762 * @param filePathList The files contained in the zipped package file. 763 * @param hostControllerJarPath The host controller jar file path. 764 * @return If the host controller jar file contained in the zipped package, 765 * return true; else, return false. 766 */ 767 private boolean validateHostControllerJar(ArrayList<String> filePathList, 768 String hostControllerJarPath) { 769 if ((hostControllerJarPath != null) && (hostControllerJarPath.length() != 0)) { 770 String targetFilePath = 771 mRoot + File.separator + hostControllerJarPath + FILE_SUFFIX_JAR; 772 if (filePathList.contains(targetFilePath)) { 773 return true; 774 } 775 } else { 776 return true; 777 } 778 779 //String jarFileName = getPackageName(hostControllerJarPath); 780 Log.e("Package error: host controler jar file " 781 + hostControllerJarPath + FILE_SUFFIX_JAR 782 + " is not contained in the package zip file.", null); 783 return false; 784 } 785 786 /** 787 * Validate target APK file described in the package description XML file. 788 * 789 * @param filePathList The files contained in the zipped package file. 790 * @param targetName The target APK name. 791 * @return If the target APK file contained in the zipped package file, return true; 792 * else, return false. 793 */ 794 private boolean validateTargetApk(ArrayList<String> filePathList, String targetName) { 795 if ((targetName != null) && (targetName.length() != 0)) { 796 String targetFileName = mRoot + File.separator + targetName + FILE_SUFFIX_APK; 797 if (filePathList.contains(targetFileName)) { 798 return true; 799 } 800 } else { 801 return true; 802 } 803 804 Log.e("Package error: target file " + targetName + FILE_SUFFIX_APK 805 + " is not contained in the package zip file.", null); 806 return false; 807 } 808 } 809 810 /** 811 * Write the input stream to file. 812 * 813 * @param in The input stream. 814 * @param path The file to write to. 815 */ 816 private void writeToFile(InputStream in, String path) throws IOException { 817 OutputStream out = new BufferedOutputStream(new FileOutputStream(path)); 818 byte[] buffer = new byte[1024]; 819 int len; 820 821 while ((len = in.read(buffer)) >= 0) { 822 out.write(buffer, 0, len); 823 } 824 825 in.close(); 826 out.close(); 827 } 828 829 /** 830 * Remove packages from case repository. 831 * 832 * @param packageName Package to be removed. 833 */ 834 public void removePackages(String packageName) { 835 if ((packageName == null) || (packageName.length() == 0)) { 836 return; 837 } 838 839 if (packageName.equals(ALL)) { 840 ArrayList<String> packageNames = getCaseRepository().getPackageNames(); 841 for (String pkgName : packageNames) { 842 removePackage(pkgName); 843 } 844 } else { 845 if (!getPackageNames().contains(packageName)) { 846 Log.e("Package " + packageName + " doesn't exist in repository!", null); 847 return; 848 } 849 removePackage(packageName); 850 } 851 } 852 853 /** 854 * Remove the specified package. 855 * 856 * @param packageName The package name. 857 */ 858 private void removePackage(String packageName) { 859 TestPackage pkg = getTestPackage(packageName); 860 if (pkg != null) { 861 ArrayList<String> targetBinaryNames = getTargetBinaryNames(); 862 String targetBinaryName = pkg.getTargetBinaryName(); 863 if ((targetBinaryName != null) && (targetBinaryName.length() != 0) 864 && (getReferenceCount(targetBinaryNames, targetBinaryName) == 1)) { 865 String targetBinaryFileName = mRoot + File.separator + targetBinaryName 866 + FILE_SUFFIX_APK; 867 deleteFile(targetBinaryFileName); 868 } 869 870 ArrayList<String> hostControllers = getHostControllers(); 871 String hostControllerPath = pkg.getJarPath(); 872 if ((hostControllerPath != null) && (hostControllerPath.length() != 0) 873 && (getReferenceCount(hostControllers, hostControllerPath) == 1)) { 874 String jarFilePath = mRoot + File.separator 875 + hostControllerPath + FILE_SUFFIX_JAR; 876 deleteFile(jarFilePath); 877 } 878 } 879 880 String packageBinaryName = pkg.getAppBinaryName(); 881 mTestPackageMap.remove(pkg.getAppPackageName()); 882 883 String apkPath = mRoot + File.separator + packageBinaryName + FILE_SUFFIX_APK; 884 String xmlPath = mRoot + File.separator + packageBinaryName + FILE_SUFFIX_XML; 885 deleteFile(apkPath); 886 deleteFile(xmlPath); 887 } 888 889 /** 890 * Get the reference count of the specific value against the value list. 891 * 892 * @param list The value list to be checked against. 893 * @param value The value to be checked. 894 * @return The reference count. 895 */ 896 private int getReferenceCount(ArrayList<String> list, String value) { 897 if ((list == null) || (list.size() == 0) || (value == null)) { 898 return 0; 899 } 900 901 int count = 0; 902 for (String str : list) { 903 if (value.equals(str)) { 904 count ++; 905 } 906 } 907 908 return count; 909 } 910 911 /** 912 * Get the target binary names contained with the test package description XML files. 913 * 914 * @return The target binary names. 915 */ 916 private ArrayList<String> getTargetBinaryNames() { 917 ArrayList<String> targetBinaryNames = new ArrayList<String>(); 918 for (TestPackage pkg : mTestPackageMap.values()) { 919 targetBinaryNames.add(pkg.getTargetBinaryName()); 920 } 921 return targetBinaryNames; 922 } 923 924 /** 925 * Get the host controllers contained with the test package description XML files. 926 * 927 * @return The host controllers. 928 */ 929 private ArrayList<String> getHostControllers() { 930 ArrayList<String> hostControllers = new ArrayList<String>(); 931 for (TestPackage pkg : mTestPackageMap.values()) { 932 hostControllers.add(pkg.getJarPath()); 933 } 934 return hostControllers; 935 } 936 937 /** 938 * Delete the specific file. 939 * 940 * @param filepath The file to be deleted. 941 */ 942 private void deleteFile(String filepath) { 943 File file = new File(filepath); 944 if (file.exists() && file.isFile()) { 945 file.delete(); 946 } 947 } 948 949 /** 950 * Get package's APK file path via the package name. 951 * 952 * @param packageName The package name. 953 * @return The package's APK file path. 954 */ 955 public String getApkPath(String packageName) { 956 return mRoot + File.separator + packageName + FILE_SUFFIX_APK; 957 } 958 959 /** 960 * Get package's XML file path via the package name. 961 * @param packageName The package name. 962 * @return The package's XML file path. 963 */ 964 public String getXmlPath(String packageName) { 965 return mRoot + File.separator + packageName + FILE_SUFFIX_XML; 966 } 967 968 /** 969 * List available package and suite. 970 * 971 * @param expectPackage expected package name 972 * @return list which contains available packages, suites and cases. 973 */ 974 @SuppressWarnings("unchecked") 975 public List<ArrayList<String>> listAvailablePackage(String expectPackage) { 976 ArrayList<String> packageList = new ArrayList<String>(); 977 ArrayList<String> suiteList = new ArrayList<String>(); 978 ArrayList<String> caseList = new ArrayList<String>(); 979 ArrayList<String> testList = new ArrayList<String>(); 980 981 for (TestPackage testPackage : mTestPackageMap.values()) { 982 String appPackageName = testPackage.getAppPackageName(); 983 if (expectPackage.equals(appPackageName)) { 984 testPackage.getTestSuiteNames(appPackageName, suiteList, caseList); 985 } else if (appPackageName.startsWith(expectPackage)) { 986 packageList.add(appPackageName); 987 } else { 988 if (expectPackage.indexOf(Test.METHOD_SEPARATOR) == -1) { 989 testPackage.getTestCaseNames(expectPackage, caseList, testList); 990 } else { 991 testPackage.getTestNames(expectPackage, testList); 992 } 993 } 994 } 995 996 return Arrays.asList(packageList, suiteList, caseList, testList); 997 } 998 } 999 1000 /** 1001 * Storing information of test plans. 1002 * 1003 */ 1004 class PlanRepository extends Repository { 1005 1006 PlanRepository(String root) { 1007 super(root); 1008 } 1009 1010 /** 1011 * Get the path of the specified plan. 1012 * 1013 * @param name The plan name. 1014 * @return The plan path. 1015 */ 1016 public String getPlanPath(String name) { 1017 if (mRoot == null) { 1018 Log.e("Repository uninitialized!", null); 1019 return null; 1020 } 1021 1022 return mRoot + File.separator + name + FILE_SUFFIX_XML; 1023 } 1024 1025 /** 1026 * Get all plan names in the plan repository. 1027 * @return Plan names. 1028 */ 1029 public ArrayList<String> getAllPlanNames() { 1030 ArrayList<String> plans = new ArrayList<String>(); 1031 1032 if (mRoot == null) { 1033 Log.e("Not specify repository, please check your cts config", 1034 null); 1035 return plans; 1036 } 1037 1038 File planRepository = new File(mRoot); 1039 if (!planRepository.exists()) { 1040 Log.e("Plan Repository doesn't exist: " + mRoot, null); 1041 return null; 1042 } 1043 1044 for (File f : planRepository.listFiles()) { 1045 String name = f.getName(); 1046 1047 if (name.endsWith(FILE_SUFFIX_XML)) { 1048 plans.add(name.substring(0, name.length() - FILE_SUFFIX_XML.length())); 1049 } 1050 } 1051 1052 return plans; 1053 } 1054 } 1055 } 1056