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