1 /*
2  * Copyright (C) 2010 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 
17 package com.android.cts.tradefed.testtype;
18 
19 import com.android.compatibility.common.util.AbiUtils;
20 import com.android.ddmlib.testrunner.TestIdentifier;
21 import com.android.tradefed.log.LogUtil.CLog;
22 import com.android.tradefed.targetprep.ITargetPreparer;
23 import com.android.tradefed.testtype.IAbi;
24 import com.android.tradefed.testtype.IRemoteTest;
25 import com.android.tradefed.testtype.InstrumentationTest;
26 import com.android.tradefed.util.StreamUtil;
27 
28 import java.io.BufferedInputStream;
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.FileNotFoundException;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.security.DigestInputStream;
35 import java.security.MessageDigest;
36 import java.security.NoSuchAlgorithmException;
37 import java.util.Collection;
38 import java.util.LinkedHashMap;
39 import java.util.LinkedHashSet;
40 import java.util.LinkedList;
41 import java.util.List;
42 import java.util.Map;
43 
44 /**
45  * Container for CTS test info.
46  * <p/>
47  * Knows how to translate this info into a runnable {@link IRemoteTest}.
48  */
49 class TestPackageDef implements ITestPackageDef {
50 
51     public static final String HOST_SIDE_ONLY_TEST = "hostSideOnly";
52     public static final String NATIVE_TEST = "native";
53     public static final String WRAPPED_NATIVE_TEST = "wrappednative";
54     public static final String VM_HOST_TEST = "vmHostTest";
55     public static final String DEQP_TEST = "deqpTest";
56     public static final String JUNIT_DEVICE_TEST = "jUnitDeviceTest";
57     public static final String TESTNG_DEVICE_TEST = "testNGDeviceTest";
58 
59     private String mAppPackageName = null;
60     private String mAppNameSpace = null;
61     private String mName = null;
62     private String mRunner = null;
63     private String mTestType = null;
64     private String mJarPath = null;
65     private String mRunTimeArgs = null;
66     private String mTestPackageName = null;
67     private String mDigest = null;
68     private IAbi mAbi = null;
69     private List<ITargetPreparer> mPreparers = null;
70 
71     // use a LinkedHashSet for predictable iteration insertion-order, and fast
72     // lookups
73     private Collection<TestIdentifier> mTests = new LinkedHashSet<TestIdentifier>();
74     // also maintain an index of known test classes
75     private Collection<String> mTestClasses = new LinkedHashSet<String>();
76     // store instance arguments in order too for consistency
77     private Map<TestIdentifier, List<Map<String, String>>> mTestInstanceArguments =
78             new LinkedHashMap<>();
79 
80     // dynamic options, not parsed from package xml
81     private String mClassName;
82     private String mMethodName;
83     private TestFilter mTestFilter = new TestFilter();
84     private String mTargetBinaryName;
85     private String mTargetNameSpace;
86     // only timeout per package is supported. To change this to method granularity,
87     // test invocation should be done in method level.
88     // So for now, only max timeout for the package is used.
89     private int mTimeoutInMins = -1;
90 
91     @Override
getAbi()92     public IAbi getAbi() {
93         return mAbi;
94     }
95 
96     /**
97      * @param abi the ABI to run this package on
98      */
setAbi(IAbi abi)99     public void setAbi(IAbi abi) {
100         mAbi = abi;
101     }
102 
103     /**
104      * @return unique id representing this test package for this ABI.
105      */
106     @Override
getId()107     public String getId() {
108         return AbiUtils.createId(getAbi().getName(), getAppPackageName());
109     }
110 
setAppPackageName(String appPackageName)111     void setAppPackageName(String appPackageName) {
112         mAppPackageName = appPackageName;
113     }
114 
getAppPackageName()115     String getAppPackageName() {
116         return mAppPackageName;
117     }
118 
setRunTimeArgs(String runTimeArgs)119     void setRunTimeArgs(String runTimeArgs) {
120         mRunTimeArgs = runTimeArgs;
121     }
122 
setAppNameSpace(String appNameSpace)123     void setAppNameSpace(String appNameSpace) {
124         mAppNameSpace = appNameSpace;
125     }
126 
getAppNameSpace()127     String getAppNameSpace() {
128         return mAppNameSpace;
129     }
130 
setName(String name)131     void setName(String name) {
132         mName = name;
133     }
134 
135     /**
136      * {@inheritDoc}
137      */
138     @Override
getName()139     public String getName() {
140         return mName;
141     }
142 
setRunner(String runnerName)143     void setRunner(String runnerName) {
144         mRunner = runnerName;
145     }
146 
getRunner()147     String getRunner() {
148         return mRunner;
149     }
150 
setTestType(String testType)151     void setTestType(String testType) {
152         mTestType = testType;
153     }
154 
getTestType()155     String getTestType() {
156         return mTestType;
157     }
158 
setJarPath(String jarPath)159     void setJarPath(String jarPath) {
160         mJarPath = jarPath;
161     }
162 
getJarPath()163     String getJarPath() {
164         return mJarPath;
165     }
166 
setTestPackageName(String testPackageName)167     void setTestPackageName(String testPackageName) {
168         mTestPackageName = testPackageName;
169     }
170 
setTargetBinaryName(String targetBinaryName)171     void setTargetBinaryName(String targetBinaryName) {
172         mTargetBinaryName = targetBinaryName;
173     }
174 
setTargetNameSpace(String targetNameSpace)175     void setTargetNameSpace(String targetNameSpace) {
176         mTargetNameSpace = targetNameSpace;
177     }
178 
179     @Override
getTargetApkName()180     public String getTargetApkName() {
181        if (mTargetBinaryName != null && !mTargetBinaryName.isEmpty()) {
182            return String.format("%s.apk", mTargetBinaryName);
183        }
184        return null;
185     }
186 
187     @Override
getTargetPackageName()188     public String getTargetPackageName() {
189         if (mTargetNameSpace != null && mTargetNameSpace.isEmpty()) {
190             return null;
191         }
192         return mTargetNameSpace;
193     }
194 
195     /**
196      * {@inheritDoc}
197      */
198     @Override
setTestFilter(TestFilter testFilter)199     public void setTestFilter(TestFilter testFilter) {
200         mTestFilter = testFilter;
201     }
202 
203     /**
204      * {@inheritDoc}
205      */
206     @Override
setClassName(String className, String methodName)207     public void setClassName(String className, String methodName) {
208         mClassName = className;
209         mMethodName = methodName;
210     }
211 
212     /**
213      * Setter for injecting a list of {@link ITargetPreparer}s as configured in module test config.
214      * @param preparers
215      */
setPackagePreparers(List<ITargetPreparer> preparers)216     void setPackagePreparers(List<ITargetPreparer> preparers) {
217         mPreparers = preparers;
218     }
219 
220     /**
221      * {@inheritDoc}
222      */
223     @Override
getPackagePreparers()224     public List<ITargetPreparer> getPackagePreparers() {
225         return mPreparers;
226     }
227 
228     /**
229      * {@inheritDoc}
230      */
231     @Override
createTest(File testCaseDir)232     public IRemoteTest createTest(File testCaseDir) {
233         mTestFilter.setTestInclusion(mClassName, mMethodName);
234         mTests = filterTests();
235 
236         if (HOST_SIDE_ONLY_TEST.equals(mTestType)) {
237             CLog.d("Creating host test for %s", mName);
238             JarHostTest hostTest = new JarHostTest();
239             if (mTimeoutInMins >= 0) {
240                 CLog.d("Setting new timeout to " + mTimeoutInMins + " mins");
241                 hostTest.setTimeout(mTimeoutInMins * 60 * 1000);
242             }
243             hostTest.setRunName(getId());
244             hostTest.setJarFileName(mJarPath);
245             hostTest.setTests(mTests);
246             hostTest.setAbi(mAbi);
247             mDigest = generateDigest(testCaseDir, mJarPath);
248             return hostTest;
249         } else if (VM_HOST_TEST.equals(mTestType)) {
250             CLog.d("Creating vm host test for %s", mName);
251             VMHostTest vmHostTest = new VMHostTest();
252             vmHostTest.setRunName(getId());
253             vmHostTest.setJarFileName(mJarPath);
254             vmHostTest.setTests(mTests);
255             vmHostTest.setAbi(mAbi);
256             mDigest = generateDigest(testCaseDir, mJarPath);
257             return vmHostTest;
258         } else if (DEQP_TEST.equals(mTestType)) {
259             DeqpTestRunner deqpTest =
260                     new DeqpTestRunner(mAppPackageName, mName, mTests, mTestInstanceArguments);
261             deqpTest.setAbi(mAbi);
262             return deqpTest;
263         } else if (NATIVE_TEST.equals(mTestType)) {
264             GeeTest geeTest = new GeeTest(mAppPackageName, mName);
265             geeTest.setAbi(mAbi);
266             return geeTest;
267         } else if (WRAPPED_NATIVE_TEST.equals(mTestType)) {
268             CLog.d("Creating new wrapped native test for %s", mName);
269             WrappedGTest wrappedGeeTest = new WrappedGTest(mAppNameSpace, mAppPackageName, mName, mRunner);
270             wrappedGeeTest.setAbi(mAbi);
271             return wrappedGeeTest;
272         } else if (JUNIT_DEVICE_TEST.equals(mTestType)){
273             CLog.d("Creating JUnit device test %s", mName);
274             JUnitDeviceTest jUnitDeviceTest = new JUnitDeviceTest();
275             jUnitDeviceTest.setRunName(getId());
276             jUnitDeviceTest.addTestJarFileName(mJarPath);
277             jUnitDeviceTest.addRunTimeArgs(mRunTimeArgs);
278             jUnitDeviceTest.setTests(mTests);
279             jUnitDeviceTest.setAbi(mAbi);
280             mDigest = generateDigest(testCaseDir, mJarPath);
281             return jUnitDeviceTest;
282         } else if (TESTNG_DEVICE_TEST.equals(mTestType)){
283             CLog.d("Creating TestNG device test %s", mName);
284             TestNGDeviceTest testNGDeviceTest = new TestNGDeviceTest();
285             testNGDeviceTest.setRunName(getId());
286             testNGDeviceTest.addTestJarFileName(mJarPath);
287             testNGDeviceTest.addRunTimeArgs(mRunTimeArgs);
288             testNGDeviceTest.setTests(mTests);
289             testNGDeviceTest.setAbi(mAbi);
290             mDigest = generateDigest(testCaseDir, mJarPath);
291             return testNGDeviceTest;
292         }
293         else {
294             CLog.d("Creating instrumentation test for %s", mName);
295             CtsInstrumentationApkTest instrTest = new CtsInstrumentationApkTest();
296             if (mTimeoutInMins >= 0) {
297                 // as timeout cannot be set for each test,
298                 // increase the time-out of the whole package
299                 CLog.d("Setting new timeout to " + mTimeoutInMins + " mins");
300                 instrTest.setTestTimeout(mTimeoutInMins * 60 * 1000);
301             }
302             return setInstrumentationTest(instrTest, testCaseDir);
303         }
304     }
305 
306     /**
307      * Populates given {@link CtsInstrumentationApkTest} with data from the package xml.
308      *
309      * @param testCaseDir
310      * @param instrTest
311      * @return the populated {@link InstrumentationTest} or <code>null</code>
312      */
setInstrumentationTest(CtsInstrumentationApkTest instrTest, File testCaseDir)313     private InstrumentationTest setInstrumentationTest(CtsInstrumentationApkTest instrTest,
314             File testCaseDir) {
315         instrTest.setRunName(getId());
316         instrTest.setPackageName(mAppNameSpace);
317         instrTest.setRunnerName(mRunner);
318         instrTest.setAbi(mAbi);
319         instrTest.setTestsToRun(mTests, false
320             /* force batch mode off to always run using testFile */);
321         instrTest.setReRunUsingTestFile(true);
322         // mName means 'apk file name' for instrumentation tests
323         instrTest.addInstallApk(String.format("%s.apk", mName), mAppNameSpace);
324         mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName));
325         return instrTest;
326     }
327 
328     /**
329      * Filter the tests to run based on list of included/excluded tests, class and method name.
330      *
331      * @return the filtered collection of tests
332      */
filterTests()333     private Collection<TestIdentifier> filterTests() {
334         mTestFilter.setTestInclusion(mClassName, mMethodName);
335         return mTestFilter.filter(mTests);
336     }
337 
isKnownTestClass(String className)338     boolean isKnownTestClass(String className) {
339         return mTestClasses.contains(className);
340     }
341 
342     /**
343      * Add a {@link TestIdentifier} to the list of tests in this package.
344      *
345      * @param testDef
346      * @param timeout in mins
347      */
addTest(TestIdentifier testDef, int timeout)348     void addTest(TestIdentifier testDef, int timeout) {
349         mTests.add(testDef);
350         mTestClasses.add(testDef.getClassName());
351         mTestInstanceArguments.put(testDef, new LinkedList<Map<String, String>>());
352         // 0 means no timeout, so keep 0 if already is.
353         if ((timeout > mTimeoutInMins) && (mTimeoutInMins != 0)) {
354             mTimeoutInMins = timeout;
355         }
356     }
357 
358     /**
359      * Add a test instance to an existing {@link TestIdentifier}.
360      */
addTestInstance(TestIdentifier testDef, Map<String, String> instanceArguments)361     void addTestInstance(TestIdentifier testDef, Map<String, String> instanceArguments) {
362         if (!mTestInstanceArguments.containsKey(testDef)) {
363             throw new IllegalStateException("test id does not name an existing test");
364         }
365         mTestInstanceArguments.get(testDef).add(instanceArguments);
366     }
367 
368     /**
369      * {@inheritDoc}
370      */
371     @Override
getTests()372     public Collection<TestIdentifier> getTests() {
373         return mTests;
374     }
375 
376     /**
377      * Get the instance argument map for tests.
378      * <p/>
379      * Exposed for unit testing.
380      */
getTestInstanceArguments()381     public Map<TestIdentifier, List<Map<String, String>>> getTestInstanceArguments() {
382         return mTestInstanceArguments;
383     }
384 
385     /**
386      * {@inheritDoc}
387      */
388     @Override
getDigest()389     public String getDigest() {
390         return mDigest;
391     }
392 
393     /**
394      * Generate a sha1sum digest for a file.
395      * <p/>
396      * Exposed for unit testing.
397      *
398      * @param fileDir the directory of the file
399      * @param fileName the name of the file
400      * @return a hex {@link String} of the digest
401      */
generateDigest(File fileDir, String fileName)402     String generateDigest(File fileDir, String fileName) {
403         final String algorithm = "SHA-1";
404         InputStream fileStream = null;
405         DigestInputStream d = null;
406         try {
407             fileStream = getFileStream(fileDir, fileName);
408             MessageDigest md = MessageDigest.getInstance(algorithm);
409             d = new DigestInputStream(fileStream, md);
410             byte[] buffer = new byte[8196];
411             while (d.read(buffer) != -1) {
412             }
413             return toHexString(md.digest());
414         } catch (NoSuchAlgorithmException e) {
415             return algorithm + " not found";
416         } catch (IOException e) {
417             CLog.e(e);
418         } finally {
419             StreamUtil.close(d);
420             StreamUtil.close(fileStream);
421         }
422         return "failed to generate digest";
423     }
424 
425     /**
426      * Retrieve an input stream for given file
427      * <p/>
428      * Exposed so unit tests can mock.
429      */
getFileStream(File fileDir, String fileName)430     InputStream getFileStream(File fileDir, String fileName) throws FileNotFoundException {
431         InputStream fileStream;
432         fileStream = new BufferedInputStream(new FileInputStream(new File(fileDir, fileName)));
433         return fileStream;
434     }
435 
436     /**
437      * Convert the given byte array into a lowercase hex string.
438      *
439      * @param arr The array to convert.
440      * @return The hex encoded string.
441      */
toHexString(byte[] arr)442     private String toHexString(byte[] arr) {
443         StringBuilder buf = new StringBuilder(arr.length * 2);
444         for (byte b : arr) {
445             buf.append(String.format("%02x", b & 0xFF));
446         }
447         return buf.toString();
448     }
449 
450     @Override
compareTo(ITestPackageDef testPackageDef)451     public int compareTo(ITestPackageDef testPackageDef) {
452         return getId().compareTo(testPackageDef.getId());
453     }
454 }
455