| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.cts; |
| |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * Holds CTS host configuration information, such as: |
| * <ul> |
| * <li> test case repository |
| * <li> test plan repository |
| * <li> test result repository |
| * </ul> |
| */ |
| public class HostConfig extends XMLResourceHandler { |
| public static boolean DEBUG = false; |
| |
| public static final String ALL = "all"; |
| |
| static final String SIGNATURE_TEST_PACKAGE_NAME = "SignatureTest"; |
| static final String DEFAULT_HOST_CONFIG_FILE_NAME = "host_config.xml"; |
| static final String FILE_SUFFIX_XML = ".xml"; |
| static final String FILE_SUFFIX_APK = ".apk"; |
| static final String FILE_SUFFIX_ZIP = ".zip"; |
| static final String FILE_SUFFIX_JAR = ".jar"; |
| static final String[] CTS_RESULT_RESOURCES = {"cts_result.xsl", "cts_result.css", |
| "logo.gif", "newrule-green.png"}; |
| |
| private String mConfigRoot; |
| private String mLogRoot; |
| private CaseRepository mCaseRepos; |
| private ResultRepository mResultRepos; |
| private PlanRepository mPlanRepos; |
| |
| // key: app package name |
| // value: TestPackage |
| private HashMap<String, TestPackage> mTestPackageMap; |
| |
| enum Ints { |
| // Number of tests executed between reboots. A value <= 0 disables reboots. |
| maxTestCount (200), |
| // Max size [tests] for a package to be run in batch mode |
| maxTestsInBatchMode (0), |
| // Max time [ms] between test status updates for both individual and batch mode. |
| testStatusTimeoutMs (5 * 60 * 1000), |
| // Max time [ms] from start of package in batch mode and the first test status update. |
| batchStartTimeoutMs (30 * 60 * 1000), |
| // Max time [ms] from start of test in individual mode to the first test status update. |
| individualStartTimeoutMs (5 * 60 * 1000), |
| // Timeout [ms] for the signature check |
| signatureTestTimeoutMs (10 * 60 * 1000), |
| // Timeout [ms] for package installations |
| packageInstallTimeoutMs (2 * 60 * 1000), |
| // Time to wait [ms] after a package installation or removal |
| postInstallWaitMs (30 * 1000); |
| |
| private int value; |
| |
| Ints(int value) { |
| this.value = value; |
| } |
| |
| int value() { |
| return value; |
| } |
| |
| void setValue(int value) { |
| this.value = value; |
| } |
| } |
| |
| private final static HostConfig sInstance = new HostConfig(); |
| |
| private HostConfig() { |
| mTestPackageMap = new HashMap<String, TestPackage>(); |
| } |
| |
| public static HostConfig getInstance() { |
| return sInstance; |
| } |
| |
| /** |
| * Returns the max number of tests to run between reboots. A value of 0 or smaller indicates |
| * that reboots should not be used. |
| */ |
| public static int getMaxTestCount() { |
| return Ints.maxTestCount.value(); |
| } |
| |
| /** |
| * Load configuration. |
| * |
| * @param configPath The configuration path. |
| * @return If succeed in loading, return true; else, return false. |
| */ |
| public boolean load(String configPath) throws SAXException, IOException, |
| ParserConfigurationException { |
| |
| String fileName = null; |
| String[] subDirs = configPath.split("\\" + File.separator); |
| for (String d : subDirs) { |
| if (d.contains(FILE_SUFFIX_XML)) { |
| fileName = d; |
| } |
| } |
| |
| String configFile = null; |
| if (fileName == null) { |
| //remove the possible trailing "/" of the path |
| if (File.separatorChar == configPath.charAt(configPath.length() - 1)) { |
| configPath = configPath.substring(0, configPath.length() - 1); |
| } |
| mConfigRoot = configPath; |
| fileName = DEFAULT_HOST_CONFIG_FILE_NAME; |
| } else { |
| mConfigRoot = configPath.substring(0, configPath.length() - fileName.length() - 1); |
| } |
| configFile = mConfigRoot + File.separator + fileName; |
| |
| Document doc = DocumentBuilderFactory.newInstance() |
| .newDocumentBuilder().parse(new File(configFile)); |
| |
| String repositoryRoot = getStringAttributeValue(doc |
| .getElementsByTagName("Repository").item(0), "root"); |
| if ((null == repositoryRoot) || (repositoryRoot.length() == 0)) { |
| repositoryRoot = mConfigRoot; |
| } |
| |
| String caseCfg = getStringAttributeValue(doc, "TestCase", "path", fileName); |
| String planCfg = getStringAttributeValue(doc, "TestPlan", "path", fileName); |
| String resCfg = getStringAttributeValue(doc, "TestResult", "path", fileName); |
| if ((caseCfg == null) || (planCfg == null) || (resCfg == null)) { |
| return false; |
| } |
| |
| getConfigValues(doc); |
| |
| String caseRoot = repositoryRoot + File.separator + caseCfg; |
| String planRoot = repositoryRoot + File.separator + planCfg; |
| String resRoot = repositoryRoot + File.separator + resCfg; |
| |
| String logCfg = getStringAttributeValueOpt(doc, "TestLog", "path", fileName); |
| if (null == logCfg) { |
| mLogRoot = mConfigRoot; |
| } else { |
| mLogRoot = repositoryRoot + File.separator + logCfg; |
| } |
| |
| boolean validCase = true; |
| if (!validateDirectory(caseRoot)) { |
| validCase = new File(caseRoot).mkdirs(); |
| } |
| boolean validRes = true; |
| if (!validateDirectory(resRoot)) { |
| validRes = new File(resRoot).mkdirs(); |
| } |
| if (validRes) { |
| extractResultResources(resRoot); |
| } |
| boolean validPlan = true; |
| if (!validateDirectory(planRoot)) { |
| validPlan = new File(planRoot).mkdirs(); |
| } |
| boolean validLog = true; |
| if (!validateDirectory(mLogRoot)) { |
| validLog = new File(mLogRoot).mkdirs(); |
| } |
| |
| mCaseRepos = new CaseRepository(caseRoot); |
| mResultRepos = new ResultRepository(resRoot); |
| mPlanRepos = new PlanRepository(planRoot); |
| |
| return validCase && validRes && validPlan && validLog; |
| } |
| |
| /** |
| * Extract the result resources into the specified directory. |
| * |
| * @param resRoot the directory to extract the resources into. |
| */ |
| public void extractResultResources(String resRoot) { |
| for (String res: CTS_RESULT_RESOURCES) { |
| extractResource(res, resRoot); |
| } |
| } |
| |
| /** |
| * Get the test packages. |
| * |
| * @return The test packages. |
| */ |
| public Collection<TestPackage> getTestPackages() { |
| return mTestPackageMap.values(); |
| } |
| |
| /** |
| * Get the test package by the JAVA package name of the test package. |
| * |
| * @param packageName The JAVA package name. |
| * @return The test package. |
| */ |
| public TestPackage getTestPackage(final String packageName) { |
| return mTestPackageMap.get(packageName); |
| } |
| |
| /** |
| * Load repositories. |
| */ |
| public void loadRepositories() throws NoSuchAlgorithmException { |
| loadTestPackages(); |
| loadTestResults(); |
| } |
| |
| /** |
| * Load test results to create session accordingly. |
| */ |
| private void loadTestResults() { |
| getResultRepository().loadTestResults(); |
| } |
| |
| /** |
| * Load all of the test packages. |
| */ |
| public void loadTestPackages() throws NoSuchAlgorithmException { |
| if (mTestPackageMap.size() == 0) { |
| mCaseRepos.loadTestPackages(); |
| } |
| } |
| |
| /** |
| * Remove all of the test packages. |
| */ |
| public void removeTestPacakges() { |
| mTestPackageMap.clear(); |
| } |
| |
| /** |
| * Get the package binary name. |
| * |
| * @param appPackageName The JAVA package name. |
| * @return The binary name of the package. |
| */ |
| public String getPackageBinaryName(String appPackageName) { |
| |
| for (TestPackage pkg : mTestPackageMap.values()) { |
| if (appPackageName.equals(pkg.getAppPackageName())) { |
| return pkg.getAppBinaryName(); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Get the root directory of configuration. |
| * |
| * @return The root directory of configuration. |
| */ |
| public String getConfigRoot() { |
| return mConfigRoot; |
| } |
| |
| /** |
| * Get the root directory of log files. |
| * |
| * @return the root directory of log files. |
| */ |
| public String getLogRoot() { |
| return mLogRoot; |
| } |
| |
| /** |
| * Get string attribute value. |
| * |
| * @param doc The document. |
| * @param tagName The tag name. |
| * @param attrName The attribute name. |
| * @param fileName The file name. |
| * @return The attribute value. |
| */ |
| private String getStringAttributeValue(final Document doc, |
| final String tagName, final String attrName, final String fileName) { |
| |
| String cfgStr = null; |
| try { |
| cfgStr = getStringAttributeValue(doc |
| .getElementsByTagName(tagName).item(0), attrName); |
| if ((null == cfgStr) || (cfgStr.length() == 0)) { |
| Log.e("Configure error (in " + fileName |
| + "), pls make sure <" + tagName + ">'s attribute <" |
| + attrName + ">'s value is correctly set.", null); |
| return null; |
| } |
| } catch (Exception e) { |
| Log.e("Configure error (in " + fileName |
| + "), pls make sure <" + tagName |
| + ">'s value is correctly set.", null); |
| return null; |
| } |
| |
| return cfgStr; |
| } |
| |
| /** |
| * Get string attribute value if it exists. |
| * |
| * @param doc The document. |
| * @param tagName The tag name. |
| * @param attrName The attribute name. |
| * @param fileName The file name. |
| * @return The attribute value. |
| */ |
| private String getStringAttributeValueOpt(final Document doc, |
| final String tagName, final String attrName, final String fileName) { |
| |
| String cfgStr = null; |
| try { |
| cfgStr = getStringAttributeValue(doc |
| .getElementsByTagName(tagName).item(0), attrName); |
| } catch (Exception e) { |
| return null; |
| } |
| |
| return cfgStr; |
| } |
| |
| /** |
| * Load configuration values from config file. |
| * |
| * @param doc The document from which to load the values. |
| */ |
| private void getConfigValues(final Document doc) { |
| NodeList intValues = doc.getElementsByTagName("IntValue"); |
| for (int i = 0; i < intValues.getLength(); i++) { |
| Node n = intValues.item(i); |
| String name = getStringAttributeValue(n, "name"); |
| String value = getStringAttributeValue(n, "value"); |
| try { |
| Integer v = Integer.parseInt(value); |
| Ints.valueOf(name).setValue(v); |
| } catch (NumberFormatException e) { |
| Log.e("Configuration error. Illegal value for " + name, e); |
| } catch (IllegalArgumentException e) { |
| Log.e("Unknown configuration value " + name, e); |
| } |
| } |
| } |
| |
| /** |
| * Validate the directory. |
| * |
| * @param path The path to be validated. |
| * @return If valid directory, return true; else, return false. |
| */ |
| private boolean validateDirectory(final String path) { |
| |
| File pathFile = new File(path); |
| if ((null == pathFile) || (pathFile.exists() == false) |
| || (pathFile.isDirectory() == false)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Extract a resource into the given destination directory. This is used |
| * for resource files such as images and CSS. |
| * |
| * @param name The name of the resource. |
| * @param dest The directory the resource should be extracted to. |
| * @return true, if successful, or the file already existed. |
| */ |
| private boolean extractResource(String name, String dest) { |
| File file = new File(dest, name); |
| if (!file.exists()) { |
| // do not extract again if the file is already there |
| InputStream in = getClass().getResourceAsStream(File.separator + name); |
| if (in != null) { |
| try { |
| FileOutputStream fout = new FileOutputStream(file); |
| byte[] data = new byte[512]; |
| int len = in.read(data); |
| while (len > 0) { |
| fout.write(data, 0, len); |
| len = in.read(data); |
| } |
| fout.flush(); |
| fout.close(); |
| in.close(); |
| } catch (FileNotFoundException e) { |
| return false; |
| } catch (IOException e) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Get the case repository. |
| * |
| * @return The case repository. |
| */ |
| public CaseRepository getCaseRepository() { |
| return mCaseRepos; |
| } |
| |
| /** |
| * Get the plan repository. |
| * |
| * @return The plan repository. |
| */ |
| public PlanRepository getPlanRepository() { |
| return mPlanRepos; |
| } |
| |
| /** |
| * Get the result repository. |
| * |
| * @return The result repository. |
| */ |
| public ResultRepository getResultRepository() { |
| return mResultRepos; |
| } |
| |
| /** |
| * Storing the root information of some repository. |
| * |
| */ |
| class Repository { |
| protected String mRoot; |
| |
| Repository(String root) { |
| mRoot = root; |
| } |
| |
| /** |
| * Get the root of the repository. |
| * |
| * @return The root of the repository. |
| */ |
| public String getRoot() { |
| return mRoot; |
| } |
| |
| /** |
| * Check if the specified file is a valid XML file. |
| * |
| * @param f The file to be valid. |
| * @return If valid XML file, return true; else, return false. |
| */ |
| public boolean isValidXmlFile(File f) { |
| if (f.getPath().endsWith(FILE_SUFFIX_XML)) { |
| return true; |
| } |
| |
| return false; |
| } |
| } |
| |
| /** |
| * Storing the information of result repository. |
| */ |
| class ResultRepository extends Repository { |
| |
| ResultRepository(String root) { |
| super(root); |
| } |
| |
| /** |
| * Load test results to create session accordingly. |
| */ |
| public void loadTestResults() { |
| |
| for (File f : new File(mRoot).listFiles()) { |
| if (f.isDirectory()) { |
| String pathName = mRoot + File.separator + f.getName() |
| + File.separator + TestSessionLog.CTS_RESULT_FILE_NAME; |
| if (HostUtils.isFileExist(pathName)) { |
| try { |
| TestSessionLog log = |
| TestSessionLogBuilder.getInstance().build(pathName); |
| TestSession ts = TestSessionBuilder.getInstance().build(log); |
| if (ts != null) { |
| TestHost.getInstance().addSession(ts); |
| } |
| } catch (Exception e) { |
| Log.e("Error importing existing result from " + pathName, e); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Storing the information of case repository. |
| */ |
| class CaseRepository extends Repository { |
| CaseRepository(String root) { |
| super(root); |
| } |
| |
| /** |
| * Get package names. |
| * |
| * @return The JAVA package names. |
| */ |
| public ArrayList<String> getPackageNames() { |
| ArrayList<String> packageNames = new ArrayList<String>(); |
| for (TestPackage pkg : mTestPackageMap.values()) { |
| String binaryName = pkg.getAppBinaryName(); |
| if (binaryName.equals(SIGNATURE_TEST_PACKAGE_NAME)) { |
| packageNames.add(0, binaryName); |
| } else { |
| packageNames.add(pkg.getAppPackageName()); |
| } |
| } |
| |
| return packageNames; |
| } |
| |
| /** |
| * Get package binary names. |
| * |
| * @return The package binary names. |
| */ |
| public ArrayList<String> getPackageBinaryNames() { |
| ArrayList<String> pkgBinaryNames = new ArrayList<String>(); |
| for (TestPackage pkg : mTestPackageMap.values()) { |
| String pkgBinaryName = pkg.getAppBinaryName(); |
| if (pkgBinaryName.equals(SIGNATURE_TEST_PACKAGE_NAME)) { |
| pkgBinaryNames.add(0, pkgBinaryName); |
| } else { |
| pkgBinaryNames.add(pkg.getAppBinaryName()); |
| } |
| } |
| |
| return pkgBinaryNames; |
| } |
| |
| /** |
| * Load package XML file names. |
| * |
| * @return The package XML file names. |
| */ |
| public List<String> loadPackageXmlFileNames() { |
| ArrayList<String> packageXmlFileNames = new ArrayList<String>(); |
| |
| for (File f : new File(mRoot).listFiles()) { |
| if (isValidXmlFile(f)) { |
| String fileName = f.getName(); |
| String name = fileName.substring(0, fileName.lastIndexOf(".")); |
| packageXmlFileNames.add(name); |
| } |
| } |
| |
| return packageXmlFileNames; |
| } |
| |
| /** |
| * Load test packages. |
| */ |
| public void loadTestPackages() throws NoSuchAlgorithmException { |
| List<String> pkgXmlFileNameList = loadPackageXmlFileNames(); |
| for (String pkgXmlFileName : pkgXmlFileNameList) { |
| String xmlPath = getRoot() + File.separator |
| + pkgXmlFileName + FILE_SUFFIX_XML; |
| TestPackage pkg = loadPackage(xmlPath); |
| if (isValidPackage(pkg)) { |
| mTestPackageMap.put(pkg.getAppPackageName(), pkg); |
| } |
| } |
| } |
| |
| /** |
| * Get package binary name. |
| * |
| * @param packagePath The package path. |
| * @return The package binary name. |
| */ |
| private String getPackageBinaryName(String packagePath) { |
| return packagePath.substring(packagePath.lastIndexOf(File.separator) + 1, |
| packagePath.lastIndexOf(".")); |
| } |
| |
| /** |
| * Check if the specified package is a valid package. |
| * |
| * @param pkg The specified package to be checked. |
| * @return If valid package, return true; else, return false. |
| */ |
| private boolean isValidPackage(TestPackage pkg) { |
| if (pkg == null) { |
| return false; |
| } |
| |
| String pkgFileName = pkg.getAppBinaryName(); |
| String apkFilePath = mRoot + File.separator + pkgFileName + FILE_SUFFIX_APK; |
| String xmlFilePath = mRoot + File.separator + pkgFileName + FILE_SUFFIX_XML; |
| File xmlFile = new File(xmlFilePath); |
| if (pkg.isHostSideOnly()) { |
| if (xmlFile.exists() && xmlFile.isFile()) { |
| return true; |
| } |
| } else { |
| File apkFile = new File(apkFilePath); |
| if (xmlFile.exists() && xmlFile.isFile() |
| && apkFile.exists() && apkFile.isFile()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Add package to case repository. |
| * |
| * @param packagePath The package to be added. |
| * @return If valid package and succeed in add it, return true; else, return false. |
| */ |
| public boolean addPackage(String packagePath) throws FileNotFoundException, |
| IOException, NoSuchAlgorithmException { |
| ZipFile zipFile = new ZipFile(packagePath); |
| Enumeration<? extends ZipEntry> entries = zipFile.entries(); |
| |
| ArrayList<String> filePathList = new ArrayList<String>(); |
| String xmlFilePath = null; |
| while (entries.hasMoreElements()) { |
| ZipEntry entry = entries.nextElement(); |
| |
| String name = entry.getName(); |
| if (name.endsWith(FILE_SUFFIX_APK) |
| || name.endsWith(FILE_SUFFIX_XML) |
| || name.endsWith(FILE_SUFFIX_JAR)) { |
| int index = name.lastIndexOf(File.separator); |
| String fileName = name; |
| if (index != -1) { |
| fileName = name.substring(index + 1); |
| } |
| String filePath = mRoot + File.separator + fileName; |
| writeToFile(zipFile.getInputStream(entry), filePath); |
| filePathList.add(filePath); |
| if (name.endsWith(FILE_SUFFIX_XML)) { |
| xmlFilePath = filePath; |
| } |
| } |
| } |
| |
| String packageName = getPackageBinaryName(packagePath); |
| PackageZipFileValidator zipValidator = new PackageZipFileValidator(); |
| if (zipValidator.validate(filePathList, packageName, xmlFilePath) == false) { |
| for (String filePath : filePathList) { |
| deleteFile(filePath); |
| } |
| return false; |
| } |
| |
| TestPackage pkg = loadPackage(xmlFilePath); |
| if (pkg != null) { |
| mTestPackageMap.put(pkg.getAppPackageName(), pkg); |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Load the package from the package description XML file. |
| * |
| * @param xmlFileName The package description XML file. |
| * @return The TestPackage. |
| */ |
| private TestPackage loadPackage(String xmlFileName) throws NoSuchAlgorithmException { |
| if ((xmlFileName == null) || (xmlFileName.length() == 0)) { |
| return null; |
| } |
| |
| File xmlFile = new File(xmlFileName); |
| TestSessionBuilder sessionBuilder; |
| TestPackage pkg = null; |
| try { |
| sessionBuilder = TestSessionBuilder.getInstance(); |
| pkg = sessionBuilder.loadPackage(xmlFile, null); |
| } catch (ParserConfigurationException e) { |
| } catch (SAXException e) { |
| } catch (IOException e) { |
| } |
| return pkg; |
| } |
| |
| /** |
| * check if the packagePath is valid against the case repository |
| * |
| * @param packagePath the path to be checked |
| * @return if the path isn't suffixed with .zip, return false; |
| * if the package name exists in case repository, return false; |
| * for other conditions, return true; |
| */ |
| public boolean isValidPackageName(String packagePath) { |
| if (!packagePath.endsWith(FILE_SUFFIX_ZIP)) { |
| Log.e("Package error: package name " + packagePath + " is not a zip file.", null); |
| return false; |
| } |
| |
| String fileName = packagePath.substring(packagePath.lastIndexOf(File.separator) + 1, |
| packagePath.length() - FILE_SUFFIX_ZIP.length()); |
| |
| String path = mRoot + File.separator + fileName; |
| if (HostUtils.isFileExist(path + FILE_SUFFIX_APK) |
| || HostUtils.isFileExist(path + FILE_SUFFIX_XML)) { |
| Log.e("Package error: package name " + fileName + " exists already.", null); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Validate zipped package file against package logic |
| */ |
| class PackageZipFileValidator { |
| /** |
| * validate the package content to see if it contains enough data |
| ** |
| * @param filePathList The file path list contained in the package zip file. |
| * @param packageName The package name. |
| * @param xmlFilePath The description XML file path. |
| * @return If valid, return true; else, return false. |
| */ |
| public boolean validate(ArrayList<String> filePathList, String packageName, |
| String xmlFilePath) throws NoSuchAlgorithmException { |
| if (xmlFilePath == null) { |
| Log.e("Package error: package doesn't contain XML file: " |
| + packageName + FILE_SUFFIX_XML, null); |
| return false; |
| } else { |
| TestPackage pkg = loadPackage(xmlFilePath); |
| if (pkg == null) { |
| Log.e("Package error: the description XML file contained in : " |
| + packageName + FILE_SUFFIX_APK + " is invalid.", null); |
| return false; |
| } else { |
| if (!validateTargetApk(filePathList, pkg.getTargetBinaryName())) { |
| return false; |
| } |
| |
| if (!validateHostControllerJar(filePathList, pkg.getJarPath())) { |
| return false; |
| } |
| |
| String apkFilePath = mRoot + File.separator |
| + packageName + FILE_SUFFIX_APK; |
| if (!filePathList.contains(apkFilePath)) { |
| Log.e("Package error: package doesn't contain APK file: " |
| + packageName + FILE_SUFFIX_APK, null); |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Validate host controller jar file described in the package description XML file. |
| * |
| * @param filePathList The files contained in the zipped package file. |
| * @param hostControllerJarPath The host controller jar file path. |
| * @return If the host controller jar file contained in the zipped package, |
| * return true; else, return false. |
| */ |
| private boolean validateHostControllerJar(ArrayList<String> filePathList, |
| String hostControllerJarPath) { |
| if ((hostControllerJarPath != null) && (hostControllerJarPath.length() != 0)) { |
| String targetFilePath = |
| mRoot + File.separator + hostControllerJarPath + FILE_SUFFIX_JAR; |
| if (filePathList.contains(targetFilePath)) { |
| return true; |
| } |
| } else { |
| return true; |
| } |
| |
| //String jarFileName = getPackageName(hostControllerJarPath); |
| Log.e("Package error: host controler jar file " |
| + hostControllerJarPath + FILE_SUFFIX_JAR |
| + " is not contained in the package zip file.", null); |
| return false; |
| } |
| |
| /** |
| * Validate target APK file described in the package description XML file. |
| * |
| * @param filePathList The files contained in the zipped package file. |
| * @param targetName The target APK name. |
| * @return If the target APK file contained in the zipped package file, return true; |
| * else, return false. |
| */ |
| private boolean validateTargetApk(ArrayList<String> filePathList, String targetName) { |
| if ((targetName != null) && (targetName.length() != 0)) { |
| String targetFileName = mRoot + File.separator + targetName + FILE_SUFFIX_APK; |
| if (filePathList.contains(targetFileName)) { |
| return true; |
| } |
| } else { |
| return true; |
| } |
| |
| Log.e("Package error: target file " + targetName + FILE_SUFFIX_APK |
| + " is not contained in the package zip file.", null); |
| return false; |
| } |
| } |
| |
| /** |
| * Write the input stream to file. |
| * |
| * @param in The input stream. |
| * @param path The file to write to. |
| */ |
| private void writeToFile(InputStream in, String path) throws IOException { |
| OutputStream out = new BufferedOutputStream(new FileOutputStream(path)); |
| byte[] buffer = new byte[1024]; |
| int len; |
| |
| while ((len = in.read(buffer)) >= 0) { |
| out.write(buffer, 0, len); |
| } |
| |
| in.close(); |
| out.close(); |
| } |
| |
| /** |
| * Remove packages from case repository. |
| * |
| * @param packageName Package to be removed. |
| */ |
| public void removePackages(String packageName) { |
| if ((packageName == null) || (packageName.length() == 0)) { |
| return; |
| } |
| |
| if (packageName.equals(ALL)) { |
| ArrayList<String> packageNames = getCaseRepository().getPackageNames(); |
| for (String pkgName : packageNames) { |
| removePackage(pkgName); |
| } |
| } else { |
| if (!getPackageNames().contains(packageName)) { |
| Log.e("Package " + packageName + " doesn't exist in repository!", null); |
| return; |
| } |
| removePackage(packageName); |
| } |
| } |
| |
| /** |
| * Remove the specified package. |
| * |
| * @param packageName The package name. |
| */ |
| private void removePackage(String packageName) { |
| TestPackage pkg = getTestPackage(packageName); |
| if (pkg != null) { |
| ArrayList<String> targetBinaryNames = getTargetBinaryNames(); |
| String targetBinaryName = pkg.getTargetBinaryName(); |
| if ((targetBinaryName != null) && (targetBinaryName.length() != 0) |
| && (getReferenceCount(targetBinaryNames, targetBinaryName) == 1)) { |
| String targetBinaryFileName = mRoot + File.separator + targetBinaryName |
| + FILE_SUFFIX_APK; |
| deleteFile(targetBinaryFileName); |
| } |
| |
| ArrayList<String> hostControllers = getHostControllers(); |
| String hostControllerPath = pkg.getJarPath(); |
| if ((hostControllerPath != null) && (hostControllerPath.length() != 0) |
| && (getReferenceCount(hostControllers, hostControllerPath) == 1)) { |
| String jarFilePath = mRoot + File.separator |
| + hostControllerPath + FILE_SUFFIX_JAR; |
| deleteFile(jarFilePath); |
| } |
| } |
| |
| String packageBinaryName = pkg.getAppBinaryName(); |
| mTestPackageMap.remove(pkg.getAppPackageName()); |
| |
| String apkPath = mRoot + File.separator + packageBinaryName + FILE_SUFFIX_APK; |
| String xmlPath = mRoot + File.separator + packageBinaryName + FILE_SUFFIX_XML; |
| deleteFile(apkPath); |
| deleteFile(xmlPath); |
| } |
| |
| /** |
| * Get the reference count of the specific value against the value list. |
| * |
| * @param list The value list to be checked against. |
| * @param value The value to be checked. |
| * @return The reference count. |
| */ |
| private int getReferenceCount(ArrayList<String> list, String value) { |
| if ((list == null) || (list.size() == 0) || (value == null)) { |
| return 0; |
| } |
| |
| int count = 0; |
| for (String str : list) { |
| if (value.equals(str)) { |
| count ++; |
| } |
| } |
| |
| return count; |
| } |
| |
| /** |
| * Get the target binary names contained with the test package description XML files. |
| * |
| * @return The target binary names. |
| */ |
| private ArrayList<String> getTargetBinaryNames() { |
| ArrayList<String> targetBinaryNames = new ArrayList<String>(); |
| for (TestPackage pkg : mTestPackageMap.values()) { |
| targetBinaryNames.add(pkg.getTargetBinaryName()); |
| } |
| return targetBinaryNames; |
| } |
| |
| /** |
| * Get the host controllers contained with the test package description XML files. |
| * |
| * @return The host controllers. |
| */ |
| private ArrayList<String> getHostControllers() { |
| ArrayList<String> hostControllers = new ArrayList<String>(); |
| for (TestPackage pkg : mTestPackageMap.values()) { |
| hostControllers.add(pkg.getJarPath()); |
| } |
| return hostControllers; |
| } |
| |
| /** |
| * Delete the specific file. |
| * |
| * @param filepath The file to be deleted. |
| */ |
| private void deleteFile(String filepath) { |
| File file = new File(filepath); |
| if (file.exists() && file.isFile()) { |
| file.delete(); |
| } |
| } |
| |
| /** |
| * Get package's APK file path via the package name. |
| * |
| * @param packageName The package name. |
| * @return The package's APK file path. |
| */ |
| public String getApkPath(String packageName) { |
| return mRoot + File.separator + packageName + FILE_SUFFIX_APK; |
| } |
| |
| /** |
| * Get package's XML file path via the package name. |
| * @param packageName The package name. |
| * @return The package's XML file path. |
| */ |
| public String getXmlPath(String packageName) { |
| return mRoot + File.separator + packageName + FILE_SUFFIX_XML; |
| } |
| |
| /** |
| * List available package and suite. |
| * |
| * @param expectPackage expected package name |
| * @return list which contains available packages, suites and cases. |
| */ |
| @SuppressWarnings("unchecked") |
| public List<ArrayList<String>> listAvailablePackage(String expectPackage) { |
| ArrayList<String> packageList = new ArrayList<String>(); |
| ArrayList<String> suiteList = new ArrayList<String>(); |
| ArrayList<String> caseList = new ArrayList<String>(); |
| ArrayList<String> testList = new ArrayList<String>(); |
| |
| for (TestPackage testPackage : mTestPackageMap.values()) { |
| String appPackageName = testPackage.getAppPackageName(); |
| if (expectPackage.equals(appPackageName)) { |
| testPackage.getTestSuiteNames(appPackageName, suiteList, caseList); |
| } else if (appPackageName.startsWith(expectPackage)) { |
| packageList.add(appPackageName); |
| } else { |
| if (expectPackage.indexOf(Test.METHOD_SEPARATOR) == -1) { |
| testPackage.getTestCaseNames(expectPackage, caseList, testList); |
| } else { |
| testPackage.getTestNames(expectPackage, testList); |
| } |
| } |
| } |
| |
| return Arrays.asList(packageList, suiteList, caseList, testList); |
| } |
| } |
| |
| /** |
| * Storing information of test plans. |
| * |
| */ |
| class PlanRepository extends Repository { |
| |
| PlanRepository(String root) { |
| super(root); |
| } |
| |
| /** |
| * Get the path of the specified plan. |
| * |
| * @param name The plan name. |
| * @return The plan path. |
| */ |
| public String getPlanPath(String name) { |
| if (mRoot == null) { |
| Log.e("Repository uninitialized!", null); |
| return null; |
| } |
| |
| return mRoot + File.separator + name + FILE_SUFFIX_XML; |
| } |
| |
| /** |
| * Get all plan names in the plan repository. |
| * @return Plan names. |
| */ |
| public ArrayList<String> getAllPlanNames() { |
| ArrayList<String> plans = new ArrayList<String>(); |
| |
| if (mRoot == null) { |
| Log.e("Not specify repository, please check your cts config", |
| null); |
| return plans; |
| } |
| |
| File planRepository = new File(mRoot); |
| if (!planRepository.exists()) { |
| Log.e("Plan Repository doesn't exist: " + mRoot, null); |
| return null; |
| } |
| |
| for (File f : planRepository.listFiles()) { |
| String name = f.getName(); |
| |
| if (name.endsWith(FILE_SUFFIX_XML)) { |
| plans.add(name.substring(0, name.length() - FILE_SUFFIX_XML.length())); |
| } |
| } |
| |
| return plans; |
| } |
| } |
| } |