1 package android.icu.cts.tools;
2 
3 import com.google.common.base.Joiner;
4 import com.android.compatibility.common.util.AbiUtils;
5 import org.w3c.dom.Attr;
6 import org.w3c.dom.Document;
7 import org.w3c.dom.Element;
8 import org.w3c.dom.Node;
9 import java.io.BufferedReader;
10 import java.io.File;
11 import java.io.FileOutputStream;
12 import java.io.FileReader;
13 import java.io.IOException;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.TreeMap;
17 import java.util.TreeSet;
18 import javax.xml.parsers.DocumentBuilderFactory;
19 import javax.xml.transform.Transformer;
20 import javax.xml.transform.TransformerFactory;
21 import javax.xml.transform.dom.DOMSource;
22 import javax.xml.transform.stream.StreamResult;
23 
24 /**
25  * Generates an XML file suitable for CTS version 1.
26  *
27  * <p>A lot of this code is copied from {@code tools/utils/DescriptionGenerator.java} and
28  * {@code tools/utils/CollectAllTests.java}. Ideally, that code should have been refactored to make
29  * it usable in this case but that would have taken quite a lot of time and given that CTS version 1
30  * is going away anyway and CTS version 2 doesn't need an XML file this seemed like the best
31  * approach.
32  */
33 public class GenerateTestCaseXML {
34 
35   private static final String ATTRIBUTE_RUNNER = "runner";
36 
37   private static final String ATTRIBUTE_PACKAGE = "appPackageName";
38 
39   private static final String ATTRIBUTE_NS = "appNameSpace";
40 
41   static final String TAG_PACKAGE = "TestPackage";
42 
43   static final String TAG_SUITE = "TestSuite";
44 
45   static final String TAG_CASE = "TestCase";
46 
47   static final String TAG_TEST = "Test";
48 
49   static final String ATTRIBUTE_NAME_VERSION = "version";
50 
51   static final String ATTRIBUTE_VALUE_VERSION = "1.0";
52 
53   static final String ATTRIBUTE_NAME_FRAMEWORK = "AndroidFramework";
54 
55   static final String ATTRIBUTE_VALUE_FRAMEWORK = "Android 1.0";
56 
57   static final String ATTRIBUTE_NAME = "name";
58 
59   static final String ATTRIBUTE_ABIS = "abis";
60 
main(String[] args)61   public static void main(String[] args) throws Exception {
62     if (args.length != 3) {
63       throw new IllegalStateException(
64           "... <test-list-path> <architecture> <output-file-path");
65     }
66 
67     String testListPath = args[0];
68     String architecture = args[1];
69     String outputFilePath = args[2];
70 
71     File testListFile = new File(testListPath);
72     String abis = Joiner.on(" ").join(AbiUtils.getAbisForArch(architecture));
73     File testCaseXML = new File(outputFilePath);
74 
75     TestSuite root = new TestSuite("");
76     try (FileReader fileReader = new FileReader(testListFile);
77          BufferedReader reader = new BufferedReader(fileReader)) {
78 
79       String line;
80       while ((line = reader.readLine()) != null) {
81         int index = line.indexOf('#');
82         String className = line.substring(0, index);
83         String methodName = line.substring(index + 1);
84 
85         root.addTest(className, methodName);
86       }
87     }
88 
89     Document mDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
90 
91     Element testPackageElem = mDoc.createElement(TAG_PACKAGE);
92     mDoc.appendChild(testPackageElem);
93 
94     setAttribute(testPackageElem, ATTRIBUTE_NAME_VERSION, ATTRIBUTE_VALUE_VERSION);
95     setAttribute(testPackageElem, ATTRIBUTE_NAME_FRAMEWORK, ATTRIBUTE_VALUE_FRAMEWORK);
96     setAttribute(testPackageElem, ATTRIBUTE_NAME, "CtsIcuTestCases");
97     setAttribute(testPackageElem, ATTRIBUTE_RUNNER, "android.icu.cts.IcuTestRunnerForCtsV1");
98     setAttribute(testPackageElem, ATTRIBUTE_PACKAGE, "android.icu.dev.test");
99     setAttribute(testPackageElem, ATTRIBUTE_NS, "android.icu.cts");
100 
101     root.addChildInformation(testPackageElem, abis);
102 
103     Transformer t = TransformerFactory.newInstance().newTransformer();
104 
105     // enable indent in result file
106     t.setOutputProperty("indent", "yes");
107     t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
108 
109     File directory = testCaseXML.getParentFile();
110     if (!directory.exists() && !directory.mkdirs()) {
111       throw new IOException("Could not create directory: " + directory);
112     }
113 
114     t.transform(new DOMSource(mDoc), new StreamResult(new FileOutputStream(testCaseXML)));
115   }
116 
117   /**
118    * Set the attribute of element.
119    *
120    * @param elem The element to be set attribute.
121    * @param name The attribute name.
122    * @param value The attribute value.
123    */
setAttribute(Node elem, String name, String value)124   protected static void setAttribute(Node elem, String name, String value) {
125     Attr attr = elem.getOwnerDocument().createAttribute(name);
126     attr.setNodeValue(value);
127 
128     elem.getAttributes().setNamedItem(attr);
129   }
130 
131 
132   /**
133    * The contents of a {@link TestSuite}, may be a {@link TestSuite} or a {@link TestCase}.
134    */
135   private static abstract class SuiteContent {
136 
137     private final String name;
138 
SuiteContent(String name)139     private SuiteContent(String name) {
140       this.name = name;
141     }
142 
getName()143     public String getName() {
144       return name;
145     }
146 
addInformation(Element parent, String abis)147     public abstract void addInformation(Element parent, String abis);
148   }
149 
150   public static class TestSuite extends SuiteContent {
151 
152     private Map<String, SuiteContent> name2Content = new TreeMap<>();
153 
TestSuite(String name)154     public TestSuite(String name) {
155       super(name);
156     }
157 
getSuite(String name)158     public TestSuite getSuite(String name) {
159       return getSuiteContent(TestSuite.class, name);
160     }
161 
getTestCase(String name)162     public TestCase getTestCase(String name) {
163       return getSuiteContent(TestCase.class, name);
164     }
165 
getSuiteContent(Class<? extends S> contentClass, String name)166     private <S extends SuiteContent> S getSuiteContent(Class<? extends S> contentClass,
167         String name) {
168       SuiteContent content = name2Content.get(name);
169       S s;
170       if (content == null) {
171         try {
172           s = contentClass.getConstructor(String.class).newInstance(name);
173         } catch (Exception e) {
174           throw new RuntimeException("Could not create instance of " + contentClass, e);
175         }
176         name2Content.put(name, s);
177       } else if (contentClass.isInstance(content)) {
178         s = contentClass.cast(content);
179       } else {
180         throw new IllegalStateException("Expected " + this
181             + " to have a TestSuite called '" + name + "' but has "
182             + content + " instead");
183       }
184       return s;
185     }
186 
addTest(String className, String methodName)187     public void addTest(String className, String methodName) {
188       int index = className.indexOf('.');
189       if (index == -1) {
190         TestCase testCase = getTestCase(className);
191         testCase.addMethod(methodName);
192       } else {
193         String suiteName = className.substring(0, index);
194         TestSuite childSuite = getSuite(suiteName);
195         childSuite.addTest(className.substring(index + 1), methodName);
196       }
197     }
198 
199     @Override
addInformation(Element parent, String abis)200     public void addInformation(Element parent, String abis) {
201       Element suiteElement = appendElement(parent, TAG_SUITE);
202 
203       setAttribute(suiteElement, ATTRIBUTE_NAME, getName());
204 
205       addChildInformation(suiteElement, abis);
206     }
207 
addChildInformation(Element parent, String abis)208     public void addChildInformation(Element parent, String abis) {
209       for (SuiteContent suiteContent : name2Content.values()) {
210         suiteContent.addInformation(parent, abis);
211       }
212     }
213   }
214 
215   public static class TestCase extends SuiteContent {
216 
217     private final Set<String> methods = new TreeSet<>();
218 
TestCase(String name)219     public TestCase(String name) {
220       super(name);
221     }
222 
223     @Override
addInformation(Element parent, String abis)224     public void addInformation(Element parent, String abis) {
225       Element testCaseElement = appendElement(parent, TAG_CASE);
226       setAttribute(testCaseElement, ATTRIBUTE_NAME, getName());
227       setAttribute(testCaseElement, ATTRIBUTE_ABIS, abis);
228 
229       for (String method : methods) {
230         Element testElement = appendElement(testCaseElement, TAG_TEST);
231         setAttribute(testElement, ATTRIBUTE_NAME, method);
232       }
233     }
234 
addMethod(String methodName)235     public void addMethod(String methodName) {
236       methods.add(methodName);
237     }
238   }
239 
appendElement(Element parent, String tagName)240   private static Element appendElement(Element parent, String tagName) {
241     Element testCaseElement = parent.getOwnerDocument().createElement(tagName);
242     parent.appendChild(testCaseElement);
243     return testCaseElement;
244   }
245 }
246