1 /*
2  * Copyright (C) 2010 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.testtype;
17 
18 import com.android.compatibility.common.util.AbiUtils;
19 import com.android.ddmlib.Log;
20 import com.android.ddmlib.testrunner.TestIdentifier;
21 import com.android.tradefed.util.xml.AbstractXmlParser;
22 
23 import org.xml.sax.Attributes;
24 import org.xml.sax.helpers.DefaultHandler;
25 
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.Stack;
32 
33 /**
34  * Parser for CTS test case XML.
35  * <p/>
36  * Dumb parser that just retrieves data from in the test case xml and stuff it into a
37  * {@link TestPackageDef}. Currently performs limited error checking.
38  */
39 public class TestPackageXmlParser extends AbstractXmlParser {
40 
41     private static final String LOG_TAG = "TestPackageXmlParser";
42 
43     private final boolean mIncludeKnownFailures;
44 
45     private Map<String, TestPackageDef> mPackageDefs = new HashMap<String, TestPackageDef>();
46 
47     /**
48      * @param includeKnownFailures Whether to run tests which are known to fail.
49      */
TestPackageXmlParser(boolean includeKnownFailures)50     public TestPackageXmlParser(boolean includeKnownFailures) {
51         mIncludeKnownFailures = includeKnownFailures;
52     }
53 
54     /**
55      * SAX callback object. Handles parsing data from the xml tags.
56      * <p/>
57      * Expected structure:
58      * <TestPackage>
59      *     <TestSuite ...>
60      *        <TestCase>
61      *           <Test>
62      *             <TestInstance> (optional)
63      */
64     private class TestPackageHandler extends DefaultHandler {
65 
66         private static final String TEST_PACKAGE_TAG = "TestPackage";
67         private static final String TEST_SUITE_TAG = "TestSuite";
68         private static final String TEST_CASE_TAG = "TestCase";
69         private static final String TEST_TAG = "Test";
70         private static final String TEST_INSTANCE_TAG = "TestInstance";
71 
72         // holds current class name segments
73         private Stack<String> mClassNameStack = new Stack<String>();
74         private TestIdentifier mTestId;
75 
76         @Override
startElement(String uri, String localName, String name, Attributes attributes)77         public void startElement(String uri, String localName, String name, Attributes attributes) {
78             if (TEST_PACKAGE_TAG.equals(localName)) {
79                 final String appPackageName = attributes.getValue("appPackageName");
80                 final String testPackageNameSpace = attributes.getValue("appNameSpace");
81                 final String packageName = attributes.getValue("name");
82                 final String runnerName = attributes.getValue("runner");
83                 final String jarPath = attributes.getValue("jarPath");
84                 final String javaPackageFilter = attributes.getValue("javaPackageFilter");
85                 final String targetBinaryName = attributes.getValue("targetBinaryName");
86                 final String targetNameSpace = attributes.getValue("targetNameSpace");
87                 final String runTimeArgs = attributes.getValue("runtimeArgs");
88                 final String testType = getTestType(attributes);
89 
90                 for (String abiName : AbiUtils.getAbisSupportedByCompatibility()) {
91                     Abi abi = new Abi(abiName, AbiUtils.getBitness(abiName));
92                     TestPackageDef packageDef = new TestPackageDef();
93                     packageDef.setAppPackageName(appPackageName);
94                     packageDef.setAppNameSpace(testPackageNameSpace);
95                     packageDef.setName(packageName);
96                     packageDef.setRunner(runnerName);
97                     packageDef.setTestType(testType);
98                     packageDef.setJarPath(jarPath);
99                     packageDef.setRunTimeArgs(runTimeArgs);
100                     if (!"".equals(javaPackageFilter)) {
101                         packageDef.setTestPackageName(javaPackageFilter);
102                     }
103                     packageDef.setTargetBinaryName(targetBinaryName);
104                     packageDef.setTargetNameSpace(targetNameSpace);
105                     packageDef.setAbi(abi);
106                     mPackageDefs.put(abiName, packageDef);
107                 }
108 
109                 // reset the class name
110                 mClassNameStack = new Stack<String>();
111             } else if (TEST_SUITE_TAG.equals(localName)) {
112                 String packageSegment = attributes.getValue("name");
113                 if (packageSegment != null) {
114                     mClassNameStack.push(packageSegment);
115                 } else {
116                     Log.e(LOG_TAG, String.format("Invalid XML: missing 'name' attribute for '%s'",
117                             TEST_SUITE_TAG));
118                 }
119             } else if (TEST_CASE_TAG.equals(localName)) {
120                 String classSegment = attributes.getValue("name");
121                 if (classSegment != null) {
122                     mClassNameStack.push(classSegment);
123                 } else {
124                     Log.e(LOG_TAG, String.format("Invalid XML: missing 'name' attribute for '%s'",
125                             TEST_CASE_TAG));
126                 }
127             } else if (TEST_TAG.equals(localName)) {
128                 String methodName = attributes.getValue("name");
129                 if (mPackageDefs.isEmpty()) {
130                     Log.e(LOG_TAG, String.format(
131                             "Invalid XML: encountered a '%s' tag not enclosed within a '%s' tag",
132                             TEST_TAG, TEST_PACKAGE_TAG));
133                 } else if (methodName == null) {
134                     Log.e(LOG_TAG, String.format("Invalid XML: missing 'name' attribute for '%s'",
135                             TEST_TAG));
136                 } else {
137                     // build class name from package segments
138                     StringBuilder classNameBuilder = new StringBuilder();
139                     for (Iterator<String> iter = mClassNameStack.iterator(); iter.hasNext(); ) {
140                         classNameBuilder.append(iter.next());
141                         if (iter.hasNext()) {
142                             classNameBuilder.append(".");
143                         }
144                     }
145                     mTestId = new TestIdentifier(classNameBuilder.toString(), methodName);
146                     int timeout = -1;
147                     String timeoutStr = attributes.getValue("timeout");
148                     if (timeoutStr != null) {
149                         timeout = Integer.parseInt(timeoutStr);
150                     }
151                     boolean isKnownFailure = "failure".equals(attributes.getValue("expectation"));
152                     if (!isKnownFailure || mIncludeKnownFailures) {
153                         String abiList = attributes.getValue("abis");
154                         Set<String> abis = new HashSet<String>();
155                         if (abiList == null) {
156                             // If no specification, add all supported abis
157                             abis.addAll(AbiUtils.getAbisSupportedByCompatibility());
158                         } else {
159                             for (String abi : abiList.split(",")) {
160                                 // Else only add the abi which are supported
161                                 abis.add(abi.trim());
162                             }
163                         }
164                         for (String abi : abis) {
165                             mPackageDefs.get(abi).addTest(mTestId, timeout);
166                         }
167                     }
168                 }
169             } else if (TEST_INSTANCE_TAG.equals(localName)) {
170                 if (mTestId != null) {
171                     final Map<String, String> instanceArguments = genAttributeMap(attributes);
172                     for (TestPackageDef packageDef : mPackageDefs.values()) {
173                         if (packageDef.getTests().contains(mTestId)) {
174                             packageDef.addTestInstance(mTestId, instanceArguments);
175                         }
176                     }
177                 } else {
178                     Log.e(LOG_TAG, String.format(
179                             "Invalid XML: encountered a '%s' tag not enclosed within a '%s' tag",
180                             TEST_INSTANCE_TAG, TEST_TAG));
181                 }
182             }
183         }
184 
getTestType(Attributes attributes)185         private String getTestType(Attributes attributes) {
186             if (parseBoolean(attributes.getValue("hostSideOnly"))) {
187                 return TestPackageDef.HOST_SIDE_ONLY_TEST;
188             } else if (parseBoolean(attributes.getValue("vmHostTest"))) {
189                 return TestPackageDef.VM_HOST_TEST;
190             } else {
191                 return attributes.getValue("testType");
192             }
193         }
194 
195         @Override
endElement(String uri, String localName, String qName)196         public void endElement (String uri, String localName, String qName) {
197             if (TEST_SUITE_TAG.equals(localName) || TEST_CASE_TAG.equals(localName)) {
198                 mClassNameStack.pop();
199             } else if (TEST_TAG.equals(localName)) {
200                 mTestId = null;
201             }
202         }
203 
204         /**
205          * Parse a boolean attribute value
206          */
parseBoolean(final String stringValue)207         private boolean parseBoolean(final String stringValue) {
208             return stringValue != null &&
209                     Boolean.parseBoolean(stringValue);
210         }
211 
genAttributeMap(Attributes attributes)212         private Map<String, String> genAttributeMap(Attributes attributes) {
213             final Map<String, String> attribMap = new HashMap<String, String>();
214             for (int i = 0; i < attributes.getLength(); ++i) {
215                 final String localName = attributes.getLocalName(i);
216                 final String namespace = attributes.getURI(i);
217                 final String fullyQualifiedName =
218                         (namespace.isEmpty()) ? (localName) : (namespace + ":" + localName);
219 
220                 attribMap.put(fullyQualifiedName, attributes.getValue(i));
221             }
222             return attribMap;
223         }
224     }
225 
226     @Override
createXmlHandler()227     protected DefaultHandler createXmlHandler() {
228         return new TestPackageHandler();
229     }
230 
231     /**
232      * @return the set of {@link TestPackageDef} containing data parsed from xml
233      */
getTestPackageDefs()234     public Set<TestPackageDef> getTestPackageDefs() {
235         return new HashSet<>(mPackageDefs.values());
236     }
237 }
238