1 /*
2  * Copyright (C) 2011 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.tradefed.result;
17 
18 import com.android.compatibility.common.util.AbiUtils;
19 import com.android.compatibility.common.util.MetricsStore;
20 import com.android.compatibility.common.util.ReportLog;
21 import com.android.cts.tradefed.testtype.CtsTest;
22 import com.android.ddmlib.testrunner.TestIdentifier;
23 import com.android.tradefed.log.LogUtil.CLog;
24 
25 import org.kxml2.io.KXmlSerializer;
26 import org.xmlpull.v1.XmlPullParser;
27 import org.xmlpull.v1.XmlPullParserException;
28 
29 import java.io.IOException;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Deque;
33 import java.util.HashMap;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.Map;
37 
38 /**
39  * Data structure for a CTS test package result.
40  * <p/>
41  * Provides methods to serialize to XML.
42  */
43 class TestPackageResult extends AbstractXmlPullParser {
44 
45     static final String TAG = "TestPackage";
46 
47     public static final String RESULT_KEY = "COMPATIBILITY_TEST_RESULT";
48 
49     private static final String DIGEST_ATTR = "digest";
50     private static final String APP_PACKAGE_NAME_ATTR = "appPackageName";
51     private static final String NAME_ATTR = "name";
52     private static final String ABI_ATTR = "abi";
53     private static final String ns = CtsXmlResultReporter.ns;
54     private static final String SIGNATURE_TEST_PKG = "android.tests.sigtest";
55 
56     private String mDeviceSerial;
57     private String mAppPackageName;
58     private String mName;
59     private String mAbi;
60     private String mDigest;
61 
62     private Map<String, String> mMetrics = new HashMap<String, String>();
63     private Map<TestIdentifier, Map<String, String>> mTestMetrics = new HashMap<TestIdentifier, Map<String, String>>();
64 
65     private TestSuite mSuiteRoot = new TestSuite(null);
66 
setDeviceSerial(String deviceSerial)67     public void setDeviceSerial(String deviceSerial) {
68         mDeviceSerial = deviceSerial;
69     }
70 
getDeviceSerial()71     public String getDeviceSerial() {
72         return mDeviceSerial;
73     }
74 
getId()75     public String getId() {
76         return AbiUtils.createId(getAbi(), getAppPackageName());
77     }
78 
setAppPackageName(String appPackageName)79     public void setAppPackageName(String appPackageName) {
80         mAppPackageName = appPackageName;
81     }
82 
getAppPackageName()83     public String getAppPackageName() {
84         return mAppPackageName;
85     }
86 
setName(String name)87     public void setName(String name) {
88         mName = name;
89     }
90 
getName()91     public String getName() {
92         return mName;
93     }
94 
setAbi(String abi)95     public void setAbi(String abi) {
96         mAbi = abi;
97     }
98 
getAbi()99     public String getAbi() {
100         return mAbi;
101     }
102 
setDigest(String digest)103     public void setDigest(String digest) {
104         mDigest = digest;
105     }
106 
getDigest()107     public String getDigest() {
108         return mDigest;
109     }
110 
111     /**
112      * Return the {@link TestSuite}s
113      */
getTestSuites()114     public Collection<TestSuite> getTestSuites() {
115         return mSuiteRoot.getTestSuites();
116     }
117 
118     /**
119      * Adds a test result to this test package
120      *
121      * @param testId
122      */
insertTest(TestIdentifier testId)123     public Test insertTest(TestIdentifier testId) {
124         return findTest(testId, true);
125     }
126 
findTest(TestIdentifier testId, boolean insertIfMissing)127     private Test findTest(TestIdentifier testId, boolean insertIfMissing) {
128         List<String> classNameSegments = new LinkedList<String>();
129         Collections.addAll(classNameSegments, testId.getClassName().split("\\."));
130         if (classNameSegments.size() <= 0) {
131             CLog.e("Unrecognized package name format for test class '%s'",
132                     testId.getClassName());
133             // should never happen
134             classNameSegments.add("UnknownTestClass");
135         }
136         String testCaseName = classNameSegments.remove(classNameSegments.size() - 1);
137         return mSuiteRoot.findTest(classNameSegments, testCaseName, testId.getTestName(), insertIfMissing);
138     }
139 
140 
141     /**
142      * Find the test result for given {@link TestIdentifier}.
143      * @param testId
144      * @return the {@link Test} or <code>null</code>
145      */
findTest(TestIdentifier testId)146     public Test findTest(TestIdentifier testId) {
147         return findTest(testId, false);
148     }
149 
150     /**
151      * Serialize this object and all its contents to XML.
152      *
153      * @param serializer
154      * @throws IOException
155      */
serialize(KXmlSerializer serializer)156     public void serialize(KXmlSerializer serializer) throws IOException {
157         serializer.startTag(ns, TAG);
158         serializeAttribute(serializer, NAME_ATTR, mName);
159         serializeAttribute(serializer, APP_PACKAGE_NAME_ATTR, mAppPackageName);
160         serializeAttribute(serializer, ABI_ATTR, mAbi);
161         serializeAttribute(serializer, DIGEST_ATTR, getDigest());
162         if (SIGNATURE_TEST_PKG.equals(mName)) {
163             serializer.attribute(ns, "signatureCheck", "true");
164         }
165         mSuiteRoot.serialize(serializer);
166         serializer.endTag(ns, TAG);
167     }
168 
169     /**
170      * Helper method to serialize attributes.
171      * Can handle null values. Useful for cases where test package has not been fully populated
172      * such as when unit testing.
173      *
174      * @param attrName
175      * @param attrValue
176      * @throws IOException
177      */
serializeAttribute(KXmlSerializer serializer, String attrName, String attrValue)178     private void serializeAttribute(KXmlSerializer serializer, String attrName, String attrValue)
179             throws IOException {
180         attrValue = attrValue == null ? "" : attrValue;
181         serializer.attribute(ns, attrName, attrValue);
182     }
183 
184     /**
185      * Populates this class with package result data parsed from XML.
186      *
187      * @param parser the {@link XmlPullParser}. Expected to be pointing at start
188      *            of TestPackage tag
189      */
190     @Override
parse(XmlPullParser parser)191     void parse(XmlPullParser parser) throws XmlPullParserException, IOException {
192         if (!parser.getName().equals(TAG)) {
193             throw new XmlPullParserException(String.format(
194                     "invalid XML: Expected %s tag but received %s", TAG, parser.getName()));
195         }
196         setAppPackageName(getAttribute(parser, APP_PACKAGE_NAME_ATTR));
197         setName(getAttribute(parser, NAME_ATTR));
198         setAbi(getAttribute(parser, ABI_ATTR));
199         setDigest(getAttribute(parser, DIGEST_ATTR));
200         int eventType = parser.getEventType();
201         while (eventType != XmlPullParser.END_DOCUMENT) {
202             if (eventType == XmlPullParser.START_TAG && parser.getName().equals(TestSuite.TAG)) {
203                 TestSuite suite = new TestSuite();
204                 suite.parse(parser);
205                 mSuiteRoot.insertSuite(suite);
206             }
207             if (eventType == XmlPullParser.END_TAG && parser.getName().equals(TAG)) {
208                 return;
209             }
210             eventType = parser.next();
211         }
212     }
213 
214     /**
215      * Return a list of {@link TestIdentifier}s contained in this result with the given status
216      *
217      * @param resultFilter the {@link CtsTestStatus} to filter by
218      * @return a collection of {@link TestIdentifier}s
219      */
getTestsWithStatus(CtsTestStatus resultFilter)220     public Collection<TestIdentifier> getTestsWithStatus(CtsTestStatus resultFilter) {
221         Collection<TestIdentifier> tests = new LinkedList<TestIdentifier>();
222         Deque<String> suiteNames = new LinkedList<String>();
223         mSuiteRoot.addTestsWithStatus(tests, suiteNames, resultFilter);
224         return tests;
225     }
226 
227     /**
228      * Populate values in this package result from run metrics
229      * @param metrics A map of metrics from the completed test run.
230      */
populateMetrics(Map<String, String> metrics)231     public void populateMetrics(Map<String, String> metrics) {
232         String name = metrics.get(CtsTest.PACKAGE_NAME_METRIC);
233         if (name != null) {
234             setName(name);
235         }
236         String abi = metrics.get(CtsTest.PACKAGE_ABI_METRIC);
237         if (abi != null) {
238             setAbi(abi);
239         }
240         String digest = metrics.get(CtsTest.PACKAGE_DIGEST_METRIC);
241         if (digest != null) {
242             setDigest(digest);
243         }
244         mMetrics.putAll(metrics);
245 
246         // Collect performance results
247         for (TestIdentifier test : mTestMetrics.keySet()) {
248             // device test can have performance results in test metrics
249             String perfResult = mTestMetrics.get(test).get(RESULT_KEY);
250             ReportLog report = null;
251             if (perfResult != null) {
252                 try {
253                     report = ReportLog.parse(perfResult);
254                 } catch (XmlPullParserException | IOException e) {
255                     e.printStackTrace();
256                 }
257             }
258             Test result = findTest(test);
259             if (report != null && !result.getResult().equals(CtsTestStatus.FAIL)) {
260                 result.setResultStatus(CtsTestStatus.PASS);
261                 result.setReportLog(report);
262             }
263         }
264     }
265 
266     /**
267      * Report the given test as a failure.
268      *
269      * @param test
270      * @param status
271      * @param trace
272      */
reportTestFailure(TestIdentifier test, CtsTestStatus status, String trace)273     public void reportTestFailure(TestIdentifier test, CtsTestStatus status, String trace) {
274         Test result = findTest(test);
275         result.setResultStatus(status);
276         result.setStackTrace(trace);
277     }
278 
279     /**
280      * Report that the given test has completed.
281      *
282      * @param test The {@link TestIdentifier} of the completed test.
283      * @param testMetrics A map holding metrics about the completed test, if any.
284      */
reportTestEnded(TestIdentifier test, Map<String, String> testMetrics)285     public void reportTestEnded(TestIdentifier test, Map<String, String> testMetrics) {
286         Test result = findTest(test);
287         if (!result.getResult().equals(CtsTestStatus.FAIL)) {
288             result.setResultStatus(CtsTestStatus.PASS);
289         }
290         result.updateEndTime();
291         if (mTestMetrics.containsKey(test)) {
292             CLog.e("Test metrics already contains key: " + test);
293         }
294         mTestMetrics.put(test, testMetrics);
295         CLog.i("Test metrics:" + testMetrics);
296     }
297 
298     /**
299      * Return the number of tests with given status
300      *
301      * @param status
302      * @return the total number of tests with given status
303      */
countTests(CtsTestStatus status)304     public int countTests(CtsTestStatus status) {
305         return mSuiteRoot.countTests(status);
306     }
307 
308     /**
309      * @return A map holding the metrics from the test run.
310      */
getMetrics()311     public Map<String, String> getMetrics() {
312         return mMetrics;
313     }
314 
315 }
316