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