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