1 /* This file is part of the KDE project
2  * Copyright (C) 2004 David Faure <faure@kde.org>
3  * Copyright 2008 Thomas Zander <zander@kde.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 // clazy:excludeall=qstring-arg
21 #include <KoXmlWriter.h>
22 
23 #include <QString>
24 #include <QBuffer>
25 #include <QTest>
26 
27 class TestXmlWriter : public QObject
28 {
29     Q_OBJECT
30 private Q_SLOTS:
31     void testDocytype();
32     void testEmtpyElement();
33     void testAttributes();
34     void testIndent();
35     void testTextNode();
36     void testTextSpan();
37     void testTextSpanWithTabCache();
38     void testProcessingInstruction();
39     void testAddManifestEntry();
40     void testEscapingLongString();
41     void testEscalingLongString2();
42     void testConfig();
43 
44     void speedTest();
45 
46 private:
47     void setup(const char *publicId = 0, const char *systemId = 0);
48     QString content();
49 
50     KoXmlWriter *writer;
51     QBuffer *buffer;
52 };
53 
setup(const char * publicId,const char * systemId)54 void TestXmlWriter::setup(const char *publicId, const char *systemId)
55 {
56     buffer = new QBuffer();
57     buffer->open(QIODevice::WriteOnly);
58 
59     writer = new KoXmlWriter(buffer);
60     writer->startDocument("dummy", publicId, systemId);
61     writer->startElement("dummy");
62 }
63 
content()64 QString TestXmlWriter::content()
65 {
66     writer->endElement();
67     writer->endDocument();
68     buffer->putChar('\0'); /*null-terminate*/
69     buffer->close();
70     QString stringContent = QString::fromUtf8(buffer->data());
71     int index = stringContent.indexOf("<dummy");
72     Q_ASSERT(index);
73     index = stringContent.indexOf('>', index);
74     stringContent = stringContent.mid(index+1, stringContent.length() - index - 11).trimmed();
75     return stringContent;
76 }
77 
testDocytype()78 void TestXmlWriter::testDocytype()
79 {
80     setup("foo", "bar");
81     QCOMPARE(content(), QString());
82     QString stringContent = QString::fromUtf8(buffer->data());
83     QCOMPARE(stringContent, QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
84         "<!DOCTYPE dummy PUBLIC \"foo\" \"bar\">\n<dummy/>\n"));
85 }
86 
testAttributes()87 void TestXmlWriter::testAttributes()
88 {
89     setup();
90 
91     writer->startElement("test");
92     writer->addAttribute("a", "val");
93     writer->addAttribute("b", "<\">");
94     writer->addAttribute("c", -42);
95     writer->addAttribute("d", 1234.56789012345);
96     writer->addAttributePt("e", 1234.56789012345);
97     writer->addAttribute("f", false);
98     writer->addAttribute("g", true);
99     writer->endElement();
100     QCOMPARE(content(), QString("<test a=\"val\" b=\"&lt;&quot;&gt;\" c=\"-42\" d=\"1234.56789012345\" e=\"1234.56789012345pt\" f=\"false\" g=\"true\"/>"));
101 }
102 
testEmtpyElement()103 void TestXmlWriter::testEmtpyElement()
104 {
105     setup();
106     writer->startElement("m");
107     writer->endElement();
108     QCOMPARE(content(), QString("<m/>"));
109 }
110 
testIndent()111 void TestXmlWriter::testIndent()
112 {
113     setup();
114     writer->startElement("a");
115     writer->startElement("b");
116     writer->startElement("c");
117     writer->endElement();
118     writer->endElement();
119     writer->endElement();
120     QCOMPARE(content(), QString("<a>\n  <b>\n   <c/>\n  </b>\n </a>"));
121 }
122 
testTextNode()123 void TestXmlWriter::testTextNode()
124 {
125     setup();
126     writer->startElement("a");
127     writer->startElement("b", false /*no indent*/);
128     writer->startElement("c");
129     writer->endElement();
130     writer->addTextNode("te");
131     writer->addTextNode("xt");
132     writer->endElement();
133     writer->endElement();
134     QCOMPARE(content(), QString("<a>\n  <b><c/>text</b>\n </a>"));
135 }
136 
testTextSpan()137 void TestXmlWriter::testTextSpan()
138 {
139     setup();
140     writer->startElement("p", false /*no indent*/);
141     writer->addTextSpan(QString::fromLatin1("   \t\n foo  "));
142     writer->endElement();
143     QCOMPARE(content(), QString("<p><text:s text:c=\"3\"/><text:tab/><text:line-break/> foo<text:s text:c=\"2\"/></p>"));
144 }
145 
testTextSpanWithTabCache()146 void TestXmlWriter::testTextSpanWithTabCache()
147 {
148     setup();
149     writer->startElement("p", false /*no indent*/);
150     QMap<int, int> tabCache;
151     tabCache.insert(3, 0);
152     writer->addTextSpan(QString::fromUtf8("   \t\n foö  "), tabCache);
153     writer->endElement();
154     QCOMPARE(content(), QString::fromUtf8("<p><text:s text:c=\"3\"/><text:tab text:tab-ref=\"1\"/>"
155             "<text:line-break/> foö<text:s text:c=\"2\"/></p>"));
156 }
157 
testProcessingInstruction()158 void TestXmlWriter::testProcessingInstruction()
159 {
160     setup();
161     writer->startElement("p", false /*no indent*/);
162     writer->addProcessingInstruction("opendocument foobar");
163     writer->addTextSpan(QString::fromLatin1("foo"));
164     writer->endElement();
165     QCOMPARE(content(), QString("<p><?opendocument foobar?>foo</p>"));
166 }
167 
testAddManifestEntry()168 void TestXmlWriter::testAddManifestEntry()
169 {
170     setup();
171     writer->addManifestEntry(QString::fromLatin1("foo/bar/blah"), QString::fromLatin1("mime/type"));
172     QCOMPARE(content(), QString("<manifest:file-entry manifest:media-type=\"mime/type\" "
173                 "manifest:full-path=\"foo/bar/blah\"/>"));
174 }
175 
176 
testEscapingLongString()177 void TestXmlWriter::testEscapingLongString()
178 {
179     int sz = 15000;  // must be more than KoXmlWriter::s_escapeBufferLen
180     QString x(sz);
181     x.fill('x', sz);
182     x += '&';
183     setup();
184 
185     writer->startElement("test");
186     writer->addAttribute("a", x);
187     writer->endElement();
188 
189     QString expected = "<test a=\"";
190     expected += x + "amp;\"/>";
191     QCOMPARE(content(), QString(expected));
192 }
193 
testEscalingLongString2()194 void TestXmlWriter::testEscalingLongString2()
195 {
196     QString longPath;
197     for (uint i = 0 ; i < 1000 ; ++i)
198         longPath += QString::fromLatin1("M10 10L20 20 ");
199     setup();
200     writer->startElement("test");
201     writer->addAttribute("a", longPath);
202     writer->endElement();
203     QString expected = "<test a=\"";
204     expected += longPath.toUtf8() + "\"/>";
205     QCOMPARE(content(), expected);
206 
207 }
208 
testConfig()209 void TestXmlWriter::testConfig()
210 {
211     setup();
212     const bool val = true;
213     const int num = 1;
214     const qreal numdouble = 5.0;
215     writer->addConfigItem(QString::fromLatin1("TestConfigBool"), val);
216     writer->addConfigItem(QString::fromLatin1("TestConfigInt"), num);
217     writer->addConfigItem(QString::fromLatin1("TestConfigDouble"), numdouble);
218     QCOMPARE(content(), QString("<config:config-item config:name=\"TestConfigBool\""
219             " config:type=\"boolean\">true</config:config-item>\n"
220             " <config:config-item config:name=\"TestConfigInt\" config:type=\"int\">1</config:config-item>\n"
221             " <config:config-item config:name=\"TestConfigDouble\" config:type=\"double\">5</config:config-item>"));
222 }
223 
224 static const int NumParagraphs = 30000;
225 
speedTest()226 void TestXmlWriter::speedTest()
227 {
228     QTime time;
229     time.start();
230     QString paragText = QString::fromUtf8("This is the text of the paragraph. I'm including a euro sign to test encoding issues: €");
231     QString styleName = "Heading 1";
232 
233     QFile out(QString::fromLatin1("out5.xml"));
234     if (out.open(QIODevice::WriteOnly)) {
235         KoXmlWriter writer(&out);
236         writer.startDocument("rootelem");
237         writer.startElement("rootelem");
238         for (int i = 0 ; i < NumParagraphs ; ++i) {
239             writer.startElement("paragraph");
240             writer.addAttribute("text:style-name", styleName);
241             writer.addTextNode(paragText);
242             writer.endElement();
243         }
244         writer.endElement();
245         writer.endDocument();
246     }
247     out.close();
248     out.remove();
249     qDebug("writing %i XML elements using KoXmlWriter: %i ms", NumParagraphs, time.elapsed());
250     // TODO we might want to convert this into a QBenchmark test
251 }
252 
253 QTEST_GUILESS_MAIN(TestXmlWriter)
254 #include <TestXmlWriter.moc>
255 
256