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"