File indexing completed on 2024-05-12 05:10:06
0001 /*************************************************************************** 0002 Copyright (C) 2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include <config.h> 0026 #include "tellicoreadtest.h" 0027 0028 #include "../translators/tellicoimporter.h" 0029 #include "../collections/bookcollection.h" 0030 #include "../collections/bibtexcollection.h" 0031 #include "../collections/coincollection.h" 0032 #include "../collections/musiccollection.h" 0033 #include "../collectionfactory.h" 0034 #include "../translators/tellicoxmlexporter.h" 0035 #include "../translators/tellico_xml.h" 0036 #include "../translators/xslthandler.h" 0037 #include "../images/imagefactory.h" 0038 #include "../images/image.h" 0039 #include "../fieldformat.h" 0040 #include "../entry.h" 0041 #include "../document.h" 0042 #include "../utils/xmlhandler.h" 0043 #include "../utils/string_utils.h" 0044 0045 #include <KLocalizedString> 0046 0047 #include <QTest> 0048 #include <QNetworkInterface> 0049 #include <QDate> 0050 #include <QTextCodec> 0051 #include <QStandardPaths> 0052 0053 QTEST_GUILESS_MAIN( TellicoReadTest ) 0054 0055 #define QSL(x) QStringLiteral(x) 0056 #define TELLICOREAD_NUMBER_OF_CASES 11 0057 0058 static bool hasNetwork() { 0059 #ifdef ENABLE_NETWORK_TESTS 0060 foreach(const QNetworkInterface& net, QNetworkInterface::allInterfaces()) { 0061 if(net.flags().testFlag(QNetworkInterface::IsUp) && !net.flags().testFlag(QNetworkInterface::IsLoopBack)) { 0062 return true; 0063 } 0064 } 0065 #endif 0066 return false; 0067 } 0068 0069 void TellicoReadTest::initTestCase() { 0070 QStandardPaths::setTestModeEnabled(true); 0071 KLocalizedString::setApplicationDomain("tellico"); 0072 // need to register this first 0073 Tellico::RegisterCollection<Tellico::Data::BookCollection> registerBook(Tellico::Data::Collection::Book, "book"); 0074 Tellico::RegisterCollection<Tellico::Data::BibtexCollection> registerBibtex(Tellico::Data::Collection::Bibtex, "bibtex"); 0075 Tellico::RegisterCollection<Tellico::Data::CoinCollection> registerCoin(Tellico::Data::Collection::Coin, "coin"); 0076 Tellico::RegisterCollection<Tellico::Data::Collection> registerBase(Tellico::Data::Collection::Base, "entry"); 0077 Tellico::RegisterCollection<Tellico::Data::MusicCollection> registerAlbum(Tellico::Data::Collection::Album, "album"); 0078 0079 for(int i = 1; i <= TELLICOREAD_NUMBER_OF_CASES; ++i) { 0080 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA(QSL("data/books-format%1.bc").arg(i))); 0081 0082 Tellico::Import::TellicoImporter importer(url); 0083 Tellico::Data::CollPtr coll = importer.collection(); 0084 m_collections.append(coll); 0085 } 0086 Tellico::ImageFactory::init(); 0087 } 0088 0089 void TellicoReadTest::init() { 0090 Tellico::ImageFactory::clean(true); 0091 } 0092 0093 void TellicoReadTest::testBookCollection() { 0094 Tellico::Data::CollPtr coll1 = m_collections[0]; 0095 // skip the first one 0096 for(int i = 1; i < m_collections.count(); ++i) { 0097 Tellico::Data::CollPtr coll2 = m_collections[i]; 0098 QVERIFY(coll2); 0099 QCOMPARE(coll1->type(), coll2->type()); 0100 QCOMPARE(coll1->title(), coll2->title()); 0101 QCOMPARE(coll1->entryCount(), coll2->entryCount()); 0102 } 0103 } 0104 0105 void TellicoReadTest::testEntries() { 0106 QFETCH(QString, fieldName); 0107 0108 Tellico::Data::FieldPtr field1 = m_collections[0]->fieldByName(fieldName); 0109 0110 // skip the first one 0111 for(int i = 1; i < m_collections.count(); ++i) { 0112 Tellico::Data::FieldPtr field2 = m_collections[i]->fieldByName(fieldName); 0113 if(field1 && field2) { 0114 QCOMPARE(field1->name(), field2->name()); 0115 QCOMPARE(field1->title(), field2->title()); 0116 QCOMPARE(field1->category(), field2->category()); 0117 QCOMPARE(field1->type(), field2->type()); 0118 QCOMPARE(field1->flags(), field2->flags()); 0119 QCOMPARE(field1->propertyList(), field2->propertyList()); 0120 } 0121 0122 for(int j = 0; j < m_collections[0]->entryCount(); ++j) { 0123 // don't test id values since the initial value has changed from 0 to 1 0124 Tellico::Data::EntryPtr entry1 = m_collections[0]->entries().at(j); 0125 Tellico::Data::EntryPtr entry2 = m_collections[i]->entries().at(j); 0126 QVERIFY(entry1); 0127 QVERIFY(entry2); 0128 QCOMPARE(entry1->field(fieldName), entry2->field(fieldName)); 0129 } 0130 } 0131 } 0132 0133 void TellicoReadTest::testEntries_data() { 0134 QTest::addColumn<QString>("fieldName"); 0135 0136 QTest::newRow("title") << QSL("title"); 0137 QTest::newRow("author") << QSL("author"); 0138 QTest::newRow("publisher") << QSL("publisher"); 0139 QTest::newRow("keywords") << QSL("keywords"); 0140 QTest::newRow("keyword") << QSL("keyword"); 0141 QTest::newRow("genre") << QSL("genre"); 0142 QTest::newRow("isbn") << QSL("isbn"); 0143 QTest::newRow("pub_year") << QSL("pub_year"); 0144 QTest::newRow("rating") << QSL("rating"); 0145 QTest::newRow("comments") << QSL("comments"); 0146 } 0147 0148 void TellicoReadTest::testCoinCollection() { 0149 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("data/coins-format9.tc")); 0150 0151 Tellico::Import::TellicoImporter importer(url); 0152 Tellico::Data::CollPtr coll = importer.collection(); 0153 0154 QVERIFY(coll); 0155 QCOMPARE(coll->type(), Tellico::Data::Collection::Coin); 0156 0157 Tellico::Data::FieldPtr field = coll->fieldByName(QStringLiteral("title")); 0158 // old field has Dependent value, now is Line 0159 QVERIFY(field); 0160 QCOMPARE(field->type(), Tellico::Data::Field::Line); 0161 QCOMPARE(field->title(), QSL("Title")); 0162 QVERIFY(field->hasFlag(Tellico::Data::Field::Derived)); 0163 0164 Tellico::Data::EntryPtr entry = coll->entries().at(0); 0165 // test creating the derived title 0166 QCOMPARE(entry->title(), QSL("1974D Jefferson Nickel 0.05")); 0167 } 0168 0169 void TellicoReadTest::testBibtexCollection() { 0170 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("data/bibtex-format11.tc")); 0171 0172 Tellico::Import::TellicoImporter importer(url); 0173 Tellico::Data::CollPtr coll = importer.collection(); 0174 Tellico::Data::BibtexCollection* bColl = dynamic_cast<Tellico::Data::BibtexCollection*>(coll.data()); 0175 0176 QVERIFY(coll); 0177 QCOMPARE(coll->type(), Tellico::Data::Collection::Bibtex); 0178 QVERIFY(bColl); 0179 QVERIFY(!bColl->preamble().isEmpty()); 0180 0181 auto macroList = bColl->macroList(); 0182 QCOMPARE(macroList.count(), 13); // includes 12 months plus the one in the file 0183 QVERIFY(!macroList.value(QLatin1String("SPE")).isEmpty()); 0184 0185 auto borrowerList = coll->borrowers(); 0186 QCOMPARE(borrowerList.count(), 1); 0187 auto borr1 = borrowerList.front(); 0188 QCOMPARE(borr1->count(), 1); 0189 QCOMPARE(borr1->name(), QStringLiteral("кириллица")); 0190 0191 auto filterList = coll->filters(); 0192 QCOMPARE(filterList.count(), 1); 0193 auto filter1 = filterList.front(); 0194 QCOMPARE(filter1->name(), QStringLiteral("1990")); 0195 0196 Tellico::Export::TellicoXMLExporter exporter(coll); 0197 exporter.setEntries(coll->entries()); 0198 exporter.setOptions(exporter.options() | Tellico::Export::ExportComplete); 0199 Tellico::Import::TellicoImporter importer2(exporter.text()); 0200 Tellico::Data::CollPtr coll2 = importer2.collection(); 0201 Tellico::Data::BibtexCollection* bColl2 = dynamic_cast<Tellico::Data::BibtexCollection*>(coll2.data()); 0202 0203 QVERIFY(coll2); 0204 QCOMPARE(coll2->type(), coll->type()); 0205 QCOMPARE(coll2->entryCount(), coll->entryCount()); 0206 QVERIFY(bColl2); 0207 QCOMPARE(bColl2->preamble(), bColl->preamble()); 0208 QCOMPARE(bColl2->macroList(), bColl->macroList()); 0209 0210 QCOMPARE(coll2->filters().count(), coll->filters().count()); 0211 auto filter2 = coll->filters().front(); 0212 QCOMPARE(filter1->name(), filter2->name()); 0213 QCOMPARE(filter1->count(), filter2->count()); 0214 QCOMPARE(filter1->op(), filter2->op()); 0215 0216 QCOMPARE(coll2->borrowers().count(), coll->borrowers().count()); 0217 auto borr2 = coll2->borrowers().front(); 0218 QCOMPARE(borr1->name(), borr2->name()); 0219 QCOMPARE(borr1->uid(), borr2->uid()); 0220 QCOMPARE(borr1->count(), borr2->count()); 0221 auto loan1 = borr1->loans().front(); 0222 auto loan2 = borr2->loans().front(); 0223 QCOMPARE(loan1->loanDate(), loan2->loanDate()); 0224 QCOMPARE(loan1->borrower()->name(), borr1->name()); 0225 QCOMPARE(loan1->dueDate(), loan2->dueDate()); 0226 QCOMPARE(loan1->note(), loan2->note()); 0227 QCOMPARE(loan1->uid(), loan2->uid()); 0228 QCOMPARE(loan1->entry()->title(), loan2->entry()->title()); 0229 } 0230 0231 void TellicoReadTest::testTableData() { 0232 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("/data/tabletest.tc")); 0233 0234 Tellico::Import::TellicoImporter importer(url); 0235 Tellico::Data::CollPtr coll = importer.collection(); 0236 0237 QVERIFY(coll); 0238 QCOMPARE(coll->entryCount(), 3); 0239 0240 Tellico::Export::TellicoXMLExporter exporter(coll); 0241 exporter.setEntries(coll->entries()); 0242 Tellico::Import::TellicoImporter importer2(exporter.text()); 0243 Tellico::Data::CollPtr coll2 = importer2.collection(); 0244 0245 QVERIFY(coll2); 0246 QCOMPARE(coll2->type(), coll->type()); 0247 QCOMPARE(coll2->entryCount(), coll->entryCount()); 0248 0249 foreach(Tellico::Data::EntryPtr e1, coll->entries()) { 0250 Tellico::Data::EntryPtr e2 = coll2->entryById(e1->id()); 0251 QVERIFY(e2); 0252 foreach(Tellico::Data::FieldPtr f, coll->fields()) { 0253 QCOMPARE(f->name() + e1->field(f), f->name() + e2->field(f)); 0254 } 0255 } 0256 0257 // test table value concatenation 0258 Tellico::Data::EntryPtr e3(new Tellico::Data::Entry(coll)); 0259 coll->addEntries(e3); 0260 QString value = QSL("11a") + Tellico::FieldFormat::delimiterString() + QSL("11b") 0261 + Tellico::FieldFormat::columnDelimiterString() + QSL("12") 0262 + Tellico::FieldFormat::columnDelimiterString() + QSL("13") 0263 + Tellico::FieldFormat::rowDelimiterString() + QSL("21") 0264 + Tellico::FieldFormat::columnDelimiterString() + QSL("22") 0265 + Tellico::FieldFormat::columnDelimiterString() + QSL("23"); 0266 e3->setField(QSL("table"), value); 0267 QStringList groups = e3->groupNamesByFieldName(QStringLiteral("table")); 0268 QCOMPARE(groups.count(), 3); 0269 // the order of the group names is not stable (it uses QSet::toList) 0270 QCOMPARE(groups.size(), 3); 0271 QVERIFY(groups.contains(QSL("11a"))); 0272 QVERIFY(groups.contains(QSL("11b"))); 0273 QVERIFY(groups.contains(QSL("21"))); 0274 0275 // test having empty value in table 0276 Tellico::Data::EntryPtr e = coll2->entryById(2); 0277 QVERIFY(e); 0278 const QStringList rows = Tellico::FieldFormat::splitTable(e->field(QSL("table"))); 0279 QCOMPARE(rows.count(), 1); 0280 const QStringList cols = Tellico::FieldFormat::splitRow(rows.at(0)); 0281 QCOMPARE(cols.count(), 3); 0282 } 0283 0284 void TellicoReadTest::testDuplicateLoans() { 0285 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("/data/duplicate_loan.xml")); 0286 0287 Tellico::Import::TellicoImporter importer(url); 0288 Tellico::Data::CollPtr coll = importer.collection(); 0289 0290 QVERIFY(coll); 0291 0292 QCOMPARE(coll->borrowers().count(), 1); 0293 0294 Tellico::Data::BorrowerPtr bor = coll->borrowers().first(); 0295 QVERIFY(bor); 0296 0297 QCOMPARE(bor->loans().count(), 1); 0298 } 0299 0300 void TellicoReadTest::testDuplicateBorrowers() { 0301 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("/data/duplicate_borrower.xml")); 0302 0303 Tellico::Import::TellicoImporter importer(url); 0304 Tellico::Data::CollPtr coll = importer.collection(); 0305 0306 QVERIFY(coll); 0307 0308 QCOMPARE(coll->borrowers().count(), 1); 0309 0310 Tellico::Data::BorrowerPtr bor = coll->borrowers().first(); 0311 QVERIFY(bor); 0312 0313 QCOMPARE(bor->loans().count(), 2); 0314 } 0315 0316 void TellicoReadTest::testLocalImage() { 0317 // this is the md5 hash of the tellico.png icon, used as an image id 0318 const QString imageId(QSL("dde5bf2cbd90fad8635a26dfb362e0ff.png")); 0319 // not yet loaded 0320 QVERIFY(!Tellico::ImageFactory::self()->hasImageInMemory(imageId)); 0321 QVERIFY(!Tellico::ImageFactory::self()->hasImageInfo(imageId)); 0322 0323 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("/data/image_test.xml")); 0324 QFile f(url.toLocalFile()); 0325 QVERIFY(f.exists()); 0326 QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text)); 0327 0328 QTextStream in(&f); 0329 QString fileText = in.readAll(); 0330 // replace %COVER% with image file location 0331 fileText.replace(QSL("%COVER%"), 0332 QFINDTESTDATA("../../icons/tellico.png")); 0333 0334 Tellico::Import::TellicoImporter importer(fileText); 0335 Tellico::Data::CollPtr coll = importer.collection(); 0336 QVERIFY(coll); 0337 QCOMPARE(coll->entries().count(), 1); 0338 0339 Tellico::Data::EntryPtr entry = coll->entries().at(0); 0340 QVERIFY(entry); 0341 QCOMPARE(entry->field(QStringLiteral("cover")), imageId); 0342 0343 // the image should be in local memory now 0344 QVERIFY(Tellico::ImageFactory::self()->hasImageInMemory(imageId)); 0345 QVERIFY(Tellico::ImageFactory::self()->hasImageInfo(imageId)); 0346 0347 const Tellico::Data::Image& img = Tellico::ImageFactory::imageById(imageId); 0348 QVERIFY(!img.isNull()); 0349 } 0350 0351 void TellicoReadTest::testRemoteImage() { 0352 if(!hasNetwork()) QSKIP("This test requires network access", SkipSingle); 0353 0354 // this is the md5 hash of the logo.png icon, used as an image id 0355 const QString imageId(QSL("ecaf5185c4016881aaabb4933211d5d6.png")); 0356 // not yet loaded 0357 QVERIFY(!Tellico::ImageFactory::self()->hasImageInMemory(imageId)); 0358 QVERIFY(!Tellico::ImageFactory::self()->hasImageInfo(imageId)); 0359 0360 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("/data/image_test.xml")); 0361 QFile f(url.toLocalFile()); 0362 QVERIFY(f.exists()); 0363 QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text)); 0364 0365 QTextStream in(&f); 0366 QString fileText = in.readAll(); 0367 // replace %COVER% with image file location 0368 fileText.replace(QSL("%COVER%"), 0369 QSL("https://tellico-project.org/wp-content/uploads/96-tellico.png")); 0370 0371 Tellico::Import::TellicoImporter importer(fileText); 0372 Tellico::Data::CollPtr coll = importer.collection(); 0373 QVERIFY(coll); 0374 QCOMPARE(coll->entries().count(), 1); 0375 0376 Tellico::Data::EntryPtr entry = coll->entries().at(0); 0377 QVERIFY(entry); 0378 QCOMPARE(entry->field(QStringLiteral("cover")), imageId); 0379 0380 // the image should be in local memory now 0381 QVERIFY(Tellico::ImageFactory::self()->hasImageInMemory(imageId)); 0382 QVERIFY(Tellico::ImageFactory::self()->hasImageInfo(imageId)); 0383 0384 const Tellico::Data::Image& img = Tellico::ImageFactory::imageById(imageId); 0385 QVERIFY(!img.isNull()); 0386 } 0387 0388 void TellicoReadTest::testDataImage() { 0389 // this is the md5 hash of the tellico.png icon, used as an image id 0390 const QString imageId(QSL("dde5bf2cbd90fad8635a26dfb362e0ff.png")); 0391 // not yet loaded 0392 QVERIFY(!Tellico::ImageFactory::self()->hasImageInMemory(imageId)); 0393 QVERIFY(!Tellico::ImageFactory::self()->hasImageInfo(imageId)); 0394 0395 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("/data/image_test.xml")); 0396 QFile f(url.toLocalFile()); 0397 QVERIFY(f.exists()); 0398 QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text)); 0399 0400 Tellico::Data::Image testImage(QFINDTESTDATA("../../icons/tellico.png")); 0401 QVERIFY(!testImage.isNull()); 0402 const QByteArray imgData = testImage.byteArray().toBase64(); 0403 0404 QTextStream in(&f); 0405 QString fileText = in.readAll(); 0406 // replace %COVER% with image file location 0407 fileText.replace(QSL("%COVER%"), 0408 QString::fromUtf8("data:image/png;base64,") + 0409 QLatin1String(imgData)); 0410 0411 Tellico::Import::TellicoImporter importer(fileText); 0412 Tellico::Data::CollPtr coll = importer.collection(); 0413 QVERIFY(coll); 0414 QCOMPARE(coll->entries().count(), 1); 0415 0416 Tellico::Data::EntryPtr entry = coll->entries().at(0); 0417 QVERIFY(entry); 0418 QCOMPARE(entry->field(QStringLiteral("cover")), imageId); 0419 0420 // the image should be in local memory now 0421 QVERIFY(Tellico::ImageFactory::self()->hasImageInMemory(imageId)); 0422 QVERIFY(Tellico::ImageFactory::self()->hasImageInfo(imageId)); 0423 0424 const Tellico::Data::Image& img = Tellico::ImageFactory::imageById(imageId); 0425 QVERIFY(!img.isNull()); 0426 } 0427 0428 void TellicoReadTest::testXMLHandler() { 0429 QFETCH(QByteArray, data); 0430 QFETCH(QString, expectedString); 0431 QFETCH(bool, changeEncoding); 0432 0433 QString origString = QString::fromUtf8(data); 0434 QCOMPARE(Tellico::XMLHandler::readXMLData(data), expectedString); 0435 QCOMPARE(Tellico::XMLHandler::setUtf8XmlEncoding(origString), changeEncoding); 0436 } 0437 0438 void TellicoReadTest::testXMLHandler_data() { 0439 QTest::addColumn<QByteArray>("data"); 0440 QTest::addColumn<QString>("expectedString"); 0441 QTest::addColumn<bool>("changeEncoding"); 0442 0443 QTest::newRow("basic") << QByteArray("<x>value</x>") << QStringLiteral("<x>value</x>") << false; 0444 QTest::newRow("utf8") << QByteArray("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<x>value</x>") 0445 << QStringLiteral("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<x>value</x>") << false; 0446 QTest::newRow("UTF8") << QByteArray("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<x>value</x>") 0447 << QStringLiteral("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<x>value</x>") << false; 0448 QTest::newRow("latin1") << QByteArray("<?xml version=\"1.0\" encoding=\"latin1\"?>\n<x>value</x>") 0449 << QStringLiteral("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<x>value</x>") << true; 0450 QTest::newRow("LATIN1") << QByteArray("<?xml version=\"1.0\" encoding=\"LATIN1\"?>\n<x>value</x>") 0451 << QStringLiteral("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<x>value</x>") << true; 0452 0453 QString usa = QString::fromUtf8("США"); 0454 QTextCodec* cp1251 = QTextCodec::codecForName("cp1251"); 0455 QByteArray usaBytes = QByteArray("<?xml version=\"1.0\" encoding=\"cp1251\"?>\n<x>") 0456 + cp1251->fromUnicode(usa) 0457 + QByteArray("</x>"); 0458 QString usaString = QStringLiteral("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<x>") 0459 + usa 0460 + QStringLiteral("</x>"); 0461 QTest::newRow("cp1251") << usaBytes << usaString << true; 0462 } 0463 0464 void TellicoReadTest::testXmlName() { 0465 QFETCH(bool, valid); 0466 QFETCH(QString, input); 0467 QFETCH(QString, modified); 0468 0469 QCOMPARE(Tellico::XML::validXMLElementName(input), valid); 0470 QCOMPARE(Tellico::XML::elementName(input), modified); 0471 } 0472 0473 void TellicoReadTest::testXmlName_data() { 0474 QTest::addColumn<bool>("valid"); 0475 QTest::addColumn<QString>("input"); 0476 QTest::addColumn<QString>("modified"); 0477 0478 QTest::newRow("start") << true << QSL("start") << QSL("start"); 0479 QTest::newRow("_start") << true << QSL("_start") << QSL("_start"); 0480 QTest::newRow("n42") << true << QSL("n42") << QSL("n42"); 0481 // an empty string is handled in CollectionFieldsDialog when creating the field name 0482 QTest::newRow("42") << false << QSL("42") << QString(); 0483 QTest::newRow("she is") << false << QSL("she is") << QSL("she-is"); 0484 QTest::newRow("colon:") << false << QSL("colon:") << QSL("colon"); 0485 QTest::newRow("Svět") << true << QSL("Svět") << QSL("Svět"); 0486 QTest::newRow("<test>") << false << QSL("<test>") << QSL("test"); 0487 QTest::newRow("is-€:") << false << QSL("is-€:") << QSL("is-"); 0488 } 0489 0490 void TellicoReadTest::testRecoverXmlName() { 0491 QFETCH(QByteArray, input); 0492 QFETCH(QByteArray, modified); 0493 0494 QCOMPARE(Tellico::XML::recoverFromBadXMLName(input), modified); 0495 } 0496 0497 void TellicoReadTest::testRecoverXmlName_data() { 0498 QTest::addColumn<QByteArray>("input"); 0499 QTest::addColumn<QByteArray>("modified"); 0500 0501 QTest::newRow("<nr:>") << QByteArray("<fields><field name=\"nr:\"/></fields><nr:>x</nr:>") 0502 << QByteArray("<fields><field name=\"nr\"/></fields><nr>x</nr>"); 0503 QTest::newRow("<nr:>2") << QByteArray("<fields><field name=\"nr:\" d=\"d\"/></fields><nr:>x</nr:>") 0504 << QByteArray("<fields><field name=\"nr\" d=\"d\"/></fields><nr>x</nr>"); 0505 QTest::newRow("<nr:>3") << QByteArray("<fields><field name=\"nr:\"/></fields><nr:s><nr:>x</nr:></nr:s>") 0506 << QByteArray("<fields><field name=\"nr\"/></fields><nrs><nr>x</nr></nrs>"); 0507 QTest::newRow("<nr:>4") << QByteArray("<fields><field d=\"nr:\" name=\"nr:\" d=\"nr:\"/></fields><nr:>x</nr:>") 0508 << QByteArray("<fields><field d=\"nr:\" name=\"nr\" d=\"nr:\"/></fields><nr>x</nr>"); 0509 QTest::newRow("<is-€:>") << QByteArray("<fields><field name=\"is-€:\"/></fields><is-€:>x</is-€:>") 0510 << QByteArray("<fields><field name=\"is-\"/></fields><is->x</is->"); 0511 } 0512 0513 void TellicoReadTest::testBug418067() { 0514 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA(QSL("data/bug418067.xml"))); 0515 0516 Tellico::Import::TellicoImporter importer(url); 0517 Tellico::Data::CollPtr coll = importer.collection(); 0518 0519 QVERIFY(coll); 0520 QVERIFY(coll->hasField(QSL("lc-no."))); 0521 QVERIFY(coll->hasField(QSL("mein-wunschpreis-"))); 0522 } 0523 0524 void TellicoReadTest::testNoCreationDate() { 0525 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA(QSL("data/no_cdate.xml"))); 0526 0527 Tellico::Import::TellicoImporter importer(url); 0528 Tellico::Data::CollPtr coll = importer.collection(); 0529 0530 QVERIFY(coll); 0531 QVERIFY(coll->hasField(QStringLiteral("cdate"))); 0532 QVERIFY(coll->hasField(QStringLiteral("mdate"))); 0533 QCOMPARE(coll->entries().count(), 1); 0534 0535 Tellico::Data::EntryPtr entry = coll->entries().at(0); 0536 QVERIFY(entry); 0537 // entry data has an mdate but no cdate 0538 // cdate should be set to same as mdate 0539 QString mdate(QStringLiteral("2020-05-30")); 0540 QCOMPARE(entry->field(QStringLiteral("cdate")), mdate); 0541 QCOMPARE(entry->field(QStringLiteral("mdate")), mdate); 0542 } 0543 0544 void TellicoReadTest::testFutureVersion() { 0545 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA(QSL("data/future_version.xml"))); 0546 0547 Tellico::Import::TellicoImporter importer(url); 0548 Tellico::Data::CollPtr coll = importer.collection(); 0549 0550 QVERIFY(!coll); 0551 QVERIFY(!importer.statusMessage().isEmpty()); 0552 } 0553 0554 void TellicoReadTest::testRelativeLink() { 0555 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA(QSL("data/relative-link.xml"))); 0556 0557 Tellico::Import::TellicoImporter importer(url); 0558 Tellico::Data::CollPtr coll = importer.collection(); 0559 0560 QVERIFY(coll); 0561 QVERIFY(coll->hasField(QStringLiteral("url"))); 0562 Tellico::Data::EntryPtr entry = coll->entries().at(0); 0563 QVERIFY(entry); 0564 QCOMPARE(entry->field(QStringLiteral("url")), QLatin1String("collectorz/image.png")); 0565 0566 Tellico::XSLTHandler handler(QFile::encodeName(QFINDTESTDATA("data/output-url.xsl"))); 0567 QVERIFY(handler.isValid()); 0568 0569 Tellico::Data::Document::self()->setURL(url); // set the base url 0570 QUrl expected = url.resolved(QUrl(QLatin1String("collectorz/image.png"))); 0571 0572 Tellico::Export::TellicoXMLExporter exp(coll); 0573 exp.setEntries(coll->entries()); 0574 exp.setURL(url); 0575 QString output = handler.applyStylesheet(exp.text()); 0576 // first, the link should remain completely relative 0577 QVERIFY(output.contains(QLatin1String("href=\"collectorz/image.png"))); 0578 0579 exp.setOptions(exp.options() | Tellico::Export::ExportAbsoluteLinks); 0580 output = handler.applyStylesheet(exp.text()); 0581 // now, the link should be absolute 0582 QVERIFY(output.contains(expected.url())); 0583 } 0584 0585 void TellicoReadTest::testEmptyFirstTableRow() { 0586 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA(QSL("data/table-empty-first-row.xml"))); 0587 0588 Tellico::Import::TellicoImporter importer(url); 0589 Tellico::Data::CollPtr coll = importer.collection(); 0590 0591 QVERIFY(coll); 0592 QVERIFY(coll->hasField(QStringLiteral("table"))); 0593 Tellico::Data::EntryPtr entry = coll->entries().at(0); 0594 QVERIFY(entry); 0595 const QStringList rows = Tellico::FieldFormat::splitTable(entry->field(QSL("table"))); 0596 QCOMPARE(rows.count(), 2); 0597 0598 Tellico::Export::TellicoXMLExporter exporter(coll); 0599 exporter.setEntries(coll->entries()); 0600 Tellico::Import::TellicoImporter importer2(exporter.text()); 0601 Tellico::Data::CollPtr coll2 = importer2.collection(); 0602 QVERIFY(coll2); 0603 Tellico::Data::EntryPtr entry2 = coll2->entries().at(0); 0604 QVERIFY(entry2); 0605 const QStringList rows2 = Tellico::FieldFormat::splitTable(entry2->field(QSL("table"))); 0606 QCOMPARE(rows2.count(), 2); 0607 } 0608 0609 void TellicoReadTest::testBug443845() { 0610 // Tellico allowed data in a paragraph field that included an invalid control character 0611 // and Tellico 3.3 could load the resulting file, but Tellico 3.4 couldn't 0612 // first verify that we don't write an invalid character to the XML 0613 QString badTitle = QStringLiteral("title with control") + QChar(0x0C); 0614 Tellico::Data::CollPtr coll(new Tellico::Data::Collection(true)); // add default fields 0615 QVERIFY(coll->hasField(QStringLiteral("title"))); 0616 Tellico::Data::EntryPtr entry1(new Tellico::Data::Entry(coll)); 0617 entry1->setField(QStringLiteral("title"), badTitle); 0618 coll->addEntries(entry1); 0619 Tellico::Export::TellicoXMLExporter exporter(coll); 0620 exporter.setEntries(coll->entries()); 0621 // exported XML should not contain the illegal control character 0622 QVERIFY(!exporter.text().contains(QChar(0x0C))); 0623 0624 // now since we used to allow these characters, need to make the parser robust for them 0625 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA(QSL("data/bug443845.xml"))); 0626 0627 Tellico::Import::TellicoImporter importer(url); 0628 coll = importer.collection(); 0629 0630 QVERIFY(coll); 0631 Tellico::Data::EntryPtr entry = coll->entries().at(0); 0632 QVERIFY(entry); 0633 } 0634 0635 void TellicoReadTest::testEmoji() { 0636 // https://www.fileformat.info/info/unicode/char/1f3e1/index.htm 0637 QString textWithEmoji = QString::fromUtf8("Title 🏡️"); 0638 // stripping control codes should not affect the emoji 0639 QCOMPARE(Tellico::removeControlCodes(textWithEmoji), textWithEmoji); 0640 0641 Tellico::Data::CollPtr coll(new Tellico::Data::Collection(true)); // add default fields 0642 QVERIFY(coll->hasField(QStringLiteral("title"))); 0643 Tellico::Data::FieldPtr field = coll->fieldByName(QStringLiteral("title")); 0644 QVERIFY(field); 0645 field->setTitle(textWithEmoji); 0646 Tellico::Data::EntryPtr entry1(new Tellico::Data::Entry(coll)); 0647 entry1->setField(QStringLiteral("title"), textWithEmoji); 0648 QCOMPARE(entry1->title(), textWithEmoji); 0649 coll->addEntries(entry1); 0650 Tellico::Export::TellicoXMLExporter exporter(coll); 0651 exporter.setEntries(coll->entries()); 0652 0653 Tellico::Import::TellicoImporter importer(exporter.text()); 0654 Tellico::Data::CollPtr coll2 = importer.collection(); 0655 QVERIFY(coll2); 0656 0657 Tellico::Data::FieldPtr field2 = coll2->fieldByName(QStringLiteral("title")); 0658 QVERIFY(field2); 0659 QCOMPARE(field2->title(), textWithEmoji); 0660 0661 Tellico::Data::EntryPtr entry2 = coll2->entries().at(0); 0662 QVERIFY(entry2); 0663 QCOMPARE(entry2->title(), textWithEmoji); 0664 } 0665 0666 void TellicoReadTest::testXmlWithJunk() { 0667 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA(QSL("data/xml-with-junk.xml"))); 0668 0669 Tellico::Import::TellicoImporter importer(url); 0670 Tellico::Data::CollPtr coll = importer.collection(); 0671 QVERIFY(!coll); 0672 0673 QFile f(url.toLocalFile()); 0674 QVERIFY(f.exists()); 0675 QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text)); 0676 0677 QTextStream in(&f); 0678 QString fileText = in.readAll(); 0679 Tellico::Import::TellicoImporter importer2(fileText); 0680 Tellico::Data::CollPtr coll2 = importer2.collection(); 0681 QVERIFY(!coll2); 0682 }