1 /* 2 * Copyright (C) 2009 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 libcore.xml; 18 19 import java.io.ByteArrayOutputStream; 20 import java.io.IOException; 21 import java.io.StringWriter; 22 import junit.framework.TestCase; 23 import org.kxml2.io.KXmlSerializer; 24 import org.w3c.dom.Document; 25 import org.w3c.dom.Node; 26 import org.w3c.dom.NodeList; 27 import org.w3c.dom.Text; 28 import org.xmlpull.v1.XmlSerializer; 29 import static tests.support.Support_Xml.domOf; 30 31 public final class KxmlSerializerTest extends TestCase { 32 private static final String NAMESPACE = null; 33 34 public void testWhitespaceInAttributeValue() throws Exception { 35 StringWriter stringWriter = new StringWriter(); 36 XmlSerializer serializer = new KXmlSerializer(); 37 serializer.setOutput(stringWriter); 38 serializer.startDocument("UTF-8", null); 39 serializer.startTag(NAMESPACE, "a"); 40 serializer.attribute(NAMESPACE, "cr", "\r"); 41 serializer.attribute(NAMESPACE, "lf", "\n"); 42 serializer.attribute(NAMESPACE, "tab", "\t"); 43 serializer.attribute(NAMESPACE, "space", " "); 44 serializer.endTag(NAMESPACE, "a"); 45 serializer.endDocument(); 46 assertXmlEquals("<a cr=\" \" lf=\" \" tab=\"	\" space=\" \" />", 47 stringWriter.toString()); 48 } 49 50 public void testWriteDocument() throws Exception { 51 StringWriter stringWriter = new StringWriter(); 52 XmlSerializer serializer = new KXmlSerializer(); 53 serializer.setOutput(stringWriter); 54 serializer.startDocument("UTF-8", null); 55 serializer.startTag(NAMESPACE, "foo"); 56 serializer.attribute(NAMESPACE, "quux", "abc"); 57 serializer.startTag(NAMESPACE, "bar"); 58 serializer.endTag(NAMESPACE, "bar"); 59 serializer.startTag(NAMESPACE, "baz"); 60 serializer.endTag(NAMESPACE, "baz"); 61 serializer.endTag(NAMESPACE, "foo"); 62 serializer.endDocument(); 63 assertXmlEquals("<foo quux=\"abc\"><bar /><baz /></foo>", stringWriter.toString()); 64 } 65 66 // http://code.google.com/p/android/issues/detail?id=21250 67 public void testWriteSpecialCharactersInText() throws Exception { 68 StringWriter stringWriter = new StringWriter(); 69 XmlSerializer serializer = new KXmlSerializer(); 70 serializer.setOutput(stringWriter); 71 serializer.startDocument("UTF-8", null); 72 serializer.startTag(NAMESPACE, "foo"); 73 serializer.text("5'8\", 5 < 6 & 7 > 3!"); 74 serializer.endTag(NAMESPACE, "foo"); 75 serializer.endDocument(); 76 assertXmlEquals("<foo>5'8\", 5 < 6 & 7 > 3!</foo>", stringWriter.toString()); 77 } 78 79 private void assertXmlEquals(String expectedXml, String actualXml) throws Exception { 80 String declaration = "<?xml version='1.0' encoding='UTF-8' ?>"; 81 assertEquals(declaration + expectedXml, actualXml); 82 } 83 84 private static XmlSerializer newSerializer() throws IOException { 85 ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 86 XmlSerializer serializer = new KXmlSerializer(); 87 serializer.setOutput(bytesOut, "UTF-8"); 88 serializer.startDocument("UTF-8", null); 89 return serializer; 90 } 91 92 public String fromCodePoint(int codePoint) { 93 if (codePoint > Character.MAX_VALUE) { 94 return new String(Character.toChars(codePoint)); 95 } 96 return Character.toString((char) codePoint); 97 } 98 99 // http://b/17960630 100 public void testSpeakNoEvilMonkeys() throws Exception { 101 StringWriter stringWriter = new StringWriter(); 102 XmlSerializer serializer = new KXmlSerializer(); 103 serializer.setOutput(stringWriter); 104 serializer.startDocument("UTF-8", null); 105 serializer.startTag(NAMESPACE, "tag"); 106 serializer.attribute(NAMESPACE, "attr", "a\ud83d\ude4ab"); 107 serializer.text("c\ud83d\ude4ad"); 108 serializer.cdsect("e\ud83d\ude4af"); 109 serializer.endTag(NAMESPACE, "tag"); 110 serializer.endDocument(); 111 assertXmlEquals("<tag attr=\"a🙊b\">" + 112 "c🙊d" + 113 "<![CDATA[e]]>🙊<![CDATA[f]]>" + 114 "</tag>", stringWriter.toString()); 115 116 // Check we can parse what we just output. 117 Document doc = domOf(stringWriter.toString()); 118 Node root = doc.getDocumentElement(); 119 assertEquals("a\ud83d\ude4ab", root.getAttributes().getNamedItem("attr").getNodeValue()); 120 Text text = (Text) root.getFirstChild(); 121 assertEquals("c\ud83d\ude4ade\ud83d\ude4af", text.getNodeValue()); 122 } 123 124 public void testBadSurrogates() throws Exception { 125 StringWriter stringWriter = new StringWriter(); 126 XmlSerializer serializer = new KXmlSerializer(); 127 serializer.setOutput(stringWriter); 128 serializer.startDocument("UTF-8", null); 129 serializer.startTag(NAMESPACE, "tag"); 130 try { 131 serializer.attribute(NAMESPACE, "attr", "a\ud83d\u0040b"); 132 fail(); 133 } catch (IllegalArgumentException expected) { 134 } 135 try { 136 serializer.text("c\ud83d\u0040d"); 137 fail(); 138 } catch (IllegalArgumentException expected) { 139 } 140 try { 141 serializer.cdsect("e\ud83d\u0040f"); 142 fail(); 143 } catch (IllegalArgumentException expected) { 144 } 145 } 146 147 // Cover all the BMP code points plus a few that require us to use surrogates. 148 private static int MAX_TEST_CODE_POINT = 0x10008; 149 150 public void testInvalidCharactersInText() throws IOException { 151 XmlSerializer serializer = newSerializer(); 152 serializer.startTag(NAMESPACE, "root"); 153 for (int c = 0; c <= MAX_TEST_CODE_POINT; ++c) { 154 final String s = fromCodePoint(c); 155 if (isValidXmlCodePoint(c)) { 156 serializer.text("a" + s + "b"); 157 } else { 158 try { 159 serializer.text("a" + s + "b"); 160 fail(s); 161 } catch (IllegalArgumentException expected) { 162 } 163 } 164 } 165 serializer.endTag(NAMESPACE, "root"); 166 } 167 168 public void testInvalidCharactersInAttributeValues() throws IOException { 169 XmlSerializer serializer = newSerializer(); 170 serializer.startTag(NAMESPACE, "root"); 171 for (int c = 0; c <= MAX_TEST_CODE_POINT; ++c) { 172 final String s = fromCodePoint(c); 173 if (isValidXmlCodePoint(c)) { 174 serializer.attribute(NAMESPACE, "a", "a" + s + "b"); 175 } else { 176 try { 177 serializer.attribute(NAMESPACE, "a", "a" + s + "b"); 178 fail(s); 179 } catch (IllegalArgumentException expected) { 180 } 181 } 182 } 183 serializer.endTag(NAMESPACE, "root"); 184 } 185 186 public void testInvalidCharactersInCdataSections() throws IOException { 187 XmlSerializer serializer = newSerializer(); 188 serializer.startTag(NAMESPACE, "root"); 189 for (int c = 0; c <= MAX_TEST_CODE_POINT; ++c) { 190 final String s = fromCodePoint(c); 191 if (isValidXmlCodePoint(c)) { 192 serializer.cdsect("a" + s + "b"); 193 } else { 194 try { 195 serializer.cdsect("a" + s + "b"); 196 fail(s); 197 } catch (IllegalArgumentException expected) { 198 } 199 } 200 } 201 serializer.endTag(NAMESPACE, "root"); 202 } 203 204 public void testCdataWithTerminatorInside() throws Exception { 205 StringWriter writer = new StringWriter(); 206 XmlSerializer serializer = new KXmlSerializer(); 207 serializer.setOutput(writer); 208 serializer.startDocument("UTF-8", null); 209 serializer.startTag(NAMESPACE, "p"); 210 serializer.cdsect("a]]>b"); 211 serializer.endTag(NAMESPACE, "p"); 212 serializer.endDocument(); 213 // Adjacent CDATA sections aren't merged, so let's stick them together ourselves... 214 Document doc = domOf(writer.toString()); 215 NodeList children = doc.getFirstChild().getChildNodes(); 216 String text = ""; 217 for (int i = 0; i < children.getLength(); ++i) { 218 text += children.item(i).getNodeValue(); 219 } 220 assertEquals("a]]>b", text); 221 } 222 223 private static boolean isValidXmlCodePoint(int c) { 224 // http://www.w3.org/TR/REC-xml/#charsets 225 return (c >= 0x20 && c <= 0xd7ff) || (c == 0x9) || (c == 0xa) || (c == 0xd) || 226 (c >= 0xe000 && c <= 0xfffd) || (c >= 0x10000 && c <= 0x10ffff); 227 } 228 } 229