File indexing completed on 2024-04-21 03:48:17
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Andreas Xavier <andxav at zoho dot com> 0003 * SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "keduvocdocument.h" 0007 #include "keduvockvtml2reader.h" 0008 #include "kvtml2defs.h" 0009 #include "readerTestHelpers.h" 0010 #include "readermanager.h" 0011 0012 #include <QDebug> 0013 #include <QTest> 0014 0015 namespace Kvtml2ReaderUnitTests 0016 { 0017 0018 /** 0019 * @file readerkvtml2test.cpp 0020 * Unit Tests of parsing of KVTML 2.0 XML strings 0021 * 0022 * Each test creates a QString of XML 0023 * reads, parses and verifies the parse. The portions of the 0024 * XML generator object relevant to each test are defined in this file. 0025 * Refer to the dtd file to determine what should parse and what should 0026 * fail to parse. 0027 * */ 0028 0029 /** 0030 * @brief Unit Tests of parsing of KVTML 2.0 strings 0031 * 0032 * Each test creates a string of XML 0033 * reads, parses and verifies the parse. 0034 * */ 0035 0036 class Kvtml2ReaderTest : public QObject 0037 { 0038 Q_OBJECT 0039 0040 private slots: 0041 /** Smallest passing KVTML 2 file according to DTD file. 0042 * Bug 336780 - Missing Identifier causes segfault in Parley 0043 * Bug 337352 - Missing Identifier causes segfault in kvocdoc 0044 * */ 0045 void testParseExpectedMinimalXMLAccordingToDTD(); 0046 /** Smallest passing KVTML 2 file according to code*/ 0047 void testParseActualMinimalXML(); 0048 /** Missing Info*/ 0049 void testParseMissingInformation(); 0050 /** Missing Ids*/ 0051 void testParseMissingIdentifiers(); 0052 /** Missing Entries*/ 0053 void testParseMissingEntries(); 0054 /** Missing Title*/ 0055 void testParseMissingTitle(); 0056 /** One Identifier With Locale*/ 0057 void testParseWithLocale(); 0058 /** Missing Locale*/ 0059 void testParseMissingLocale(); 0060 /** Parsing Sound URL*/ 0061 void testParseSoundUrl(); 0062 void testParseSoundUrl_data(); 0063 /** Parsing Image URL*/ 0064 void testParseImageUrl(); 0065 void testParseImageUrl_data(); 0066 0067 private: 0068 }; 0069 0070 /** 0071 * @brief XMLGenerator builds the XML doc 0072 * This is to minimize repetition in the unit tests. 0073 * */ 0074 class XMLGenerator : public QDomDocument 0075 { 0076 public: 0077 XMLGenerator(); 0078 ~XMLGenerator(); 0079 0080 // String Data 0081 const QString generator, author, license, comment, date, category, title; 0082 const QString lang0name, lang0loc, lang1name, lang1loc; 0083 0084 // Generators of parts of the XML Doc 0085 XMLGenerator &preamble(); 0086 XMLGenerator &minimalInfo(); 0087 XMLGenerator &missingTitle(); 0088 XMLGenerator &minimalIds(); 0089 XMLGenerator &missingLocale(); 0090 XMLGenerator &minimalEntries(); 0091 XMLGenerator &minimalHeader(); 0092 0093 XMLGenerator &blankInfo(); 0094 XMLGenerator &addTitle(); 0095 0096 XMLGenerator &blankIdentifier(const int ii); 0097 XMLGenerator &addLocale(const int ii); 0098 XMLGenerator &blankEntry(int entryId); 0099 XMLGenerator &blankTranslation(int translationId); 0100 XMLGenerator &addSoundUrl(const QString &soundUrl); 0101 XMLGenerator &addImageUrl(const QString &soundUrl); 0102 XMLGenerator &minimalLessons(); 0103 XMLGenerator &blankContainer(); 0104 XMLGenerator &addContainerEntry(int entryId); 0105 0106 /** Convert to the QIODevice that the reader expects*/ 0107 QIODevice *toQIODevice(); 0108 QByteArray m_barray; 0109 QBuffer *m_buffer; 0110 0111 KEduVocDocument::FileType myType; 0112 }; 0113 0114 XMLGenerator::XMLGenerator() 0115 : QDomDocument() 0116 , generator(QStringLiteral("Parse KVTML2 Unit Tests")) 0117 , author(QStringLiteral("Parse KVTML2 Test Author")) 0118 , license(QStringLiteral("test license")) 0119 , comment(QStringLiteral("comment")) 0120 , date(QStringLiteral("2014-01-01")) 0121 , category(QStringLiteral("test document")) 0122 , title(QStringLiteral("Parse KVTML2 Test Title")) 0123 0124 , lang0name(QStringLiteral("English")) 0125 , lang0loc(QStringLiteral("en")) 0126 , lang1name(QStringLiteral("German")) 0127 , lang1loc(QStringLiteral("de")) 0128 , m_buffer(nullptr) 0129 , myType(KEduVocDocument::Kvtml) 0130 0131 { 0132 } 0133 XMLGenerator::~XMLGenerator() 0134 { 0135 if (m_buffer) { 0136 delete m_buffer; 0137 } 0138 } 0139 0140 QIODevice *XMLGenerator::toQIODevice() 0141 { 0142 m_barray = this->toString(4).toLatin1(); 0143 m_buffer = new QBuffer(&m_barray); 0144 m_buffer->open(QIODevice::ReadOnly); 0145 return m_buffer; 0146 } 0147 0148 XMLGenerator &XMLGenerator::preamble() 0149 { 0150 this->setContent(QStringLiteral("<!DOCTYPE kvtml PUBLIC \"kvtml2.dtd\" \"http://edu.kde.org/kvtml/kvtml2.dtd\">\n"), true, nullptr, nullptr, nullptr); 0151 this->appendChild(this->createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""))); 0152 0153 QDomElement domElementKvtml = this->createElement(KVTML_TAG); 0154 this->appendChild(domElementKvtml); 0155 domElementKvtml.setAttribute(KVTML_VERSION, (QString)QStringLiteral("2.0")); 0156 0157 return *this; 0158 } 0159 0160 XMLGenerator &XMLGenerator::blankInfo() 0161 { 0162 QDomElement info = this->createElement(KVTML_INFORMATION); 0163 elementsByTagName(KVTML_TAG).at(0).appendChild(info); 0164 return *this; 0165 } 0166 XMLGenerator &XMLGenerator::addTitle() 0167 { 0168 QDomElement mtitle = this->createElement(KVTML_TITLE); 0169 QDomText mtitletext = this->createTextNode(title); 0170 mtitle.appendChild(mtitletext); 0171 elementsByTagName(KVTML_INFORMATION).at(0).appendChild(mtitle); 0172 return *this; 0173 } 0174 XMLGenerator &XMLGenerator::minimalInfo() 0175 { 0176 return blankInfo().addTitle(); 0177 } 0178 0179 XMLGenerator &XMLGenerator::minimalIds() 0180 { 0181 QDomElement elem = this->createElement(KVTML_IDENTIFIERS); 0182 elementsByTagName(KVTML_TAG).at(0).appendChild(elem); 0183 return *this; 0184 } 0185 0186 XMLGenerator &XMLGenerator::minimalEntries() 0187 { 0188 QDomElement elem = this->createElement(KVTML_ENTRIES); 0189 elementsByTagName(KVTML_TAG).at(0).appendChild(elem); 0190 return *this; 0191 } 0192 0193 XMLGenerator &XMLGenerator::minimalHeader() 0194 { 0195 return preamble().minimalInfo().minimalIds().minimalEntries(); 0196 } 0197 0198 XMLGenerator &XMLGenerator::blankIdentifier(const int ii) 0199 { 0200 QDomElement id = this->createElement(KVTML_IDENTIFIER); 0201 QString str; 0202 id.setAttribute(QStringLiteral("id"), str.setNum(ii)); 0203 elementsByTagName(KVTML_IDENTIFIERS).at(0).appendChild(id); 0204 return *this; 0205 } 0206 XMLGenerator &XMLGenerator::addLocale(const int ii) 0207 { 0208 QDomElement x = this->createElement(KVTML_LOCALE); 0209 QDomText loctext = this->createTextNode(ii ? lang1loc : lang0loc); 0210 x.appendChild(loctext); 0211 elementsByTagName(KVTML_IDENTIFIER).at(ii).appendChild(x); 0212 return *this; 0213 } 0214 0215 XMLGenerator &XMLGenerator::blankEntry(int entryId) 0216 { 0217 QDomElement elem = this->createElement(KVTML_ENTRY); 0218 elem.setAttribute(QStringLiteral("id"), QString::number(entryId)); 0219 elementsByTagName(KVTML_ENTRIES).at(0).appendChild(elem); 0220 return *this; 0221 } 0222 0223 XMLGenerator &XMLGenerator::blankTranslation(int translationId) 0224 { 0225 QDomElement elem = this->createElement(KVTML_TRANSLATION); 0226 elem.setAttribute(QStringLiteral("id"), QString::number(translationId)); 0227 elementsByTagName(KVTML_ENTRY).at(0).appendChild(elem); 0228 return *this; 0229 } 0230 0231 XMLGenerator &XMLGenerator::addSoundUrl(const QString &soundUrl) 0232 { 0233 QDomElement elem = this->createElement(KVTML_SOUND); 0234 elem.appendChild(this->createTextNode(soundUrl)); 0235 elementsByTagName(KVTML_TRANSLATION).at(0).appendChild(elem); 0236 return *this; 0237 } 0238 0239 XMLGenerator &XMLGenerator::addImageUrl(const QString &imageUrl) 0240 { 0241 QDomElement elem = this->createElement(KVTML_IMAGE); 0242 elem.appendChild(this->createTextNode(imageUrl)); 0243 elementsByTagName(KVTML_TRANSLATION).at(0).appendChild(elem); 0244 return *this; 0245 } 0246 0247 XMLGenerator &XMLGenerator::minimalLessons() 0248 { 0249 QDomElement elem = this->createElement(KVTML_LESSONS); 0250 elementsByTagName(KVTML_TAG).at(0).appendChild(elem); 0251 return *this; 0252 } 0253 0254 XMLGenerator &XMLGenerator::blankContainer() 0255 { 0256 QDomElement elem = this->createElement(KVTML_CONTAINER); 0257 elementsByTagName(KVTML_LESSONS).at(0).appendChild(elem); 0258 return *this; 0259 } 0260 0261 XMLGenerator &XMLGenerator::addContainerEntry(int entryId) 0262 { 0263 QDomElement elem = this->createElement(KVTML_ENTRY); 0264 elem.setAttribute(QStringLiteral("id"), QString::number(entryId)); 0265 elementsByTagName(KVTML_CONTAINER).at(0).appendChild(elem); 0266 return *this; 0267 } 0268 0269 void Kvtml2ReaderTest::testParseExpectedMinimalXMLAccordingToDTD() 0270 { 0271 XMLGenerator gen; 0272 gen.preamble().minimalInfo().minimalIds().minimalEntries(); 0273 0274 ///@todo Either Fix the DTD to agree with the code or fix the code to agree with the DTD 0275 KVOCREADER_DONT_EXPECT(gen.toString(4), KEduVocDocument::NoError, gen.myType); 0276 } 0277 void Kvtml2ReaderTest::testParseActualMinimalXML() 0278 { 0279 XMLGenerator gen; 0280 gen.preamble().minimalInfo().minimalIds().blankIdentifier(0).addLocale(0).minimalEntries(); 0281 0282 KVOCREADER_EXPECT(gen.toString(4), KEduVocDocument::NoError, gen.myType); 0283 } 0284 0285 void Kvtml2ReaderTest::testParseMissingInformation() 0286 { 0287 XMLGenerator gen; 0288 gen.preamble().minimalIds().minimalEntries(); 0289 0290 KVOCREADER_EXPECT(gen.toString(4), KEduVocDocument::FileReaderFailed, gen.myType); 0291 } 0292 void Kvtml2ReaderTest::testParseMissingIdentifiers() 0293 { 0294 XMLGenerator gen; 0295 gen.preamble().minimalInfo().minimalEntries(); 0296 0297 ///@todo Either Fix the DTD to agree with the code or fix the code to agree with the DTD 0298 KVOCREADER_DONT_EXPECT(gen.toString(4), KEduVocDocument::FileReaderFailed, gen.myType); 0299 } 0300 void Kvtml2ReaderTest::testParseMissingEntries() 0301 { 0302 XMLGenerator gen; 0303 gen.preamble().minimalInfo().minimalIds(); 0304 0305 KVOCREADER_EXPECT(gen.toString(4), KEduVocDocument::FileReaderFailed, gen.myType); 0306 } 0307 void Kvtml2ReaderTest::testParseMissingTitle() 0308 { 0309 XMLGenerator gen; 0310 gen.preamble().blankInfo().minimalIds().minimalEntries(); 0311 0312 KVOCREADER_EXPECT(gen.toString(4), KEduVocDocument::FileReaderFailed, gen.myType); 0313 } 0314 0315 void Kvtml2ReaderTest::testParseWithLocale() 0316 { 0317 XMLGenerator gen; 0318 gen.preamble().blankInfo().minimalIds().minimalEntries().blankIdentifier(0).addLocale(0); 0319 0320 KVOCREADER_EXPECT(gen.toString(4), KEduVocDocument::NoError, gen.myType); 0321 } 0322 0323 void Kvtml2ReaderTest::testParseMissingLocale() 0324 { 0325 XMLGenerator gen; 0326 gen.preamble().blankInfo().minimalIds().minimalEntries().blankIdentifier(0); 0327 0328 ///@todo Either Fix the DTD to agree with the code or fix the code to agree with the DTD 0329 KVOCREADER_DONT_EXPECT(gen.toString(4), KEduVocDocument::FileReaderFailed, gen.myType); 0330 } 0331 0332 void Kvtml2ReaderTest::testParseSoundUrl() 0333 { 0334 QFETCH(QString, urlStoredInKvtmlFile); 0335 QFETCH(QString, expectedParsedUrl); 0336 QFETCH(QString, kvtmlFileUrl); 0337 0338 // Create a minimal KVTML doc with a sound url 0339 XMLGenerator gen; 0340 gen.minimalHeader().blankIdentifier(0).blankEntry(0).blankTranslation(0); 0341 gen.addSoundUrl(urlStoredInKvtmlFile); 0342 gen.minimalLessons().blankContainer().addContainerEntry(0); 0343 0344 // Parse KVTML doc with KEduVocDocument 0345 QByteArray array(gen.toString(4).toLatin1()); 0346 QScopedPointer<QBuffer> buffer(new QBuffer(&array)); 0347 buffer->open(QIODevice::ReadOnly); 0348 ReaderManager::ReaderPtr reader(ReaderManager::reader(*buffer)); 0349 KEduVocDocument testDoc; 0350 testDoc.setUrl(QUrl(kvtmlFileUrl)); 0351 KEduVocDocument::ErrorCode errorCode(reader->read(testDoc)); 0352 if (errorCode != KEduVocDocument::NoError) { 0353 QFAIL("Test document could not be filled from buffer."); 0354 } 0355 0356 QUrl urlReadFromDoc = testDoc.lesson()->childContainer(0)->entry(0)->translation(0)->soundUrl(); 0357 QCOMPARE(urlReadFromDoc.toString(), expectedParsedUrl); 0358 } 0359 0360 void Kvtml2ReaderTest::testParseSoundUrl_data() 0361 { 0362 QTest::addColumn<QString>("urlStoredInKvtmlFile"); 0363 QTest::addColumn<QString>("expectedParsedUrl"); 0364 QTest::addColumn<QString>("kvtmlFileUrl"); 0365 0366 QTest::newRow("Relative Path") << "file:sounds/bar.mp3" // Url stored in KVTML file 0367 << "file:///home/foo/sounds/bar.mp3" // Expected parsed url 0368 << "file:///home/foo/bar.kvtml"; // KVTML file url 0369 0370 QTest::newRow("Absolute Path") << "file:///data/sounds/bar.mp3" // Url stored in KVTML file 0371 << "file:///data/sounds/bar.mp3" // Expected parsed url 0372 << "file:///home/foo/bar.kvtml"; // KVTML file url 0373 0374 QTest::newRow("Remote Path") << "http://example.com/sounds/bar.mp3" // Url stored in KVTML file 0375 << "http://example.com/sounds/bar.mp3" // Expected parsed url 0376 << "file:///home/foo/bar.kvtml"; // KVTML file url 0377 } 0378 0379 void Kvtml2ReaderTest::testParseImageUrl() 0380 { 0381 QFETCH(QString, urlStoredInKvtmlFile); 0382 QFETCH(QString, expectedParsedUrl); 0383 QFETCH(QString, kvtmlFileUrl); 0384 0385 // Create a minimal KVTML doc with a image url 0386 XMLGenerator gen; 0387 gen.minimalHeader().blankIdentifier(0).blankEntry(0).blankTranslation(0); 0388 gen.addImageUrl(urlStoredInKvtmlFile); 0389 gen.minimalLessons().blankContainer().addContainerEntry(0); 0390 0391 // Parse KVTML doc with KEduVocDocument 0392 QByteArray array(gen.toString(4).toLatin1()); 0393 QScopedPointer<QBuffer> buffer(new QBuffer(&array)); 0394 buffer->open(QIODevice::ReadOnly); 0395 ReaderManager::ReaderPtr reader(ReaderManager::reader(*buffer)); 0396 KEduVocDocument testDoc; 0397 testDoc.setUrl(QUrl(kvtmlFileUrl)); 0398 KEduVocDocument::ErrorCode errorCode(reader->read(testDoc)); 0399 if (errorCode != KEduVocDocument::NoError) { 0400 QFAIL("Test document could not be filled from buffer."); 0401 } 0402 0403 QUrl urlReadFromDoc = testDoc.lesson()->childContainer(0)->entry(0)->translation(0)->imageUrl(); 0404 QCOMPARE(urlReadFromDoc.toString(), expectedParsedUrl); 0405 } 0406 0407 void Kvtml2ReaderTest::testParseImageUrl_data() 0408 { 0409 QTest::addColumn<QString>("urlStoredInKvtmlFile"); 0410 QTest::addColumn<QString>("expectedParsedUrl"); 0411 QTest::addColumn<QString>("kvtmlFileUrl"); 0412 0413 QTest::newRow("Relative Path") << "file:images/bar.png" // Url stored in KVTML file 0414 << "file:///home/foo/images/bar.png" // Expected parsed url 0415 << "file:///home/foo/bar.kvtml"; // KVTML file url 0416 0417 QTest::newRow("Absolute Path") << "file:///data/images/bar.png" // Url stored in KVTML file 0418 << "file:///data/images/bar.png" // Expected parsed url 0419 << "file:///home/foo/bar.kvtml"; // KVTML file url 0420 0421 QTest::newRow("Remote Path") << "http://example.com/images/bar.png" // Url stored in KVTML file 0422 << "http://example.com/images/bar.png" // Expected parsed url 0423 << "file:///home/foo/bar.kvtml"; // KVTML file url 0424 } 0425 } 0426 0427 QTEST_MAIN(Kvtml2ReaderUnitTests::Kvtml2ReaderTest) 0428 0429 #include "readerkvtml2test.moc"