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