1/*
2 * Copyright (C) 2013 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
17package com.android.internal.util;
18
19import android.test.suitebuilder.annotation.LargeTest;
20import android.test.suitebuilder.annotation.SmallTest;
21import android.util.Log;
22import android.util.Xml;
23
24import junit.framework.TestCase;
25
26import org.xmlpull.v1.XmlPullParser;
27import org.xmlpull.v1.XmlSerializer;
28
29import java.io.ByteArrayInputStream;
30import java.io.ByteArrayOutputStream;
31import java.nio.charset.StandardCharsets;
32
33/**
34 * Tests for {@link FastXmlSerializer}
35 */
36@SmallTest
37public class FastXmlSerializerTest extends TestCase {
38    private static final String TAG = "FastXmlSerializerTest";
39
40    private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH TRUE.
41
42    private static final String ROOT_TAG = "root";
43    private static final String ATTR = "attr";
44
45    public void testEmptyText() throws Exception {
46        final ByteArrayOutputStream stream = new ByteArrayOutputStream();
47
48        final XmlSerializer out = new FastXmlSerializer();
49        out.setOutput(stream, "utf-8");
50        out.startDocument(null, true);
51        out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
52
53        out.startTag(null, "string");
54        out.attribute(null, "name", "meow");
55        out.text("");
56        out.endTag(null, "string");
57
58        out.endDocument();
59
60        assertEquals("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
61                + "<string name=\"meow\"></string>\n", stream.toString());
62    }
63
64    private boolean checkPreserved(String description, String str) {
65        boolean ok = true;
66        byte[] data;
67        try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
68            final XmlSerializer out = new FastXmlSerializer();
69            out.setOutput(baos, StandardCharsets.UTF_16.name());
70            out.startDocument(null, true);
71
72            out.startTag(null, ROOT_TAG);
73            out.attribute(null, ATTR, str);
74            out.text(str);
75            out.endTag(null, ROOT_TAG);
76
77            out.endDocument();
78            baos.flush();
79            data = baos.toByteArray();
80        } catch (Exception e) {
81            Log.e(TAG, "Unable to serialize: " + description, e);
82            return false;
83        }
84
85        if (ENABLE_DUMP) {
86            Log.d(TAG, "Dump:");
87            Log.d(TAG, new String(data));
88        }
89
90        try (final ByteArrayInputStream baos = new ByteArrayInputStream(data)) {
91            XmlPullParser parser = Xml.newPullParser();
92            parser.setInput(baos, StandardCharsets.UTF_16.name());
93
94            int type;
95            String tag = null;
96            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
97                if (type == XmlPullParser.START_TAG) {
98                    tag = parser.getName();
99                    if (ROOT_TAG.equals(tag)) {
100                        String read = parser.getAttributeValue(null, ATTR);
101                        if (!str.equals(read)) {
102                            Log.e(TAG, "Attribute not preserved: " + description
103                                    + " input=\"" + str + "\", but read=\"" + read + "\"");
104                            ok = false;
105                        }
106                    }
107                }
108                if (type == XmlPullParser.TEXT && ROOT_TAG.equals(tag)) {
109                    String read = parser.getText();
110                    if (!str.equals(parser.getText())) {
111                        Log.e(TAG, "Text not preserved: " + description
112                                + " input=\"" + str + "\", but read=\"" + read + "\"");
113                        ok = false;
114                    }
115                }
116            }
117        } catch (Exception e) {
118            Log.e(TAG, "Unable to parse: " + description, e);
119            return false;
120        }
121        return ok;
122    }
123
124    private boolean check(String description, String str) throws Exception {
125        boolean ok = false;
126        ok |= checkPreserved(description, str);
127        ok |= checkPreserved(description + " wrapped with spaces" ,"  " + str + "  ");
128        return ok;
129    }
130
131    @LargeTest
132    public void testAllCharacters() throws Exception {
133        boolean ok = true;
134        for (int i = 0; i < 0xffff; i++) {
135            if (0xd800 <= i && i <= 0xdfff) {
136                // Surrogate pair characters.
137                continue;
138            }
139            ok &= check("char: " + i, String.valueOf((char) i));
140        }
141        // Dangling surrogate pairs. We can't preserve them.
142        assertFalse(check("+ud800", "\ud800"));
143        assertFalse(check("+udc00", "\udc00"));
144
145        for (int i = 0xd800; i < 0xdc00; i ++) {
146            for (int j = 0xdc00; j < 0xe000; j++) {
147                ok &= check("char: " + i, String.valueOf((char) i) + String.valueOf((char) j));
148            }
149        }
150        assertTrue("Some tests failed.  See logcat for details.", ok);
151    }
152}
153