/* * Copyright (C) 2011 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.result; import com.android.cts.tradefed.testtype.CtsTest; import com.android.cts.tradefed.util.CtsHostStore; import com.android.cts.util.AbiUtils; import com.android.ddmlib.testrunner.TestIdentifier; import com.android.tradefed.log.LogUtil.CLog; import org.kxml2.io.KXmlSerializer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Data structure for a CTS test package result. *

* Provides methods to serialize to XML. */ class TestPackageResult extends AbstractXmlPullParser { static final String TAG = "TestPackage"; public static final String CTS_RESULT_KEY = "CTS_TEST_RESULT"; private static final String DIGEST_ATTR = "digest"; private static final String APP_PACKAGE_NAME_ATTR = "appPackageName"; private static final String NAME_ATTR = "name"; private static final String ABI_ATTR = "abi"; private static final String ns = CtsXmlResultReporter.ns; private static final String SIGNATURE_TEST_PKG = "android.tests.sigtest"; private static final Pattern mCtsLogPattern = Pattern.compile("(.*)\\+\\+\\+\\+(.*)"); private String mDeviceSerial; private String mAppPackageName; private String mName; private String mAbi; private String mDigest; private Map mMetrics = new HashMap(); private Map> mTestMetrics = new HashMap>(); private TestSuite mSuiteRoot = new TestSuite(null); public void setDeviceSerial(String deviceSerial) { mDeviceSerial = deviceSerial; } public String getDeviceSerial() { return mDeviceSerial; } public String getId() { return AbiUtils.createId(getAbi(), getAppPackageName()); } public void setAppPackageName(String appPackageName) { mAppPackageName = appPackageName; } public String getAppPackageName() { return mAppPackageName; } public void setName(String name) { mName = name; } public String getName() { return mName; } public void setAbi(String abi) { mAbi = abi; } public String getAbi() { return mAbi; } public void setDigest(String digest) { mDigest = digest; } public String getDigest() { return mDigest; } /** * Return the {@link TestSuite}s */ public Collection getTestSuites() { return mSuiteRoot.getTestSuites(); } /** * Adds a test result to this test package * * @param testId */ public Test insertTest(TestIdentifier testId) { return findTest(testId, true); } private Test findTest(TestIdentifier testId, boolean insertIfMissing) { List classNameSegments = new LinkedList(); Collections.addAll(classNameSegments, testId.getClassName().split("\\.")); if (classNameSegments.size() <= 0) { CLog.e("Unrecognized package name format for test class '%s'", testId.getClassName()); // should never happen classNameSegments.add("UnknownTestClass"); } String testCaseName = classNameSegments.remove(classNameSegments.size() - 1); return mSuiteRoot.findTest(classNameSegments, testCaseName, testId.getTestName(), insertIfMissing); } /** * Find the test result for given {@link TestIdentifier}. * @param testId * @return the {@link Test} or null */ public Test findTest(TestIdentifier testId) { return findTest(testId, false); } /** * Serialize this object and all its contents to XML. * * @param serializer * @throws IOException */ public void serialize(KXmlSerializer serializer) throws IOException { serializer.startTag(ns, TAG); serializeAttribute(serializer, NAME_ATTR, mName); serializeAttribute(serializer, APP_PACKAGE_NAME_ATTR, mAppPackageName); serializeAttribute(serializer, ABI_ATTR, mAbi); serializeAttribute(serializer, DIGEST_ATTR, getDigest()); if (SIGNATURE_TEST_PKG.equals(mName)) { serializer.attribute(ns, "signatureCheck", "true"); } mSuiteRoot.serialize(serializer); serializer.endTag(ns, TAG); } /** * Helper method to serialize attributes. * Can handle null values. Useful for cases where test package has not been fully populated * such as when unit testing. * * @param attrName * @param attrValue * @throws IOException */ private void serializeAttribute(KXmlSerializer serializer, String attrName, String attrValue) throws IOException { attrValue = attrValue == null ? "" : attrValue; serializer.attribute(ns, attrName, attrValue); } /** * Populates this class with package result data parsed from XML. * * @param parser the {@link XmlPullParser}. Expected to be pointing at start * of TestPackage tag */ @Override void parse(XmlPullParser parser) throws XmlPullParserException, IOException { if (!parser.getName().equals(TAG)) { throw new XmlPullParserException(String.format( "invalid XML: Expected %s tag but received %s", TAG, parser.getName())); } setAppPackageName(getAttribute(parser, APP_PACKAGE_NAME_ATTR)); setName(getAttribute(parser, NAME_ATTR)); setAbi(getAttribute(parser, ABI_ATTR)); setDigest(getAttribute(parser, DIGEST_ATTR)); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG && parser.getName().equals(TestSuite.TAG)) { TestSuite suite = new TestSuite(); suite.parse(parser); mSuiteRoot.insertSuite(suite); } if (eventType == XmlPullParser.END_TAG && parser.getName().equals(TAG)) { return; } eventType = parser.next(); } } /** * Return a list of {@link TestIdentifier}s contained in this result with the given status * * @param resultFilter the {@link CtsTestStatus} to filter by * @return a collection of {@link TestIdentifier}s */ public Collection getTestsWithStatus(CtsTestStatus resultFilter) { Collection tests = new LinkedList(); Deque suiteNames = new LinkedList(); mSuiteRoot.addTestsWithStatus(tests, suiteNames, resultFilter); return tests; } /** * Populate values in this package result from run metrics * @param metrics A map of metrics from the completed test run. */ public void populateMetrics(Map metrics) { String name = metrics.get(CtsTest.PACKAGE_NAME_METRIC); if (name != null) { setName(name); } String abi = metrics.get(CtsTest.PACKAGE_ABI_METRIC); if (abi != null) { setAbi(abi); } String digest = metrics.get(CtsTest.PACKAGE_DIGEST_METRIC); if (digest != null) { setDigest(digest); } mMetrics.putAll(metrics); // Collect performance results for (TestIdentifier test : mTestMetrics.keySet()) { // device test can have performance results in test metrics String perfResult = mTestMetrics.get(test).get(CTS_RESULT_KEY); // host test should be checked in CtsHostStore. if (perfResult == null) { perfResult = CtsHostStore.removeCtsResult(mDeviceSerial, mAbi, test.toString()); } if (perfResult != null) { // CTS result is passed in Summary++++Details format. // Extract Summary and Details, and pass them. Matcher m = mCtsLogPattern.matcher(perfResult); if (m.find()) { Test result = findTest(test); result.setResultStatus(CtsTestStatus.PASS); result.setSummary(m.group(1)); result.setDetails(m.group(2)); } else { CLog.e("CTS Result unrecognizable:" + perfResult); } } } } /** * Report the given test as a failure. * * @param test * @param status * @param trace */ public void reportTestFailure(TestIdentifier test, CtsTestStatus status, String trace) { Test result = findTest(test); result.setResultStatus(status); result.setStackTrace(trace); } /** * Report that the given test has completed. * * @param test The {@link TestIdentifier} of the completed test. * @param testMetrics A map holding metrics about the completed test, if any. */ public void reportTestEnded(TestIdentifier test, Map testMetrics) { Test result = findTest(test); if (!result.getResult().equals(CtsTestStatus.FAIL)) { result.setResultStatus(CtsTestStatus.PASS); } result.updateEndTime(); if (mTestMetrics.containsKey(test)) { CLog.e("Test metrics already contains key: " + test); } mTestMetrics.put(test, testMetrics); CLog.i("Test metrics:" + testMetrics); } /** * Return the number of tests with given status * * @param status * @return the total number of tests with given status */ public int countTests(CtsTestStatus status) { return mSuiteRoot.countTests(status); } /** * @return A map holding the metrics from the test run. */ public Map getMetrics() { return mMetrics; } }