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 #include <KoXmlWriter.h>
21 
22 #include <QString>
23 #include <QBuffer>
24 #include <QTest>
25 #include <QLoggingCategory>
26 
27 class TestXmlWriter : public QObject
28 {
29     Q_OBJECT
30 private Q_SLOTS:
31     void initTestCase();
32     void testDocytype();
33     void testEmtpyElement();
34     void testAttributes();
35     void testIndent();
36     void testTextNode();
37     void testTextSpan();
38     void testTextSpanWithTabCache();
39     void testProcessingInstruction();
40     void testAddManifestEntry();
41     void testEscapingLongString();
42     void testEscalingLongString2();
43     void testConfig();
44 
45     void speedTest();
46 
47 private:
48     void setup(const char *publicId = 0, const char *systemId = 0);
49     QString content();
50 
51     KoXmlWriter *writer;
52     QBuffer *buffer;
53 };
54 
initTestCase()55 void TestXmlWriter::initTestCase()
56 {
57     QLoggingCategory::setFilterRules("*.debug=false\n"
58         "calligra.lib.odf=true\ncalligra.lib.store=true");
59 }
60 
setup(const char * publicId,const char * systemId)61 void TestXmlWriter::setup(const char *publicId, const char *systemId)
62 {
63     buffer = new QBuffer();
64     buffer->open( QIODevice::WriteOnly );
65 
66     writer = new KoXmlWriter( buffer );
67     writer->startDocument( "dummy", publicId, systemId );
68     writer->startElement( "dummy" );
69 }
70 
content()71 QString TestXmlWriter::content()
72 {
73     writer->endElement();
74     writer->endDocument();
75     buffer->putChar( '\0' ); /*null-terminate*/
76     buffer->close();
77     QString stringContent = QString::fromUtf8(buffer->data());
78     int index = stringContent.indexOf("<dummy");
79     Q_ASSERT(index);
80     index = stringContent.indexOf('>', index);
81     stringContent = stringContent.mid(index+1, stringContent.length() - index - 11).trimmed();
82     return stringContent;
83 }
84 
testDocytype()85 void TestXmlWriter::testDocytype()
86 {
87     setup("foo", "bar");
88     QCOMPARE(content(), QString());
89     QString stringContent = QString::fromUtf8(buffer->data());
90     QCOMPARE(stringContent, QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
91         "<!DOCTYPE dummy PUBLIC \"foo\" \"bar\">\n<dummy/>\n"));
92 }
93 
testAttributes()94 void TestXmlWriter::testAttributes()
95 {
96     setup();
97 
98     writer->startElement("test");
99     writer->addAttribute("a", "val");
100     writer->addAttribute("b", "<\">");
101     writer->addAttribute("c", -42);
102     writer->addAttribute("d", 1234.56789012345);
103     writer->addAttributePt("e", 1234.56789012345);
104     writer->addAttribute("f", false);
105     writer->addAttribute("g", true);
106     writer->endElement();
107     QCOMPARE(content(), QString("<test a=\"val\" b=\"&lt;&quot;&gt;\" c=\"-42\" d=\"1234.56789012345\" e=\"1234.56789012345pt\" f=\"false\" g=\"true\"/>"));
108 }
109 
testEmtpyElement()110 void TestXmlWriter::testEmtpyElement()
111 {
112     setup();
113     writer->startElement("m");
114     writer->endElement();
115     QCOMPARE(content(), QString("<m/>"));
116 }
117 
testIndent()118 void TestXmlWriter::testIndent()
119 {
120     setup();
121     writer->startElement("a");
122     writer->startElement("b");
123     writer->startElement("c");
124     writer->endElement();
125     writer->endElement();
126     writer->endElement();
127     QCOMPARE(content(), QString("<a>\n  <b>\n   <c/>\n  </b>\n </a>"));
128 }
129 
testTextNode()130 void TestXmlWriter::testTextNode()
131 {
132     setup();
133     writer->startElement("a");
134     writer->startElement("b", false /*no indent*/);
135     writer->startElement("c");
136     writer->endElement();
137     writer->addTextNode("te");
138     writer->addTextNode("xt");
139     writer->endElement();
140     writer->endElement();
141     QCOMPARE(content(), QString("<a>\n  <b><c/>text</b>\n </a>"));
142 }
143 
testTextSpan()144 void TestXmlWriter::testTextSpan()
145 {
146     setup();
147     writer->startElement("p", false /*no indent*/);
148     writer->addTextSpan(QString::fromLatin1("   \t\n foo  "));
149     writer->endElement();
150     QCOMPARE(content(), QString("<p><text:s text:c=\"3\"/><text:tab/><text:line-break/> foo<text:s text:c=\"2\"/></p>"));
151 }
152 
testTextSpanWithTabCache()153 void TestXmlWriter::testTextSpanWithTabCache()
154 {
155     setup();
156     writer->startElement("p", false /*no indent*/);
157     QMap<int, int> tabCache;
158     tabCache.insert(3, 0);
159     writer->addTextSpan(QString::fromUtf8("   \t\n foö  "), tabCache);
160     writer->endElement();
161     QCOMPARE(content(), QString::fromUtf8("<p><text:s text:c=\"3\"/><text:tab text:tab-ref=\"1\"/>"
162             "<text:line-break/> foö<text:s text:c=\"2\"/></p>"));
163 }
164 
testProcessingInstruction()165 void TestXmlWriter::testProcessingInstruction()
166 {
167     setup();
168     writer->startElement("p", false /*no indent*/);
169     writer->addProcessingInstruction("opendocument foobar");
170     writer->addTextSpan(QString::fromLatin1("foo"));
171     writer->endElement();
172     QCOMPARE(content(), QString("<p><?opendocument foobar?>foo</p>"));
173 }
174 
testAddManifestEntry()175 void TestXmlWriter::testAddManifestEntry()
176 {
177     setup();
178     writer->addManifestEntry(QString::fromLatin1("foo/bar/blah"), QString::fromLatin1("mime/type"));
179     QCOMPARE(content(), QString("<manifest:file-entry manifest:media-type=\"mime/type\" "
180                 "manifest:full-path=\"foo/bar/blah\"/>"));
181 }
182 
183 
testEscapingLongString()184 void TestXmlWriter::testEscapingLongString()
185 {
186     int sz = 15000;  // must be more than KoXmlWriter::s_escapeBufferLen
187     QString x(sz);
188     x.fill('x', sz);
189     x += '&';
190     setup();
191 
192     writer->startElement("test");
193     writer->addAttribute("a", x);
194     writer->endElement();
195 
196     QString expected = "<test a=\"";
197     expected += x + "amp;\"/>";
198     QCOMPARE(content(), QString(expected));
199 }
200 
testEscalingLongString2()201 void TestXmlWriter::testEscalingLongString2()
202 {
203     QString longPath;
204     for (uint i = 0 ; i < 1000 ; ++i)
205         longPath += QString::fromLatin1("M10 10L20 20 ");
206     setup();
207     writer->startElement("test");
208     writer->addAttribute("a", longPath);
209     writer->endElement();
210     QString expected = "<test a=\"";
211     expected += longPath.toUtf8() + "\"/>";
212     QCOMPARE(content(), expected);
213 
214 }
215 
testConfig()216 void TestXmlWriter::testConfig()
217 {
218     setup();
219     const bool val = true;
220     const int num = 1;
221     const qreal numdouble = 5.0;
222     writer->addConfigItem(QString::fromLatin1("TestConfigBool"), val);
223     writer->addConfigItem(QString::fromLatin1("TestConfigInt"), num);
224     writer->addConfigItem(QString::fromLatin1("TestConfigDouble"), numdouble);
225     QCOMPARE(content(), QString("<config:config-item config:name=\"TestConfigBool\""
226             " config:type=\"boolean\">true</config:config-item>\n"
227             " <config:config-item config:name=\"TestConfigInt\" config:type=\"int\">1</config:config-item>\n"
228             " <config:config-item config:name=\"TestConfigDouble\" config:type=\"double\">5</config:config-item>"));
229 }
230 
231 static const int NumParagraphs = 30000;
232 
speedTest()233 void TestXmlWriter::speedTest()
234 {
235     QTime time;
236     time.start();
237     QString paragText = QString::fromUtf8("This is the text of the paragraph. I'm including a euro sign to test encoding issues: €");
238     QString styleName = "Heading 1";
239 
240     QFile out(QString::fromLatin1("out5.xml"));
241     if (out.open(QIODevice::WriteOnly)) {
242         KoXmlWriter writer(&out);
243         writer.startDocument("rootelem");
244         writer.startElement("rootelem");
245         for (int i = 0 ; i < NumParagraphs ; ++i) {
246             writer.startElement("paragraph");
247             writer.addAttribute("text:style-name", styleName);
248             writer.addTextNode(paragText);
249             writer.endElement();
250         }
251         writer.endElement();
252         writer.endDocument();
253     }
254     out.close();
255     out.remove();
256     qInfo()<<"writing"<<NumParagraphs<<"XML elements using KoXmlWriter:"<<time.elapsed()<<"ms";
257     // TODO we might want to convert this into a QBenchmark test
258 }
259 
260 QTEST_GUILESS_MAIN(TestXmlWriter)
261 #include <TestXmlWriter.moc>
262 
263