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.ddmlib.testrunner.TestIdentifier;
19 
20 import org.kxml2.io.KXmlSerializer;
21 import org.xmlpull.v1.XmlPullParser;
22 import org.xmlpull.v1.XmlPullParserException;
23 
24 import java.io.IOException;
25 import java.util.Collection;
26 import java.util.Deque;
27 import java.util.LinkedHashMap;
28 import java.util.List;
29 import java.util.Map;
30 
31 /**
32  * Data structure that represents a "TestSuite" XML element and its children.
33  */
34 class TestSuite extends AbstractXmlPullParser {
35 
36     static final String TAG = "TestSuite";
37 
38     private String mName;
39 
40     // use linked hash map for predictable iteration order
41     Map<String, TestSuite> mChildSuiteMap = new LinkedHashMap<String, TestSuite>();
42     Map<String, TestCase> mChildTestCaseMap = new LinkedHashMap<String, TestCase>();
43 
44     /**
45      * @param testSuite
46      */
TestSuite(String suiteName)47     public TestSuite(String suiteName) {
48         mName = suiteName;
49     }
50 
TestSuite()51     public TestSuite() {
52     }
53 
54     /**
55      * @return the name of this suite
56      */
getName()57     public String getName() {
58         return mName;
59     }
60 
61     /**
62      * Set the name of this suite
63      */
setName(String name)64     public void setName(String name) {
65         mName = name;
66     }
67 
68     /**
69      * Insert the given test result into this suite.
70      *
71      * @param suiteNames list of remaining suite names for this test
72      * @param testClassName the test class name
73      * @param testName the test method name
74      * @param testResult the {@link TestResult}
75      */
findTest(List<String> suiteNames, String testClassName, String testName, boolean insertIfMissing)76     public Test findTest(List<String> suiteNames, String testClassName, String testName,
77             boolean insertIfMissing) {
78         if (suiteNames.size() <= 0) {
79             // no more package segments
80             TestCase testCase = getTestCase(testClassName);
81             return testCase.findTest(testName, insertIfMissing);
82         } else {
83             String rootName = suiteNames.remove(0);
84             TestSuite suite = getTestSuite(rootName);
85             return suite.findTest(suiteNames, testClassName, testName, insertIfMissing);
86         }
87     }
88 
89     /**
90      * Gets all the child {@link TestSuite}s
91      */
getTestSuites()92     public Collection<TestSuite> getTestSuites() {
93         return mChildSuiteMap.values();
94     }
95 
96     /**
97      * Gets all the child {@link TestCase}s
98      */
getTestCases()99     public Collection<TestCase> getTestCases() {
100         return mChildTestCaseMap.values();
101     }
102 
103     /**
104      * Get the child {@link TestSuite} with given name, creating if necessary.
105      *
106      * @param suiteName
107      * @return the {@link TestSuite}
108      */
getTestSuite(String suiteName)109     private TestSuite getTestSuite(String suiteName) {
110         TestSuite testSuite = mChildSuiteMap.get(suiteName);
111         if (testSuite == null) {
112             testSuite = new TestSuite(suiteName);
113             mChildSuiteMap.put(suiteName, testSuite);
114         }
115         return testSuite;
116     }
117 
118     /**
119      * Get the child {@link TestCase} with given name, creating if necessary.
120      * @param testCaseName
121      * @return
122      */
getTestCase(String testCaseName)123     private TestCase getTestCase(String testCaseName) {
124         TestCase testCase = mChildTestCaseMap.get(testCaseName);
125         if (testCase == null) {
126             testCase = new TestCase(testCaseName);
127             mChildTestCaseMap.put(testCaseName, testCase);
128         }
129         return testCase;
130     }
131 
132     /**
133      * Serialize this object and all its contents to XML.
134      *
135      * @param serializer
136      * @throws IOException
137      */
serialize(KXmlSerializer serializer)138     public void serialize(KXmlSerializer serializer) throws IOException {
139         if (mName != null) {
140             serializer.startTag(CtsXmlResultReporter.ns, TAG);
141             serializer.attribute(CtsXmlResultReporter.ns, "name", mName);
142         }
143         for (TestSuite childSuite : mChildSuiteMap.values()) {
144             childSuite.serialize(serializer);
145         }
146         for (TestCase childCase : mChildTestCaseMap.values()) {
147             childCase.serialize(serializer);
148         }
149         if (mName != null) {
150             serializer.endTag(CtsXmlResultReporter.ns, TAG);
151         }
152     }
153 
154     /**
155      * Populates this class with suite result data parsed from XML.
156      *
157      * @param parser the {@link XmlPullParser}. Expected to be pointing at start
158      *            of a TestSuite tag
159      */
160     @Override
parse(XmlPullParser parser)161     void parse(XmlPullParser parser) throws XmlPullParserException, IOException {
162         if (!parser.getName().equals(TAG)) {
163             throw new XmlPullParserException(String.format(
164                     "invalid XML: Expected %s tag but received %s", TAG, parser.getName()));
165         }
166         setName(getAttribute(parser, "name"));
167         int eventType = parser.next();
168         while (eventType != XmlPullParser.END_DOCUMENT) {
169             if (eventType == XmlPullParser.START_TAG && parser.getName().equals(TestSuite.TAG)) {
170                 TestSuite suite = new TestSuite();
171                 suite.parse(parser);
172                 insertSuite(suite);
173             } else if (eventType == XmlPullParser.START_TAG && parser.getName().equals(
174                     TestCase.TAG)) {
175                 TestCase testCase = new TestCase();
176                 testCase.parse(parser);
177                 insertTestCase(testCase);
178             } else if (eventType == XmlPullParser.END_TAG && parser.getName().equals(TAG)) {
179                 return;
180             }
181             eventType = parser.next();
182         }
183     }
184 
185     /**
186      * Adds a child {@link TestCase}.
187      */
insertTestCase(TestCase testCase)188     public void insertTestCase(TestCase testCase) {
189         mChildTestCaseMap.put(testCase.getName(), testCase);
190     }
191 
192     /**
193      * Adds a child {@link TestSuite}.
194      */
insertSuite(TestSuite suite)195     public void insertSuite(TestSuite suite) {
196         mChildSuiteMap.put(suite.getName(), suite);
197     }
198 
199 
200     /**
201      * Adds tests contained in this result that have the given <var>resultFilter</var>
202      *
203      * @param tests the {@link Collection} of {@link TestIdentifier}s to add to
204      * @param parentSuiteNames a {@link Deque} of parent suite names. Used to construct the full
205      * class name of the test
206      * @param resultFilter the {@link CtsTestStatus} to filter by
207      */
addTestsWithStatus(Collection<TestIdentifier> tests, Deque<String> parentSuiteNames, CtsTestStatus resultFilter)208     void addTestsWithStatus(Collection<TestIdentifier> tests, Deque<String> parentSuiteNames,
209             CtsTestStatus resultFilter) {
210         if (getName() != null) {
211             parentSuiteNames.addLast(getName());
212         }
213         for (TestSuite suite : mChildSuiteMap.values()) {
214             suite.addTestsWithStatus(tests, parentSuiteNames, resultFilter);
215         }
216         for (TestCase testCase : mChildTestCaseMap.values()) {
217             testCase.addTestsWithStatus(tests, parentSuiteNames, resultFilter);
218         }
219         if (getName() != null) {
220             parentSuiteNames.removeLast();
221         }
222     }
223 
224     /**
225      * Count the number of tests in this {@link TestSuite} with given status.
226      *
227      * @param status
228      * @return the test count
229      */
countTests(CtsTestStatus status)230     public int countTests(CtsTestStatus status) {
231         int total = 0;
232         for (TestSuite suite : mChildSuiteMap.values()) {
233             total += suite.countTests(status);
234         }
235         for (TestCase testCase : mChildTestCaseMap.values()) {
236             total += testCase.countTests(status);
237         }
238         return total;
239     }
240 }
241