Home | History | Annotate | Download | only in result
      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 android.tests.getinfo.DeviceInfoConstants;
     19 
     20 import com.android.cts.tradefed.device.DeviceInfoCollector;
     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.HashSet;
     35 import java.util.LinkedList;
     36 import java.util.List;
     37 import java.util.Map;
     38 import java.util.Set;
     39 
     40 /**
     41  * Data structure for a CTS test package result.
     42  * <p/>
     43  * Provides methods to serialize to XML.
     44  */
     45 class TestPackageResult  extends AbstractXmlPullParser {
     46 
     47     static final String TAG = "TestPackage";
     48     private static final String DIGEST_ATTR = "digest";
     49     private static final String APP_PACKAGE_NAME_ATTR = "appPackageName";
     50     private static final String NAME_ATTR = "name";
     51     private static final String ns = CtsXmlResultReporter.ns;
     52     private static final String SIGNATURE_TEST_PKG = "android.tests.sigtest";
     53 
     54     private String mAppPackageName;
     55     private String mName;
     56     private String mDigest;
     57 
     58     private Map<String, String> mMetrics = new HashMap<String, String>();
     59 
     60     private TestSuite mSuiteRoot = new TestSuite(null);
     61 
     62     public void setAppPackageName(String appPackageName) {
     63         mAppPackageName = appPackageName;
     64     }
     65 
     66     public String getAppPackageName() {
     67         return mAppPackageName;
     68     }
     69 
     70     public void setName(String name) {
     71         mName = name;
     72     }
     73 
     74     public String getName() {
     75         return mName;
     76     }
     77 
     78     public void setDigest(String digest) {
     79         mDigest = digest;
     80     }
     81 
     82     public String getDigest() {
     83         return mDigest;
     84     }
     85 
     86     /**
     87      * Return the {@link TestSuite}s
     88      */
     89     public Collection<TestSuite> getTestSuites() {
     90         return mSuiteRoot.getTestSuites();
     91     }
     92 
     93     /**
     94      * Adds a test result to this test package
     95      *
     96      * @param testId
     97      * @param testResult
     98      */
     99     public Test insertTest(TestIdentifier testId) {
    100         return findTest(testId, true);
    101     }
    102 
    103     private Test findTest(TestIdentifier testId, boolean insertIfMissing) {
    104         List<String> classNameSegments = new LinkedList<String>();
    105         Collections.addAll(classNameSegments, testId.getClassName().split("\\."));
    106         if (classNameSegments.size() <= 0) {
    107             CLog.e("Unrecognized package name format for test class '%s'",
    108                     testId.getClassName());
    109             // should never happen
    110             classNameSegments.add("UnknownTestClass");
    111         }
    112             String testCaseName = classNameSegments.remove(classNameSegments.size()-1);
    113             return mSuiteRoot.findTest(classNameSegments, testCaseName, testId.getTestName(), insertIfMissing);
    114     }
    115 
    116 
    117     /**
    118      * Find the test result for given {@link TestIdentifier}.
    119      * @param testId
    120      * @return the {@link Test} or <code>null</code>
    121      */
    122     public Test findTest(TestIdentifier testId) {
    123         return findTest(testId, false);
    124     }
    125 
    126     /**
    127      * Serialize this object and all its contents to XML.
    128      *
    129      * @param serializer
    130      * @throws IOException
    131      */
    132     public void serialize(KXmlSerializer serializer) throws IOException {
    133         serializer.startTag(ns, TAG);
    134         serializeAttribute(serializer, NAME_ATTR, mName);
    135         serializeAttribute(serializer, APP_PACKAGE_NAME_ATTR, mAppPackageName);
    136         serializeAttribute(serializer, DIGEST_ATTR, getDigest());
    137         if (SIGNATURE_TEST_PKG.equals(mName)) {
    138             serializer.attribute(ns, "signatureCheck", "true");
    139         }
    140         mSuiteRoot.serialize(serializer);
    141         serializer.endTag(ns, TAG);
    142     }
    143 
    144     /**
    145      * Helper method to serialize attributes.
    146      * Can handle null values. Useful for cases where test package has not been fully populated
    147      * such as when unit testing.
    148      *
    149      * @param attrName
    150      * @param attrValue
    151      * @throws IOException
    152      */
    153     private void serializeAttribute(KXmlSerializer serializer, String attrName, String attrValue)
    154             throws IOException {
    155         attrValue = attrValue == null ? "" : attrValue;
    156         serializer.attribute(ns, attrName, attrValue);
    157     }
    158 
    159     /**
    160      * Populates this class with package result data parsed from XML.
    161      *
    162      * @param parser the {@link XmlPullParser}. Expected to be pointing at start
    163      *            of TestPackage tag
    164      */
    165     @Override
    166     void parse(XmlPullParser parser) throws XmlPullParserException, IOException {
    167         if (!parser.getName().equals(TAG)) {
    168             throw new XmlPullParserException(String.format(
    169                     "invalid XML: Expected %s tag but received %s", TAG, parser.getName()));
    170         }
    171         setAppPackageName(getAttribute(parser, APP_PACKAGE_NAME_ATTR));
    172         setName(getAttribute(parser, NAME_ATTR));
    173         setDigest(getAttribute(parser, DIGEST_ATTR));
    174         int eventType = parser.getEventType();
    175         while (eventType != XmlPullParser.END_DOCUMENT) {
    176             if (eventType == XmlPullParser.START_TAG && parser.getName().equals(TestSuite.TAG)) {
    177                 TestSuite suite = new TestSuite();
    178                 suite.parse(parser);
    179                 mSuiteRoot.insertSuite(suite);
    180             }
    181             if (eventType == XmlPullParser.END_TAG && parser.getName().equals(TAG)) {
    182                 return;
    183             }
    184             eventType = parser.next();
    185         }
    186     }
    187 
    188     /**
    189      * Return a list of {@link TestIdentifer}s contained in this result with the given status
    190      *
    191      * @param resultFilter the {@link CtsTestStatus} to filter by
    192      * @return a collection of {@link TestIdentifer}s
    193      */
    194     public Collection<TestIdentifier> getTestsWithStatus(CtsTestStatus resultFilter) {
    195         Collection<TestIdentifier> tests = new LinkedList<TestIdentifier>();
    196         Deque<String> suiteNames = new LinkedList<String>();
    197         mSuiteRoot.addTestsWithStatus(tests, suiteNames, resultFilter);
    198         return tests;
    199     }
    200 
    201     /**
    202      * Populate values in this package result from run metrics
    203      * @param runResult
    204      */
    205     public void populateMetrics(Map<String, String> metrics) {
    206         String name = metrics.get(CtsTest.PACKAGE_NAME_METRIC);
    207         if (name != null) {
    208             setName(name);
    209         }
    210         String digest = metrics.get(CtsTest.PACKAGE_DIGEST_METRIC);
    211         if (digest != null) {
    212             setDigest(digest);
    213         }
    214         if (DeviceInfoCollector.APP_PACKAGE_NAME.equals(getAppPackageName())) {
    215             storeDeviceMetrics(metrics);
    216         } else {
    217             mMetrics.putAll(metrics);
    218         }
    219     }
    220 
    221     /**
    222      * Check that the provided device info metrics are consistent with the currently stored metrics.
    223      * <p/>
    224      * If any inconsistencies occur, logs errors and stores error messages in the metrics map
    225      *
    226      * @param metrics
    227      */
    228     private void storeDeviceMetrics(Map<String, String> metrics) {
    229         // TODO centralize all the device metrics handling into a single class
    230         if (mMetrics.isEmpty()) {
    231             // nothing to check!
    232             mMetrics.putAll(metrics);
    233             return;
    234         }
    235         // ensure all the metrics we expect to be identical actually are
    236         checkMetrics(metrics, DeviceInfoConstants.BUILD_FINGERPRINT,
    237                 DeviceInfoConstants.BUILD_MODEL, DeviceInfoConstants.BUILD_BRAND,
    238                 DeviceInfoConstants.BUILD_MANUFACTURER, DeviceInfoConstants.BUILD_BOARD,
    239                 DeviceInfoConstants.BUILD_DEVICE, DeviceInfoConstants.PRODUCT_NAME,
    240                 DeviceInfoConstants.BUILD_ABI, DeviceInfoConstants.BUILD_ABI2,
    241                 DeviceInfoConstants.SCREEN_SIZE);
    242     }
    243 
    244     private void checkMetrics(Map<String, String> metrics, String... keysToCheck) {
    245         Set<String> keyCheckSet = new HashSet<String>();
    246         Collections.addAll(keyCheckSet, keysToCheck);
    247         for (Map.Entry<String, String> metricEntry : metrics.entrySet()) {
    248             String currentValue = mMetrics.get(metricEntry.getKey());
    249             if (keyCheckSet.contains(metricEntry.getKey()) && currentValue != null
    250                     && !metricEntry.getValue().equals(currentValue)) {
    251                 CLog.e("Inconsistent info collected from devices. "
    252                         + "Current result has %s='%s', Received '%s'. Are you sharding or " +
    253                         "resuming a test run across different devices and/or builds?",
    254                         metricEntry.getKey(), currentValue, metricEntry.getValue());
    255                 mMetrics.put(metricEntry.getKey(),
    256                         String.format("ERROR: Inconsistent results: %s, %s",
    257                                 metricEntry.getValue(), currentValue));
    258             } else {
    259                 mMetrics.put(metricEntry.getKey(), metricEntry.getValue());
    260             }
    261         }
    262     }
    263 
    264     /**
    265      * Report the given test as a failure.
    266      *
    267      * @param test
    268      * @param status
    269      * @param trace
    270      */
    271     public void reportTestFailure(TestIdentifier test, CtsTestStatus status, String trace) {
    272         Test result = findTest(test);
    273         result.setResultStatus(status);
    274         result.setStackTrace(trace);
    275     }
    276 
    277     /**
    278      * Report that the given test has completed.
    279      *
    280      * @param test
    281      */
    282     public void reportTestEnded(TestIdentifier test) {
    283         Test result = findTest(test);
    284         if (!result.getResult().equals(CtsTestStatus.FAIL)) {
    285             result.setResultStatus(CtsTestStatus.PASS);
    286         }
    287         result.updateEndTime();
    288     }
    289 
    290     /**
    291      * Return the number of tests with given status
    292      *
    293      * @param status
    294      * @return the total number of tests with given status
    295      */
    296     public int countTests(CtsTestStatus status) {
    297         return mSuiteRoot.countTests(status);
    298     }
    299 
    300     /**
    301      * @return
    302      */
    303     public Map<String, String> getMetrics() {
    304         return mMetrics;
    305     }
    306 }
    307