13029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey/*
23029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey * Copyright (C) 2013 The Android Open Source Project
33029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey *
43029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
53029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey * you may not use this file except in compliance with the License.
63029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey * You may obtain a copy of the License at
73029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey *
83029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
93029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey *
103029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
113029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
123029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey * See the License for the specific language governing permissions and
143029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey * limitations under the License.
153029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey */
163029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey
173029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkeypackage com.android.internal.util;
183029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey
193ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onukiimport android.test.suitebuilder.annotation.LargeTest;
203ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onukiimport android.test.suitebuilder.annotation.SmallTest;
213ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onukiimport android.util.Log;
223ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onukiimport android.util.Xml;
233ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
243029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkeyimport junit.framework.TestCase;
253029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey
263ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onukiimport org.xmlpull.v1.XmlPullParser;
273029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkeyimport org.xmlpull.v1.XmlSerializer;
283029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey
293ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onukiimport java.io.ByteArrayInputStream;
303029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkeyimport java.io.ByteArrayOutputStream;
313ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onukiimport java.nio.charset.StandardCharsets;
323029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey
333029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey/**
343029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey * Tests for {@link FastXmlSerializer}
353029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey */
363ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki@SmallTest
373029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkeypublic class FastXmlSerializerTest extends TestCase {
383ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki    private static final String TAG = "FastXmlSerializerTest";
393ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
403ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki    private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH TRUE.
413ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
423ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki    private static final String ROOT_TAG = "root";
433ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki    private static final String ATTR = "attr";
443ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
453029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey    public void testEmptyText() throws Exception {
463029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey        final ByteArrayOutputStream stream = new ByteArrayOutputStream();
473029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey
483029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey        final XmlSerializer out = new FastXmlSerializer();
493029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey        out.setOutput(stream, "utf-8");
503029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey        out.startDocument(null, true);
513029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey        out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
523029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey
533029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey        out.startTag(null, "string");
543029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey        out.attribute(null, "name", "meow");
553029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey        out.text("");
563029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey        out.endTag(null, "string");
573029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey
583029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey        out.endDocument();
593029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey
603029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey        assertEquals("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
61733fc6b6f9af33bae047e8296b9fc9d341f4f202Mitchell Wills                + "<string name=\"meow\"></string>\n", stream.toString());
623029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey    }
633ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
643ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki    private boolean checkPreserved(String description, String str) {
653ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        boolean ok = true;
663ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        byte[] data;
673ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
683ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            final XmlSerializer out = new FastXmlSerializer();
693ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            out.setOutput(baos, StandardCharsets.UTF_16.name());
703ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            out.startDocument(null, true);
713ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
723ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            out.startTag(null, ROOT_TAG);
733ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            out.attribute(null, ATTR, str);
743ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            out.text(str);
753ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            out.endTag(null, ROOT_TAG);
763ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
773ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            out.endDocument();
783ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            baos.flush();
793ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            data = baos.toByteArray();
803ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        } catch (Exception e) {
813ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            Log.e(TAG, "Unable to serialize: " + description, e);
823ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            return false;
833ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        }
843ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
853ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        if (ENABLE_DUMP) {
863ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            Log.d(TAG, "Dump:");
873ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            Log.d(TAG, new String(data));
883ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        }
893ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
903ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        try (final ByteArrayInputStream baos = new ByteArrayInputStream(data)) {
913ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            XmlPullParser parser = Xml.newPullParser();
923ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            parser.setInput(baos, StandardCharsets.UTF_16.name());
933ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
943ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            int type;
953ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            String tag = null;
963ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
973ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                if (type == XmlPullParser.START_TAG) {
983ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                    tag = parser.getName();
993ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                    if (ROOT_TAG.equals(tag)) {
1003ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                        String read = parser.getAttributeValue(null, ATTR);
1013ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                        if (!str.equals(read)) {
1023ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                            Log.e(TAG, "Attribute not preserved: " + description
1033ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                                    + " input=\"" + str + "\", but read=\"" + read + "\"");
1043ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                            ok = false;
1053ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                        }
1063ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                    }
1073ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                }
1083ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                if (type == XmlPullParser.TEXT && ROOT_TAG.equals(tag)) {
1093ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                    String read = parser.getText();
1103ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                    if (!str.equals(parser.getText())) {
1113ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                        Log.e(TAG, "Text not preserved: " + description
1123ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                                + " input=\"" + str + "\", but read=\"" + read + "\"");
1133ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                        ok = false;
1143ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                    }
1153ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                }
1163ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            }
1173ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        } catch (Exception e) {
1183ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            Log.e(TAG, "Unable to parse: " + description, e);
1193ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            return false;
1203ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        }
1213ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        return ok;
1223ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki    }
1233ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
1243ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki    private boolean check(String description, String str) throws Exception {
1253ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        boolean ok = false;
1263ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        ok |= checkPreserved(description, str);
1273ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        ok |= checkPreserved(description + " wrapped with spaces" ,"  " + str + "  ");
1283ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        return ok;
1293ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki    }
1303ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
1313ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki    @LargeTest
1323ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki    public void testAllCharacters() throws Exception {
1333ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        boolean ok = true;
1343ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        for (int i = 0; i < 0xffff; i++) {
1353ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            if (0xd800 <= i && i <= 0xdfff) {
1363ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                // Surrogate pair characters.
1373ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                continue;
1383ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            }
1393ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            ok &= check("char: " + i, String.valueOf((char) i));
1403ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        }
1413ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        // Dangling surrogate pairs. We can't preserve them.
1423ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        assertFalse(check("+ud800", "\ud800"));
1433ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        assertFalse(check("+udc00", "\udc00"));
1443ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki
1453ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        for (int i = 0xd800; i < 0xdc00; i ++) {
1463ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            for (int j = 0xdc00; j < 0xe000; j++) {
1473ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki                ok &= check("char: " + i, String.valueOf((char) i) + String.valueOf((char) j));
1483ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki            }
1493ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        }
1503ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki        assertTrue("Some tests failed.  See logcat for details.", ok);
1513ae3557ea3a9ad8429de9db14de62a4214a07cdcMakoto Onuki    }
1523029bf225cfa2c4b5b6e76303b0eba0d91c21026Jeff Sharkey}
153