1 /* 2 * Copyright (C) 2017 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 17 package com.android.asllib.testutils; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertThrows; 21 22 import com.android.asllib.marshallable.AslMarshallable; 23 import com.android.asllib.marshallable.AslMarshallableFactory; 24 import com.android.asllib.util.MalformedXmlException; 25 import com.android.asllib.util.XmlUtils; 26 27 import org.w3c.dom.Document; 28 import org.w3c.dom.Element; 29 import org.w3c.dom.Node; 30 import org.w3c.dom.NodeList; 31 import org.xml.sax.SAXException; 32 33 import java.io.ByteArrayInputStream; 34 import java.io.ByteArrayOutputStream; 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.nio.charset.StandardCharsets; 38 import java.nio.file.Path; 39 import java.nio.file.Paths; 40 import java.util.List; 41 import java.util.Optional; 42 43 import javax.xml.parsers.DocumentBuilderFactory; 44 import javax.xml.parsers.ParserConfigurationException; 45 import javax.xml.transform.OutputKeys; 46 import javax.xml.transform.Transformer; 47 import javax.xml.transform.TransformerException; 48 import javax.xml.transform.TransformerFactory; 49 import javax.xml.transform.dom.DOMSource; 50 import javax.xml.transform.stream.StreamResult; 51 52 public class TestUtils { 53 public static final String HOLDER_TAG_NAME = "holder_of_flattened_for_testing"; 54 55 /** Reads a Resource file into a String. */ readStrFromResource(Path filePath)56 public static String readStrFromResource(Path filePath) throws IOException { 57 InputStream hrStream = 58 TestUtils.class.getClassLoader().getResourceAsStream(filePath.toString()); 59 return new String(hrStream.readAllBytes(), StandardCharsets.UTF_8); 60 } 61 62 /** Gets List of Element from a path to an existing Resource. */ getElementsFromResource(Path filePath)63 public static List<Element> getElementsFromResource(Path filePath) 64 throws ParserConfigurationException, IOException, SAXException { 65 String str = readStrFromResource(filePath); 66 InputStream stream = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8)); 67 68 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 69 factory.setNamespaceAware(true); 70 Document document = factory.newDocumentBuilder().parse(stream); 71 Element root = document.getDocumentElement(); 72 if (root.getTagName().equals(HOLDER_TAG_NAME)) { 73 String tagName = 74 XmlUtils.asElementList(root.getChildNodes()).stream() 75 .findFirst() 76 .get() 77 .getTagName(); 78 return XmlUtils.getChildrenByTagName(root, tagName); 79 } else { 80 return List.of(root); 81 } 82 } 83 84 /** Reads a Document into a String. */ docToStr(Document doc, boolean omitXmlDeclaration)85 public static String docToStr(Document doc, boolean omitXmlDeclaration) 86 throws TransformerException { 87 TransformerFactory transformerFactory = TransformerFactory.newInstance(); 88 Transformer transformer = transformerFactory.newTransformer(); 89 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 90 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 91 transformer.setOutputProperty( 92 OutputKeys.OMIT_XML_DECLARATION, omitXmlDeclaration ? "yes" : "no"); 93 94 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 95 StreamResult streamResult = new StreamResult(outStream); // out 96 DOMSource domSource = new DOMSource(doc); 97 transformer.transform(domSource, streamResult); 98 99 return outStream.toString(StandardCharsets.UTF_8); 100 } 101 102 /** Removes on-device style child with the corresponding name */ removeOdChildEleWithName(Element ele, String childNameName)103 public static void removeOdChildEleWithName(Element ele, String childNameName) { 104 Optional<Element> childEle = 105 XmlUtils.asElementList(ele.getChildNodes()).stream() 106 .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(childNameName)) 107 .findFirst(); 108 if (childEle.isEmpty()) { 109 throw new IllegalStateException( 110 String.format("%s was not found in %s", childNameName, ele.getTagName())); 111 } 112 ele.removeChild(childEle.get()); 113 } 114 115 /** 116 * Gets formatted XML for slightly more robust comparison checking than naive string comparison. 117 */ getFormattedXml(String xmlStr, boolean omitXmlDeclaration)118 public static String getFormattedXml(String xmlStr, boolean omitXmlDeclaration) 119 throws ParserConfigurationException, IOException, SAXException, TransformerException { 120 InputStream stream = new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8)); 121 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 122 factory.setNamespaceAware(true); 123 Document document = factory.newDocumentBuilder().parse(stream); 124 stripEmptyElements(document); 125 return docToStr(document, omitXmlDeclaration); 126 } 127 128 /** Helper for getting a new Document */ document()129 public static Document document() throws ParserConfigurationException { 130 return DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 131 } 132 133 /** Helper for testing human-readable to on-device conversion expecting exception */ hrToOdExpectException( AslMarshallableFactory<T> factory, String hrFolderPath, String fileName)134 public static <T extends AslMarshallable> void hrToOdExpectException( 135 AslMarshallableFactory<T> factory, String hrFolderPath, String fileName) { 136 assertThrows( 137 MalformedXmlException.class, 138 () -> { 139 factory.createFromHrElements( 140 TestUtils.getElementsFromResource(Paths.get(hrFolderPath, fileName))); 141 }); 142 } 143 144 /** Helper for testing on-device to human-readable conversion expecting exception */ odToHrExpectException( AslMarshallableFactory<T> factory, String odFolderPath, String fileName)145 public static <T extends AslMarshallable> void odToHrExpectException( 146 AslMarshallableFactory<T> factory, String odFolderPath, String fileName) { 147 assertThrows( 148 MalformedXmlException.class, 149 () -> { 150 factory.createFromOdElements( 151 TestUtils.getElementsFromResource(Paths.get(odFolderPath, fileName))); 152 }); 153 } 154 155 /** Helper for testing human-readable to on-device conversion */ testHrToOd( Document doc, AslMarshallableFactory<T> factory, String hrFolderPath, String odFolderPath, String fileName)156 public static <T extends AslMarshallable> void testHrToOd( 157 Document doc, 158 AslMarshallableFactory<T> factory, 159 String hrFolderPath, 160 String odFolderPath, 161 String fileName) 162 throws Exception { 163 testFormatToFormat(doc, factory, hrFolderPath, odFolderPath, fileName, true); 164 } 165 166 /** Helper for testing on-device to human-readable conversion */ testOdToHr( Document doc, AslMarshallableFactory<T> factory, String odFolderPath, String hrFolderPath, String fileName)167 public static <T extends AslMarshallable> void testOdToHr( 168 Document doc, 169 AslMarshallableFactory<T> factory, 170 String odFolderPath, 171 String hrFolderPath, 172 String fileName) 173 throws Exception { 174 testFormatToFormat(doc, factory, odFolderPath, hrFolderPath, fileName, false); 175 } 176 177 /** Helper for testing format to format conversion */ testFormatToFormat( Document doc, AslMarshallableFactory<T> factory, String inFolderPath, String outFolderPath, String fileName, boolean hrToOd)178 private static <T extends AslMarshallable> void testFormatToFormat( 179 Document doc, 180 AslMarshallableFactory<T> factory, 181 String inFolderPath, 182 String outFolderPath, 183 String fileName, 184 boolean hrToOd) 185 throws Exception { 186 AslMarshallable marshallable = 187 hrToOd 188 ? factory.createFromHrElements( 189 TestUtils.getElementsFromResource( 190 Paths.get(inFolderPath, fileName))) 191 : factory.createFromOdElements( 192 TestUtils.getElementsFromResource( 193 Paths.get(inFolderPath, fileName))); 194 195 List<Element> elements = 196 hrToOd ? marshallable.toOdDomElements(doc) : marshallable.toHrDomElements(doc); 197 if (elements.isEmpty()) { 198 throw new IllegalStateException("elements was empty."); 199 } else if (elements.size() == 1) { 200 doc.appendChild(elements.get(0)); 201 } else { 202 Element root = doc.createElement(TestUtils.HOLDER_TAG_NAME); 203 for (var child : elements) { 204 root.appendChild(child); 205 } 206 doc.appendChild(root); 207 } 208 String converted = TestUtils.getFormattedXml(TestUtils.docToStr(doc, true), true); 209 System.out.println("Converted: " + converted); 210 String expectedOutContents = 211 TestUtils.getFormattedXml( 212 TestUtils.readStrFromResource(Paths.get(outFolderPath, fileName)), true); 213 System.out.println("Expected: " + expectedOutContents); 214 assertEquals(expectedOutContents, converted); 215 } 216 stripEmptyElements(Node node)217 private static void stripEmptyElements(Node node) { 218 NodeList children = node.getChildNodes(); 219 for (int i = 0; i < children.getLength(); ++i) { 220 Node child = children.item(i); 221 if (child.getNodeType() == Node.TEXT_NODE) { 222 if (child.getTextContent().trim().length() == 0) { 223 child.getParentNode().removeChild(child); 224 i--; 225 } 226 } 227 stripEmptyElements(child); 228 } 229 } 230 } 231