| /* |
| * Copyright (C) 2010 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.tradefed.testtype; |
| |
| import com.android.cts.util.AbiUtils; |
| import com.android.ddmlib.Log.LogLevel; |
| import com.android.ddmlib.testrunner.TestIdentifier; |
| import com.android.tradefed.log.LogUtil.CLog; |
| import com.android.tradefed.targetprep.ITargetPreparer; |
| import com.android.tradefed.testtype.IAbi; |
| import com.android.tradefed.testtype.IRemoteTest; |
| import com.android.tradefed.testtype.InstrumentationTest; |
| import com.android.tradefed.util.StreamUtil; |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.security.DigestInputStream; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.Collection; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Container for CTS test info. |
| * <p/> |
| * Knows how to translate this info into a runnable {@link IRemoteTest}. |
| */ |
| class TestPackageDef implements ITestPackageDef { |
| |
| public static final String HOST_SIDE_ONLY_TEST = "hostSideOnly"; |
| public static final String NATIVE_TEST = "native"; |
| public static final String WRAPPED_NATIVE_TEST = "wrappednative"; |
| public static final String VM_HOST_TEST = "vmHostTest"; |
| public static final String DEQP_TEST = "deqpTest"; |
| public static final String UIAUTOMATOR_TEST = "uiAutomator"; |
| public static final String JUNIT_DEVICE_TEST = "jUnitDeviceTest"; |
| |
| private String mAppPackageName = null; |
| private String mAppNameSpace = null; |
| private String mName = null; |
| private String mRunner = null; |
| private String mTestType = null; |
| private String mJarPath = null; |
| private String mRunTimeArgs = null; |
| private String mTestPackageName = null; |
| private String mDigest = null; |
| private long mRuntimeHint = 0; |
| private IAbi mAbi = null; |
| private List<ITargetPreparer> mPreparers = null; |
| |
| // use a LinkedHashSet for predictable iteration insertion-order, and fast |
| // lookups |
| private Collection<TestIdentifier> mTests = new LinkedHashSet<TestIdentifier>(); |
| // also maintain an index of known test classes |
| private Collection<String> mTestClasses = new LinkedHashSet<String>(); |
| // store instance arguments in order too for consistency |
| private Map<TestIdentifier, List<Map<String, String>>> mTestInstanceArguments = |
| new LinkedHashMap<>(); |
| |
| // dynamic options, not parsed from package xml |
| private String mClassName; |
| private String mMethodName; |
| private TestFilter mTestFilter = new TestFilter(); |
| private String mTargetBinaryName; |
| private String mTargetNameSpace; |
| // only timeout per package is supported. To change this to method granularity, |
| // test invocation should be done in method level. |
| // So for now, only max timeout for the package is used. |
| private int mTimeoutInMins = -1; |
| |
| @Override |
| public IAbi getAbi() { |
| return mAbi; |
| } |
| |
| /** |
| * @param abi the ABI to run this package on |
| */ |
| public void setAbi(IAbi abi) { |
| mAbi = abi; |
| } |
| |
| /** |
| * @return unique id representing this test package for this ABI. |
| */ |
| @Override |
| public String getId() { |
| return AbiUtils.createId(getAbi().getName(), getAppPackageName()); |
| } |
| |
| void setAppPackageName(String appPackageName) { |
| mAppPackageName = appPackageName; |
| } |
| |
| String getAppPackageName() { |
| return mAppPackageName; |
| } |
| |
| void setRunTimeArgs(String runTimeArgs) { |
| mRunTimeArgs = runTimeArgs; |
| } |
| |
| void setAppNameSpace(String appNameSpace) { |
| mAppNameSpace = appNameSpace; |
| } |
| |
| String getAppNameSpace() { |
| return mAppNameSpace; |
| } |
| |
| void setName(String name) { |
| mName = name; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public long getRuntimeHint() { |
| return mRuntimeHint; |
| } |
| |
| void setRuntimeHint(long runtimeHint) { |
| mRuntimeHint = runtimeHint; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String getName() { |
| return mName; |
| } |
| |
| void setRunner(String runnerName) { |
| mRunner = runnerName; |
| } |
| |
| String getRunner() { |
| return mRunner; |
| } |
| |
| void setTestType(String testType) { |
| mTestType = testType; |
| } |
| |
| String getTestType() { |
| return mTestType; |
| } |
| |
| void setJarPath(String jarPath) { |
| mJarPath = jarPath; |
| } |
| |
| String getJarPath() { |
| return mJarPath; |
| } |
| |
| void setTestPackageName(String testPackageName) { |
| mTestPackageName = testPackageName; |
| } |
| |
| void setTargetBinaryName(String targetBinaryName) { |
| mTargetBinaryName = targetBinaryName; |
| } |
| |
| void setTargetNameSpace(String targetNameSpace) { |
| mTargetNameSpace = targetNameSpace; |
| } |
| |
| @Override |
| public String getTargetApkName() { |
| if (mTargetBinaryName != null && !mTargetBinaryName.isEmpty()) { |
| return String.format("%s.apk", mTargetBinaryName); |
| } |
| return null; |
| } |
| |
| @Override |
| public String getTargetPackageName() { |
| if (mTargetNameSpace != null && mTargetNameSpace.isEmpty()) { |
| return null; |
| } |
| return mTargetNameSpace; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setTestFilter(TestFilter testFilter) { |
| mTestFilter = testFilter; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setClassName(String className, String methodName) { |
| mClassName = className; |
| mMethodName = methodName; |
| } |
| |
| /** |
| * Setter for injecting a list of {@link ITargetPreparer}s as configured in module test config. |
| * @param preparers |
| */ |
| void setPackagePreparers(List<ITargetPreparer> preparers) { |
| mPreparers = preparers; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public List<ITargetPreparer> getPackagePreparers() { |
| return mPreparers; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public IRemoteTest createTest(File testCaseDir) { |
| mTestFilter.setTestInclusion(mClassName, mMethodName); |
| mTests = filterTests(); |
| |
| if (HOST_SIDE_ONLY_TEST.equals(mTestType)) { |
| CLog.d("Creating host test for %s", mName); |
| JarHostTest hostTest = new JarHostTest(); |
| if (mTimeoutInMins >= 0) { |
| CLog.d("Setting new timeout to " + mTimeoutInMins + " mins"); |
| hostTest.setTimeout(mTimeoutInMins * 60 * 1000); |
| } |
| hostTest.setRunName(mAppPackageName); |
| hostTest.setJarFileName(mJarPath); |
| hostTest.setTests(mTests); |
| hostTest.setAbi(mAbi); |
| mDigest = generateDigest(testCaseDir, mJarPath); |
| return hostTest; |
| } else if (VM_HOST_TEST.equals(mTestType)) { |
| CLog.d("Creating vm host test for %s", mName); |
| VMHostTest vmHostTest = new VMHostTest(); |
| vmHostTest.setRunName(mAppPackageName); |
| vmHostTest.setJarFileName(mJarPath); |
| vmHostTest.setTests(mTests); |
| vmHostTest.setAbi(mAbi); |
| mDigest = generateDigest(testCaseDir, mJarPath); |
| return vmHostTest; |
| } else if (DEQP_TEST.equals(mTestType)) { |
| DeqpTestRunner deqpTest = |
| new DeqpTestRunner(mAppPackageName, mName, mTests, mTestInstanceArguments); |
| deqpTest.setAbi(mAbi); |
| return deqpTest; |
| } else if (NATIVE_TEST.equals(mTestType)) { |
| GeeTest geeTest = new GeeTest(mAppPackageName, mName); |
| geeTest.setAbi(mAbi); |
| return geeTest; |
| } else if (WRAPPED_NATIVE_TEST.equals(mTestType)) { |
| CLog.d("Creating new wrapped native test for %s", mName); |
| WrappedGTest wrappedGeeTest = new WrappedGTest(mAppNameSpace, mAppPackageName, mName, mRunner); |
| wrappedGeeTest.setAbi(mAbi); |
| return wrappedGeeTest; |
| } else if (UIAUTOMATOR_TEST.equals(mTestType)) { |
| UiAutomatorJarTest uiautomatorTest = new UiAutomatorJarTest(); |
| return setUiAutomatorTest(uiautomatorTest); |
| } else if (JUNIT_DEVICE_TEST.equals(mTestType)){ |
| CLog.d("Creating JUnit device test %s", mName); |
| JUnitDeviceTest jUnitDeviceTest = new JUnitDeviceTest(); |
| jUnitDeviceTest.setRunName(mAppPackageName); |
| jUnitDeviceTest.addTestJarFileName(mJarPath); |
| jUnitDeviceTest.addRunTimeArgs(mRunTimeArgs); |
| jUnitDeviceTest.setTests(mTests); |
| jUnitDeviceTest.setAbi(mAbi); |
| mDigest = generateDigest(testCaseDir, mJarPath); |
| return jUnitDeviceTest; |
| } else { |
| CLog.d("Creating instrumentation test for %s", mName); |
| CtsInstrumentationApkTest instrTest = new CtsInstrumentationApkTest(); |
| if (mTimeoutInMins >= 0) { |
| // as timeout cannot be set for each test, |
| // increase the time-out of the whole package |
| CLog.d("Setting new timeout to " + mTimeoutInMins + " mins"); |
| instrTest.setTestTimeout(mTimeoutInMins * 60 * 1000); |
| } |
| return setInstrumentationTest(instrTest, testCaseDir); |
| } |
| } |
| |
| /** |
| * Populates given {@link CtsInstrumentationApkTest} with data from the package xml. |
| * |
| * @param testCaseDir |
| * @param instrTest |
| * @return the populated {@link InstrumentationTest} or <code>null</code> |
| */ |
| private InstrumentationTest setInstrumentationTest(CtsInstrumentationApkTest instrTest, |
| File testCaseDir) { |
| instrTest.setRunName(mAppPackageName); |
| instrTest.setPackageName(mAppNameSpace); |
| instrTest.setRunnerName(mRunner); |
| instrTest.setAbi(mAbi); |
| instrTest.setTestsToRun(mTests, false |
| /* force batch mode off to always run using testFile */); |
| instrTest.setReRunUsingTestFile(true); |
| // mName means 'apk file name' for instrumentation tests |
| instrTest.addInstallApk(String.format("%s.apk", mName), mAppNameSpace); |
| mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName)); |
| if (mTests.size() > 1000) { |
| // TODO: hack, large test suites can take longer to collect tests, increase timeout |
| instrTest.setCollectsTestsShellTimeout(10 * 60 * 1000); |
| } |
| return instrTest; |
| } |
| |
| /** |
| * Populates given {@link UiAutomatorJarTest} with data from the package xml. |
| * |
| * @param uiautomatorTest |
| * @return the populated {@link UiAutomatorJarTest} or <code>null</code> |
| */ |
| @SuppressWarnings("deprecation") |
| private IRemoteTest setUiAutomatorTest(UiAutomatorJarTest uiautomatorTest) { |
| uiautomatorTest.setInstallArtifacts(getJarPath()); |
| if (mClassName != null) { |
| if (mMethodName != null) { |
| CLog.logAndDisplay(LogLevel.WARN, "ui automator tests don't currently support" + |
| "running individual methods"); |
| } |
| uiautomatorTest.addClassName(mClassName); |
| } else { |
| uiautomatorTest.addClassNames(mTestClasses); |
| } |
| uiautomatorTest.setRunName(mAppPackageName); |
| uiautomatorTest.setCaptureLogs(false); |
| return uiautomatorTest; |
| } |
| |
| /** |
| * Filter the tests to run based on list of included/excluded tests, class and method name. |
| * |
| * @return the filtered collection of tests |
| */ |
| private Collection<TestIdentifier> filterTests() { |
| mTestFilter.setTestInclusion(mClassName, mMethodName); |
| return mTestFilter.filter(mTests); |
| } |
| |
| boolean isKnownTestClass(String className) { |
| return mTestClasses.contains(className); |
| } |
| |
| /** |
| * Add a {@link TestIdentifier} to the list of tests in this package. |
| * |
| * @param testDef |
| * @param timeout in mins |
| */ |
| void addTest(TestIdentifier testDef, int timeout) { |
| mTests.add(testDef); |
| mTestClasses.add(testDef.getClassName()); |
| mTestInstanceArguments.put(testDef, new LinkedList<Map<String, String>>()); |
| // 0 means no timeout, so keep 0 if already is. |
| if ((timeout > mTimeoutInMins) && (mTimeoutInMins != 0)) { |
| mTimeoutInMins = timeout; |
| } |
| } |
| |
| /** |
| * Add a test instance to an existing {@link TestIdentifier}. |
| */ |
| void addTestInstance(TestIdentifier testDef, Map<String, String> instanceArguments) { |
| if (!mTestInstanceArguments.containsKey(testDef)) { |
| throw new IllegalStateException("test id does not name an existing test"); |
| } |
| mTestInstanceArguments.get(testDef).add(instanceArguments); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Collection<TestIdentifier> getTests() { |
| return mTests; |
| } |
| |
| /** |
| * Get the instance argument map for tests. |
| * <p/> |
| * Exposed for unit testing. |
| */ |
| public Map<TestIdentifier, List<Map<String, String>>> getTestInstanceArguments() { |
| return mTestInstanceArguments; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String getDigest() { |
| return mDigest; |
| } |
| |
| /** |
| * Generate a sha1sum digest for a file. |
| * <p/> |
| * Exposed for unit testing. |
| * |
| * @param fileDir the directory of the file |
| * @param fileName the name of the file |
| * @return a hex {@link String} of the digest |
| */ |
| String generateDigest(File fileDir, String fileName) { |
| final String algorithm = "SHA-1"; |
| InputStream fileStream = null; |
| DigestInputStream d = null; |
| try { |
| fileStream = getFileStream(fileDir, fileName); |
| MessageDigest md = MessageDigest.getInstance(algorithm); |
| d = new DigestInputStream(fileStream, md); |
| byte[] buffer = new byte[8196]; |
| while (d.read(buffer) != -1) { |
| } |
| return toHexString(md.digest()); |
| } catch (NoSuchAlgorithmException e) { |
| return algorithm + " not found"; |
| } catch (IOException e) { |
| CLog.e(e); |
| } finally { |
| StreamUtil.close(d); |
| StreamUtil.close(fileStream); |
| } |
| return "failed to generate digest"; |
| } |
| |
| /** |
| * Retrieve an input stream for given file |
| * <p/> |
| * Exposed so unit tests can mock. |
| */ |
| InputStream getFileStream(File fileDir, String fileName) throws FileNotFoundException { |
| InputStream fileStream; |
| fileStream = new BufferedInputStream(new FileInputStream(new File(fileDir, fileName))); |
| return fileStream; |
| } |
| |
| /** |
| * Convert the given byte array into a lowercase hex string. |
| * |
| * @param arr The array to convert. |
| * @return The hex encoded string. |
| */ |
| private String toHexString(byte[] arr) { |
| StringBuilder buf = new StringBuilder(arr.length * 2); |
| for (byte b : arr) { |
| buf.append(String.format("%02x", b & 0xFF)); |
| } |
| return buf.toString(); |
| } |
| |
| @Override |
| public int compareTo(ITestPackageDef testPackageDef) { |
| return getId().compareTo(testPackageDef.getId()); |
| } |
| } |