File indexing completed on 2024-05-19 16:19:07
0001 /*************************************************************************** 0002 Copyright (C) 2009-2016 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 "collectiontest.h" 0026 0027 #include "../collection.h" 0028 #include "../field.h" 0029 #include "../entry.h" 0030 #include "../collectionfactory.h" 0031 #include "../collections/collectioninitializer.h" 0032 #include "../collections/bookcollection.h" 0033 #include "../collections/gamecollection.h" 0034 #include "../translators/tellicoxmlexporter.h" 0035 #include "../translators/tellicoimporter.h" 0036 #include "../images/imagefactory.h" 0037 #include "../document.h" 0038 #include "../utils/mergeconflictresolver.h" 0039 #include "../entrycomparison.h" 0040 0041 #include <KProcess> 0042 0043 #include <QTest> 0044 #include <QStandardPaths> 0045 #include <QRandomGenerator> 0046 0047 QTEST_GUILESS_MAIN( CollectionTest ) 0048 0049 Q_DECLARE_METATYPE(Tellico::EntryComparison::MatchValue) 0050 0051 class TestResolver : public Tellico::Merge::ConflictResolver { 0052 public: 0053 TestResolver(Tellico::Merge::ConflictResolver::Result ret) : m_ret(ret) {}; 0054 Tellico::Merge::ConflictResolver::Result resolve(Tellico::Data::EntryPtr, 0055 Tellico::Data::EntryPtr, 0056 Tellico::Data::FieldPtr, 0057 const QString& value1 = QString(), 0058 const QString& value2 = QString()) Q_DECL_OVERRIDE { 0059 Q_UNUSED(value1); 0060 Q_UNUSED(value2); 0061 return m_ret; 0062 } 0063 0064 private: 0065 Tellico::Merge::ConflictResolver::Result m_ret; 0066 }; 0067 0068 void CollectionTest::initTestCase() { 0069 QStandardPaths::setTestModeEnabled(true); 0070 qRegisterMetaType<Tellico::EntryComparison::MatchValue>(); 0071 Tellico::ImageFactory::init(); 0072 // need to register the collection types 0073 Tellico::CollectionInitializer ci; 0074 0075 // create the collection and entry used for testMatchScore() 0076 m_coll = Tellico::Data::CollPtr(new Tellico::Data::BookCollection(true)); 0077 m_entry = Tellico::Data::EntryPtr(new Tellico::Data::Entry(m_coll)); 0078 m_entry->setField(QStringLiteral("title"), QStringLiteral("title1")); 0079 m_entry->setField(QStringLiteral("author"), QStringLiteral("John Doe")); 0080 m_entry->setField(QStringLiteral("isbn"), QStringLiteral("1234367890")); 0081 m_entry->setField(QStringLiteral("lccn"), QStringLiteral("89456")); 0082 Tellico::Data::FieldPtr f(new Tellico::Data::Field(QStringLiteral("arxiv"), QStringLiteral("Arxiv ID"))); 0083 m_coll->addField(f); 0084 m_entry->setField(QStringLiteral("arxiv"), QStringLiteral("hep-lat/0110180")); 0085 m_coll->addEntries(m_entry); 0086 } 0087 0088 void CollectionTest::cleanupTestCase() { 0089 Tellico::ImageFactory::clean(true); 0090 } 0091 0092 void CollectionTest::testEmpty() { 0093 Tellico::Data::CollPtr nullColl; 0094 QVERIFY(!nullColl); 0095 0096 Tellico::Data::Collection coll(false, QStringLiteral("Title")); 0097 0098 QCOMPARE(coll.id(), 2); // ID is 2 since ID of 1 is created in initTestCase() 0099 QCOMPARE(coll.entryCount(), 0); 0100 QCOMPARE(coll.type(), Tellico::Data::Collection::Base); 0101 QVERIFY(coll.fields().isEmpty()); 0102 QCOMPARE(coll.title(), QStringLiteral("Title")); 0103 } 0104 0105 void CollectionTest::testCollection() { 0106 Tellico::Data::CollPtr coll(new Tellico::Data::Collection(true)); // add default fields 0107 0108 QCOMPARE(coll->entryCount(), 0); 0109 QCOMPARE(coll->type(), Tellico::Data::Collection::Base); 0110 QCOMPARE(coll->fields().count(), 4); 0111 QVERIFY(coll->hasField(QStringLiteral("title"))); 0112 QVERIFY(coll->hasField(QStringLiteral("id"))); 0113 QVERIFY(coll->hasField(QStringLiteral("cdate"))); 0114 QVERIFY(coll->hasField(QStringLiteral("mdate"))); 0115 QVERIFY(coll->peopleFields().isEmpty()); 0116 QVERIFY(coll->imageFields().isEmpty()); 0117 QVERIFY(!coll->hasImages()); 0118 0119 Tellico::Data::EntryPtr entry1(new Tellico::Data::Entry(coll)); 0120 coll->addEntries(entry1); 0121 0122 // check derived value 0123 QCOMPARE(entry1->field(QStringLiteral("id")), QStringLiteral("1")); 0124 // check created and modified values 0125 QCOMPARE(entry1->field(QStringLiteral("cdate")), QDate::currentDate().toString(Qt::ISODate)); 0126 QCOMPARE(entry1->field(QStringLiteral("mdate")), QDate::currentDate().toString(Qt::ISODate)); 0127 0128 // also verify that the empty string is included in list of group names 0129 Tellico::Data::FieldPtr field1(new Tellico::Data::Field(QStringLiteral("test"), QStringLiteral("test"))); 0130 coll->addField(field1); 0131 QStringList groupNames = entry1->groupNamesByFieldName(QStringLiteral("test")); 0132 QCOMPARE(groupNames.count(), 1); 0133 QVERIFY(groupNames.at(0).isEmpty()); 0134 0135 Tellico::Data::EntryPtr entry2(new Tellico::Data::Entry(coll)); 0136 // add created and modified dates from earlier, to make sure they don't get overwritten 0137 QDate weekAgo = QDate::currentDate().addDays(-7); 0138 QDate yesterday = QDate::currentDate().addDays(-1); 0139 entry2->setField(QStringLiteral("cdate"), weekAgo.toString(Qt::ISODate)); 0140 entry2->setField(QStringLiteral("mdate"), yesterday.toString(Qt::ISODate)); 0141 coll->addEntries(entry2); 0142 0143 // check derived value 0144 QCOMPARE(entry2->field(QStringLiteral("id")), QStringLiteral("2")); 0145 // check created and modified values 0146 QCOMPARE(entry2->field(QStringLiteral("cdate")), weekAgo.toString(Qt::ISODate)); 0147 QCOMPARE(entry2->field(QStringLiteral("mdate")), yesterday.toString(Qt::ISODate)); 0148 0149 // check that mdate gets updates 0150 entry2->setField(QStringLiteral("title"), QStringLiteral("new title")); 0151 QCOMPARE(entry2->field(QStringLiteral("cdate")), weekAgo.toString(Qt::ISODate)); 0152 QCOMPARE(entry2->field(QStringLiteral("mdate")), QDate::currentDate().toString(Qt::ISODate)); 0153 0154 // check Bug 361622 - properly handling empty rows in table 0155 Tellico::Data::FieldPtr tableField(new Tellico::Data::Field(QStringLiteral("table"), QStringLiteral("Table"), Tellico::Data::Field::Table)); 0156 tableField->setFormatType(Tellico::FieldFormat::FormatName); 0157 coll->addField(tableField); 0158 QString tableValue = QStringLiteral("Value1") 0159 + Tellico::FieldFormat::rowDelimiterString() 0160 + Tellico::FieldFormat::rowDelimiterString() 0161 + QStringLiteral("Value2"); 0162 entry2->setField(QStringLiteral("table"), tableValue); 0163 QCOMPARE(entry2->formattedField(QStringLiteral("table")), tableValue); 0164 groupNames = entry2->groupNamesByFieldName(QStringLiteral("table")); 0165 QCOMPARE(groupNames.count(), 2); 0166 QVERIFY(groupNames.contains(QStringLiteral("Value1"))); 0167 QVERIFY(groupNames.contains(QStringLiteral("Value2"))); 0168 } 0169 0170 void CollectionTest::testFields() { 0171 Tellico::Data::CollPtr coll(new Tellico::Data::Collection(true)); // add default fields 0172 0173 QCOMPARE(coll->fields().count(), 4); 0174 QVERIFY(coll->peopleFields().isEmpty()); 0175 QVERIFY(coll->imageFields().isEmpty()); 0176 0177 Tellico::Data::FieldPtr aField(new Tellico::Data::Field(QStringLiteral("author"), 0178 QStringLiteral("Author"))); 0179 aField->setFlags(Tellico::Data::Field::AllowMultiple | Tellico::Data::Field::AllowGrouped); 0180 aField->setFormatType(Tellico::FieldFormat::FormatName); 0181 QCOMPARE(coll->addField(aField), true); 0182 QVERIFY(coll->hasField(QStringLiteral("author"))); 0183 QCOMPARE(coll->defaultGroupField(), QStringLiteral("author")); 0184 0185 QCOMPARE(coll->fields().count(), 5); 0186 QCOMPARE(coll->peopleFields().count(), 1); 0187 QVERIFY(coll->imageFields().isEmpty()); 0188 QVERIFY(!coll->hasImages()); 0189 QCOMPARE(coll->fieldsByCategory(QStringLiteral("General")).size(), 2); 0190 0191 Tellico::Data::FieldPtr bField(new Tellico::Data::Field(QStringLiteral("cover"), 0192 QStringLiteral("Cover"), 0193 Tellico::Data::Field::Image)); 0194 QCOMPARE(coll->addField(bField), true); 0195 QVERIFY(coll->hasField(QStringLiteral("cover"))); 0196 QVERIFY(!coll->hasField(QStringLiteral("Ccover"))); 0197 0198 QCOMPARE(coll->fields().count(), 6); 0199 QCOMPARE(coll->peopleFields().count(), 1); 0200 QCOMPARE(coll->imageFields().count(), 1); 0201 QVERIFY(coll->hasImages()); 0202 0203 QStringList cats = coll->fieldCategories(); 0204 QCOMPARE(cats.size(), 3); 0205 QVERIFY(cats.contains(QStringLiteral("General"))); 0206 QVERIFY(cats.contains(QStringLiteral("Personal"))); 0207 QVERIFY(cats.contains(QStringLiteral("Cover"))); 0208 0209 const QStringList names = coll->fieldNames(); 0210 QCOMPARE(names.size(), 6); 0211 QVERIFY(names.contains(QStringLiteral("author"))); 0212 QVERIFY(names.contains(QStringLiteral("cover"))); 0213 0214 const QStringList titles = coll->fieldTitles(); 0215 QCOMPARE(titles.size(), 6); 0216 QVERIFY(titles.contains(QStringLiteral("Author"))); 0217 QVERIFY(titles.contains(QStringLiteral("Cover"))); 0218 0219 QCOMPARE(coll->fieldByName(QStringLiteral("author")), aField); 0220 QCOMPARE(coll->fieldByTitle(QStringLiteral("Author")), aField); 0221 QCOMPARE(coll->fieldNameByTitle(QStringLiteral("Author")), QStringLiteral("author")); 0222 QCOMPARE(coll->fieldNameByTitle(QStringLiteral("author")), QString()); 0223 QCOMPARE(coll->fieldTitleByName(QStringLiteral("Author")), QString()); 0224 QCOMPARE(coll->fieldTitleByName(QStringLiteral("author")), QStringLiteral("Author")); 0225 0226 QVERIFY(coll->removeField(QStringLiteral("cover"))); 0227 QVERIFY(!coll->hasField(QStringLiteral("cover"))); 0228 QCOMPARE(coll->fields().count(), 5); 0229 QVERIFY(!coll->hasImages()); 0230 QCOMPARE(coll->fieldTitleByName(QStringLiteral("cover")), QString()); 0231 QCOMPARE(coll->fieldCategories().size(), 2); 0232 0233 Tellico::Data::FieldPtr cField(new Tellico::Data::Field(QStringLiteral("editor"), 0234 QStringLiteral("Editor"))); 0235 cField->setFlags(Tellico::Data::Field::AllowGrouped); 0236 cField->setFormatType(Tellico::FieldFormat::FormatName); 0237 cField->setCategory(QStringLiteral("People")); 0238 0239 // since the field name does not match an existing field, modifying should fail 0240 QVERIFY(!coll->modifyField(cField)); 0241 cField->setName(QStringLiteral("author")); 0242 QVERIFY(coll->modifyField(cField)); 0243 QCOMPARE(coll->fieldByName(QStringLiteral("author")), cField); 0244 QCOMPARE(coll->fieldByTitle(QStringLiteral("Author")), Tellico::Data::FieldPtr()); 0245 QCOMPARE(coll->fieldByTitle(QStringLiteral("Editor")), cField); 0246 QCOMPARE(coll->peopleFields().count(), 1); 0247 0248 cats = coll->fieldCategories(); 0249 QCOMPARE(cats.size(), 3); 0250 QVERIFY(cats.contains(QStringLiteral("General"))); 0251 QVERIFY(cats.contains(QStringLiteral("Personal"))); 0252 QVERIFY(cats.contains(QStringLiteral("People"))); 0253 0254 QCOMPARE(coll->fieldsByCategory(QStringLiteral("General")).size(), 1); 0255 QCOMPARE(coll->fieldsByCategory(QStringLiteral("People")).size(), 1); 0256 0257 coll->clear(); 0258 QVERIFY(coll->fields().isEmpty()); 0259 QVERIFY(coll->peopleFields().isEmpty()); 0260 QVERIFY(coll->imageFields().isEmpty()); 0261 QVERIFY(coll->fieldCategories().isEmpty()); 0262 QVERIFY(coll->defaultGroupField().isEmpty()); 0263 QCOMPARE(coll->fieldByName(QStringLiteral("author")), Tellico::Data::FieldPtr()); 0264 QCOMPARE(coll->fieldByTitle(QStringLiteral("Editor")), Tellico::Data::FieldPtr()); 0265 } 0266 0267 void CollectionTest::testDerived() { 0268 Tellico::Data::CollPtr coll(new Tellico::Data::Collection(true)); // add default field 0269 0270 Tellico::Data::FieldPtr aField(new Tellico::Data::Field(QStringLiteral("author"), 0271 QStringLiteral("Author"))); 0272 aField->setFlags(Tellico::Data::Field::AllowMultiple); 0273 aField->setFormatType(Tellico::FieldFormat::FormatName); 0274 coll->addField(aField); 0275 0276 Tellico::Data::EntryPtr entry(new Tellico::Data::Entry(coll)); 0277 entry->setField(QStringLiteral("author"), QStringLiteral("Albert Einstein; Niels Bohr")); 0278 coll->addEntries(entry); 0279 0280 Tellico::Data::FieldPtr field(new Tellico::Data::Field(QStringLiteral("test"), QStringLiteral("Test"))); 0281 field->setProperty(QStringLiteral("template"), QStringLiteral("%{author}")); 0282 field->setFlags(Tellico::Data::Field::Derived); 0283 field->setFormatType(Tellico::FieldFormat::FormatName); 0284 coll->addField(field); 0285 0286 QCOMPARE(entry->field(QStringLiteral("test")), QStringLiteral("Albert Einstein; Niels Bohr")); 0287 0288 field->setProperty(QStringLiteral("template"), QStringLiteral("%{test3}")); 0289 0290 Tellico::Data::FieldPtr field2(new Tellico::Data::Field(QStringLiteral("test2"), QStringLiteral("Test"))); 0291 field2->setProperty(QStringLiteral("template"), QStringLiteral("%{test}")); 0292 field2->setFlags(Tellico::Data::Field::Derived); 0293 coll->addField(field2); 0294 0295 Tellico::Data::FieldPtr field3(new Tellico::Data::Field(QStringLiteral("test3"), QStringLiteral("Test"))); 0296 field3->setProperty(QStringLiteral("template"), QStringLiteral("%{test3:1}")); 0297 field3->setFlags(Tellico::Data::Field::Derived); 0298 coll->addField(field3); 0299 0300 // recursive, so template should be empty now 0301 QCOMPARE(field3->property(QStringLiteral("template")), QString()); 0302 0303 // now test all the possible format options 0304 field->setProperty(QStringLiteral("template"), QStringLiteral("%{author:1}")); 0305 QCOMPARE(entry->field(QStringLiteral("test")), QStringLiteral("Albert Einstein")); 0306 0307 field->setProperty(QStringLiteral("template"), QStringLiteral("%{author:1/l}")); 0308 QCOMPARE(entry->field(QStringLiteral("test")), QStringLiteral("albert einstein")); 0309 0310 field->setProperty(QStringLiteral("template"), QStringLiteral("%{author:1/u}")); 0311 QCOMPARE(entry->field(QStringLiteral("test")), QStringLiteral("ALBERT EINSTEIN")); 0312 0313 field->setProperty(QStringLiteral("template"), QStringLiteral("%{author:1}")); 0314 QCOMPARE(entry->formattedField(QStringLiteral("test"), Tellico::FieldFormat::ForceFormat), QStringLiteral("Einstein, Albert")); 0315 0316 field->setProperty(QStringLiteral("template"), QStringLiteral("%{author:2}")); 0317 QCOMPARE(entry->field(QStringLiteral("test")), QStringLiteral("Niels Bohr")); 0318 0319 field->setProperty(QStringLiteral("template"), QStringLiteral("%{author:-1}")); 0320 QCOMPARE(entry->field(QStringLiteral("test")), QStringLiteral("Niels Bohr")); 0321 0322 field->setProperty(QStringLiteral("template"), QStringLiteral("%{author:-1}")); 0323 QCOMPARE(entry->formattedField(QStringLiteral("test"), Tellico::FieldFormat::ForceFormat), QStringLiteral("Bohr, Niels")); 0324 0325 field->setProperty(QStringLiteral("template"), QStringLiteral("%{author:-2}")); 0326 QCOMPARE(entry->field(QStringLiteral("test")), QStringLiteral("Albert Einstein")); 0327 } 0328 0329 void CollectionTest::testValue() { 0330 QFETCH(QString, string); 0331 QFETCH(QString, formatted); 0332 QFETCH(int, typeInt); 0333 0334 Tellico::FieldFormat::Type type = static_cast<Tellico::FieldFormat::Type>(typeInt); 0335 0336 Tellico::Data::CollPtr coll(new Tellico::Data::Collection(true)); // add default field 0337 0338 Tellico::Data::FieldPtr field1(new Tellico::Data::Field(QStringLiteral("test"), QStringLiteral("Test"))); 0339 field1->setFlags(Tellico::Data::Field::AllowMultiple); 0340 field1->setFormatType(type); 0341 coll->addField(field1); 0342 0343 Tellico::Data::FieldPtr field2(new Tellico::Data::Field(QStringLiteral("table"), QStringLiteral("Table"), Tellico::Data::Field::Table)); 0344 field2->setFormatType(type); 0345 coll->addField(field2); 0346 0347 Tellico::Data::EntryPtr entry(new Tellico::Data::Entry(coll)); 0348 coll->addEntries(entry); 0349 0350 entry->setField(field1, string); 0351 0352 const QString dummy = Tellico::FieldFormat::columnDelimiterString() + QStringLiteral("dummy, the; Dummy, The; the dummy"); 0353 entry->setField(field2, string + dummy); 0354 0355 QCOMPARE(entry->formattedField(field1, Tellico::FieldFormat::ForceFormat), formatted); 0356 QCOMPARE(entry->formattedField(field2, Tellico::FieldFormat::ForceFormat), formatted.append(dummy)); 0357 } 0358 0359 void CollectionTest::testValue_data() { 0360 QTest::addColumn<QString>("string"); 0361 QTest::addColumn<QString>("formatted"); 0362 QTest::addColumn<int>("typeInt"); 0363 0364 QTest::newRow("test1") << "name" << "Name" << int(Tellico::FieldFormat::FormatName); 0365 QTest::newRow("test2") << "name1; name2" << "Name1; Name2" << int(Tellico::FieldFormat::FormatName); 0366 QTest::newRow("test3") << "Bob Dylan;Randy Quaid" << "Dylan, Bob; Quaid, Randy" << int(Tellico::FieldFormat::FormatName); 0367 QTest::newRow("test4") << "the return of the king" << "Return of the King, The" << int(Tellico::FieldFormat::FormatTitle); 0368 QTest::newRow("test5") << "the return of the king;the who" << "Return of the King, The; Who, The" << int(Tellico::FieldFormat::FormatTitle); 0369 } 0370 0371 void CollectionTest::testDtd() { 0372 const QString xmllint = QStandardPaths::findExecutable(QStringLiteral("xmllint")); 0373 if(xmllint.isEmpty()) { 0374 QSKIP("This test requires xmllint", SkipAll); 0375 } 0376 // xmllint doesn't seem to support spaces in path. Is this an XML thing? 0377 if(QFINDTESTDATA("../../tellico.dtd").contains(QRegularExpression(QStringLiteral("\\s")))) { 0378 QSKIP("This test prohibits whitespace in the build path", SkipAll); 0379 } 0380 0381 QFETCH(int, typeInt); 0382 Tellico::Data::Collection::Type type = static_cast<Tellico::Data::Collection::Type>(typeInt); 0383 0384 Tellico::Data::CollPtr coll = Tellico::CollectionFactory::collection(type, true); 0385 QVERIFY(coll); 0386 0387 Tellico::Data::EntryPtr entry1(new Tellico::Data::Entry(coll)); 0388 coll->addEntries(entry1); 0389 0390 foreach(Tellico::Data::FieldPtr field, coll->fields()) { 0391 switch(field->type()) { 0392 case Tellico::Data::Field::Line: entry1->setField(field, field->title()); break; 0393 case Tellico::Data::Field::Para: entry1->setField(field, field->title()); break; 0394 case Tellico::Data::Field::URL: entry1->setField(field, field->title()); break; 0395 case Tellico::Data::Field::Table: entry1->setField(field, field->title()); break; 0396 case Tellico::Data::Field::Image: entry1->setField(field, field->title()); break; 0397 case Tellico::Data::Field::Number: entry1->setField(field, QStringLiteral("1")); break; 0398 case Tellico::Data::Field::Rating: entry1->setField(field, QStringLiteral("1")); break; 0399 case Tellico::Data::Field::Date: entry1->setField(field, QStringLiteral("2009-01-10")); break; 0400 case Tellico::Data::Field::Bool: entry1->setField(field, QStringLiteral("true")); break; 0401 case Tellico::Data::Field::Choice: entry1->setField(field, field->allowed().first()); break; 0402 default: break; 0403 } 0404 } 0405 0406 Tellico::Export::TellicoXMLExporter exporter(coll); 0407 exporter.setEntries(coll->entries()); 0408 0409 KProcess proc; 0410 proc.setProgram(QStringLiteral("xmllint"), 0411 QStringList() << QStringLiteral("--noout") 0412 << QStringLiteral("--nonet") 0413 << QStringLiteral("--nowarning") 0414 << QStringLiteral("--dtdvalid") 0415 << QFINDTESTDATA("../../tellico.dtd") 0416 << QStringLiteral("-")); 0417 0418 proc.start(); 0419 proc.write(exporter.text().toUtf8()); 0420 proc.closeWriteChannel(); 0421 proc.waitForFinished(); 0422 0423 QCOMPARE(proc.exitCode(), 0); 0424 } 0425 0426 void CollectionTest::testDtd_data() { 0427 QTest::addColumn<int>("typeInt"); 0428 0429 QTest::newRow("book") << int(Tellico::Data::Collection::Book); 0430 QTest::newRow("video") << int(Tellico::Data::Collection::Video); 0431 QTest::newRow("album") << int(Tellico::Data::Collection::Album); 0432 QTest::newRow("bibtex") << int(Tellico::Data::Collection::Bibtex); 0433 QTest::newRow("comic") << int(Tellico::Data::Collection::ComicBook); 0434 QTest::newRow("wine") << int(Tellico::Data::Collection::Wine); 0435 QTest::newRow("coin") << int(Tellico::Data::Collection::Coin); 0436 QTest::newRow("stamp") << int(Tellico::Data::Collection::Stamp); 0437 QTest::newRow("card") << int(Tellico::Data::Collection::Card); 0438 QTest::newRow("game") << int(Tellico::Data::Collection::Game); 0439 QTest::newRow("file") << int(Tellico::Data::Collection::File); 0440 QTest::newRow("board") << int(Tellico::Data::Collection::BoardGame); 0441 } 0442 0443 void CollectionTest::testDuplicate() { 0444 Tellico::Data::CollPtr coll(new Tellico::Data::Collection(true)); 0445 0446 QCOMPARE(coll->entryCount(), 0); 0447 0448 Tellico::Data::EntryPtr entry1(new Tellico::Data::Entry(coll)); 0449 entry1->setField(QStringLiteral("title"), QStringLiteral("title1")); 0450 entry1->setField(QStringLiteral("cdate"), QStringLiteral("2019-01-01")); 0451 entry1->setField(QStringLiteral("mdate"), QStringLiteral("2019-04-01")); 0452 coll->addEntries(entry1); 0453 QCOMPARE(coll->entryCount(), 1); 0454 0455 // this is how Controller::slotCopySelectedEntries() does it 0456 Tellico::Data::EntryPtr entry2(new Tellico::Data::Entry(*entry1)); 0457 QVERIFY(entry2->field(QStringLiteral("cdate")).isEmpty()); 0458 QVERIFY(entry2->field(QStringLiteral("mdate")).isEmpty()); 0459 coll->addEntries(entry2); 0460 QCOMPARE(coll->entryCount(), 2); 0461 0462 QCOMPARE(entry1->title(), entry2->title()); 0463 QVERIFY(entry1->id() != entry2->id()); 0464 // creation date should reflect current date in the duplicated entry 0465 QVERIFY(entry1->field(QStringLiteral("cdate")) != entry2->field(QStringLiteral("cdate"))); 0466 QCOMPARE(entry2->field(QStringLiteral("cdate")), QDate::currentDate().toString(Qt::ISODate)); 0467 0468 // also test operator= which is how ModifyEntries::swapValues() works 0469 Tellico::Data::Entry* entryPtr = new Tellico::Data::Entry(coll); 0470 *entryPtr = *entry1; 0471 Tellico::Data::EntryPtr entry3(entryPtr); 0472 QVERIFY(entry3->field(QStringLiteral("cdate")).isEmpty()); 0473 QVERIFY(entry3->field(QStringLiteral("mdate")).isEmpty()); 0474 coll->addEntries(entry3); 0475 QCOMPARE(coll->entryCount(), 3); 0476 0477 QCOMPARE(entry1->title(), entry3->title()); 0478 // entry id should be different 0479 QVERIFY(entry1->id() != entry3->id()); 0480 // creation date should reflect current date in the duplicated entry 0481 QVERIFY(entry1->field(QStringLiteral("cdate")) != entry3->field(QStringLiteral("cdate"))); 0482 QCOMPARE(entry3->field(QStringLiteral("cdate")), QDate::currentDate().toString(Qt::ISODate)); 0483 0484 bool ret = Tellico::Merge::mergeEntry(entry1, entry2); 0485 QCOMPARE(ret, true); 0486 0487 TestResolver cancelMerge(Tellico::Merge::ConflictResolver::CancelMerge); 0488 ret = Tellico::Merge::mergeEntry(entry1, entry2, &cancelMerge); 0489 QCOMPARE(ret, true); 0490 0491 entry2->setField(QStringLiteral("title"), QStringLiteral("title2")); 0492 0493 ret = Tellico::Merge::mergeEntry(entry1, entry2, &cancelMerge); 0494 QCOMPARE(ret, false); 0495 QCOMPARE(entry1->title(), QStringLiteral("title1")); 0496 QCOMPARE(entry2->title(), QStringLiteral("title2")); 0497 0498 TestResolver keepFirst(Tellico::Merge::ConflictResolver::KeepFirst); 0499 ret = Tellico::Merge::mergeEntry(entry1, entry2, &keepFirst); 0500 QCOMPARE(ret, true); 0501 QCOMPARE(entry1->title(), QStringLiteral("title1")); 0502 // the second entry never gets changed 0503 QCOMPARE(entry2->title(), QStringLiteral("title2")); 0504 0505 entry2->setField(QStringLiteral("title"), QStringLiteral("title2")); 0506 0507 TestResolver keepSecond(Tellico::Merge::ConflictResolver::KeepSecond); 0508 ret = Tellico::Merge::mergeEntry(entry1, entry2, &keepSecond); 0509 QCOMPARE(ret, true); 0510 QCOMPARE(entry1->title(), QStringLiteral("title2")); 0511 QCOMPARE(entry2->title(), QStringLiteral("title2")); 0512 0513 entry1->setField(QStringLiteral("title"), QStringLiteral("title1")); 0514 0515 // returns true, ("merge successful") even if values were not merged 0516 ret = Tellico::Merge::mergeEntry(entry1, entry2); 0517 QCOMPARE(ret, true); 0518 QCOMPARE(entry1->title(), QStringLiteral("title1")); 0519 QCOMPARE(entry2->title(), QStringLiteral("title2")); 0520 } 0521 0522 void CollectionTest::testMergeFields() { 0523 // here, we want to verify that when entries and fields from outside a collection are merged in 0524 // the allowed values for the Choice fields are retained in the same order, and new values are only 0525 // added if they are used 0526 Tellico::Data::CollPtr coll1 = Tellico::CollectionFactory::collection(Tellico::Data::Collection::Game, true); 0527 Tellico::Data::CollPtr coll2 = Tellico::CollectionFactory::collection(Tellico::Data::Collection::Game, true); 0528 0529 // modify the allowed values for "platform" in collection 1 0530 Tellico::Data::FieldPtr platform1 = coll1->fieldByName(QStringLiteral("platform")); 0531 QVERIFY(platform1); 0532 QStringList newValues1 = QStringList() << QStringLiteral("PSP") << QStringLiteral("Xbox 360"); 0533 platform1->setAllowed(newValues1); 0534 QVERIFY(coll1->modifyField(platform1)); 0535 QCOMPARE(platform1->allowed(), newValues1); 0536 0537 Tellico::Data::EntryPtr entry2(new Tellico::Data::Entry(coll2)); 0538 entry2->setField(QStringLiteral("platform"), QStringLiteral("PlayStation")); 0539 QCOMPARE(entry2->field(QStringLiteral("platform")), QStringLiteral("PlayStation")); 0540 coll2->addEntries(entry2); 0541 0542 auto p = Tellico::Merge::mergeFields(coll1, 0543 Tellico::Data::FieldList() << coll2->fieldByName(QStringLiteral("platform")), 0544 Tellico::Data::EntryList() << entry2); 0545 0546 Tellico::Data::FieldList modifiedFields = p.first; 0547 QCOMPARE(modifiedFields.count(), 1); 0548 // this is the zinger right here. The list of allowed values should be the original 0549 // with only the new existing value tacked on the end 0550 QCOMPARE(modifiedFields.first()->allowed(), newValues1 << entry2->field(QStringLiteral("platform"))); 0551 0552 Tellico::Data::FieldList addedFields = p.second; 0553 QVERIFY(addedFields.isEmpty()); 0554 } 0555 0556 void CollectionTest::testFieldsIntersection() { 0557 // simple test for the list intersection utility method 0558 Tellico::Data::CollPtr coll(new Tellico::Data::BookCollection(true)); 0559 Tellico::Data::FieldList imageFields = coll->imageFields(); 0560 0561 Tellico::Data::FieldList list = Tellico::listIntersection(imageFields, coll->fields()); 0562 QCOMPARE(imageFields.count(), list.count()); 0563 0564 QBENCHMARK { 0565 // should be something less than 0.020 msecs :) 0566 Tellico::Data::FieldList list = Tellico::listIntersection(coll->fields(), coll->fields()); 0567 Q_UNUSED(list); 0568 } 0569 } 0570 0571 void CollectionTest::testAppendCollection() { 0572 // appending a collection adds new fields, merges existing one, and add new entries 0573 // the new entries should belong to the original collection and the existing entries should 0574 // remain in the source collection 0575 Tellico::Data::CollPtr coll1 = Tellico::CollectionFactory::collection(Tellico::Data::Collection::Game, true); 0576 Tellico::Data::CollPtr coll2 = Tellico::CollectionFactory::collection(Tellico::Data::Collection::Game, true); 0577 0578 // modify the allowed values for "platform" in collection 1 0579 Tellico::Data::FieldPtr platform1 = coll1->fieldByName(QStringLiteral("platform")); 0580 QVERIFY(platform1); 0581 QStringList newValues1 = QStringList() << QStringLiteral("My Box"); 0582 platform1->setAllowed(newValues1); 0583 QVERIFY(coll1->modifyField(platform1)); 0584 // add a new field 0585 Tellico::Data::FieldPtr field1(new Tellico::Data::Field(QStringLiteral("test"), QStringLiteral("test"))); 0586 QVERIFY(coll1->addField(field1)); 0587 0588 Tellico::Data::EntryPtr entry1(new Tellico::Data::Entry(coll1)); 0589 QCOMPARE(entry1->collection(), coll1); 0590 coll1->addEntries(entry1); 0591 0592 Tellico::Data::EntryPtr entry2(new Tellico::Data::Entry(coll2)); 0593 QCOMPARE(entry2->collection(), coll2); 0594 coll2->addEntries(entry2); 0595 0596 // append coll1 into coll2 0597 bool structuralChange; 0598 Tellico::Data::Document::appendCollection(coll2, coll1, &structuralChange); 0599 QVERIFY(structuralChange); 0600 // verify that the test field was added 0601 QVERIFY(coll2->hasField(QStringLiteral("test"))); 0602 // verified that the modified field was merged 0603 Tellico::Data::FieldPtr platform2 = coll2->fieldByName(QStringLiteral("platform")); 0604 QVERIFY(platform2); 0605 QVERIFY(platform2->allowed().contains(QStringLiteral("My Box"))); 0606 0607 // coll2 should have two entries now, both with proper parent 0608 QCOMPARE(coll2->entryCount(), 2); 0609 Tellico::Data::EntryList e2 = coll2->entries(); 0610 QCOMPARE(e2.at(0)->collection(), coll2); 0611 QCOMPARE(e2.at(1)->collection(), coll2); 0612 0613 QCOMPARE(coll1->entryCount(), 1); 0614 Tellico::Data::EntryList e1 = coll1->entries(); 0615 QCOMPARE(e1.at(0)->collection(), coll1); 0616 } 0617 0618 void CollectionTest::testMergeCollection() { 0619 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("data/movies-many.tc")); 0620 0621 Tellico::Import::TellicoImporter importer1(url); 0622 Tellico::Data::CollPtr coll1 = importer1.collection(); 0623 QVERIFY(coll1); 0624 0625 Tellico::Import::TellicoImporter importer2(url); 0626 Tellico::Data::CollPtr coll2 = importer2.collection(); 0627 QVERIFY(coll2); 0628 0629 Tellico::Data::EntryPtr entryToAdd(new Tellico::Data::Entry(coll2)); 0630 QCOMPARE(entryToAdd->collection(), coll2); 0631 coll2->addEntries(entryToAdd); 0632 0633 QCOMPARE(coll1->entryCount()+1, coll2->entryCount()); 0634 0635 // merge coll2 into coll1 0636 // first item is a vector of all entries that got added in the merge process 0637 // second item is a pair of entries that had their table field modified 0638 // typedef QVector< QPair<EntryPtr, QString> > PairVector; 0639 // typedef QPair<Data::EntryList, PairVector> MergePair; 0640 bool structuralChange; 0641 Tellico::Data::MergePair mergePair = Tellico::Data::Document::mergeCollection(coll1, coll2, &structuralChange); 0642 QCOMPARE(structuralChange, false); 0643 0644 // one new entry was added 0645 QCOMPARE(mergePair.first.count(), 1); 0646 // no table fields edited either 0647 QVERIFY(mergePair.second.isEmpty()); 0648 0649 // check item count 0650 QCOMPARE(coll1->fields().count(), coll2->fields().count()); 0651 QCOMPARE(coll1->entryCount(), coll2->entryCount()); 0652 } 0653 0654 void CollectionTest::testBookMatch() { 0655 Tellico::Data::CollPtr c(new Tellico::Data::BookCollection(true)); 0656 0657 // first check merging with same isbn 0658 Tellico::Data::EntryPtr e1(new Tellico::Data::Entry(c)); 0659 e1->setField(QStringLiteral("title"), QStringLiteral("title1")); 0660 e1->setField(QStringLiteral("author"), QStringLiteral("author1")); 0661 e1->setField(QStringLiteral("edition"), QStringLiteral("edition1")); 0662 e1->setField(QStringLiteral("pur_price"), QStringLiteral("price1")); 0663 e1->setField(QStringLiteral("isbn"), QStringLiteral("1234567890")); 0664 c->addEntries(e1); 0665 0666 Tellico::Data::EntryPtr e2(new Tellico::Data::Entry(c)); 0667 e2->setField(QStringLiteral("title"), QStringLiteral("title2")); 0668 e2->setField(QStringLiteral("author"), QStringLiteral("author2")); 0669 e2->setField(QStringLiteral("edition"), QStringLiteral("edition2")); 0670 e2->setField(QStringLiteral("pur_price"), QStringLiteral("price2")); 0671 e2->setField(QStringLiteral("isbn"), QStringLiteral("000000000")); 0672 0673 // not a good match 0674 QVERIFY(c->sameEntry(e1, e2) < Tellico::EntryComparison::ENTRY_GOOD_MATCH); 0675 0676 // perfect match now 0677 e2->setField(QStringLiteral("isbn"), QStringLiteral("1234567890")); 0678 QCOMPARE(c->sameEntry(e1, e2), int(Tellico::EntryComparison::ENTRY_PERFECT_MATCH)); 0679 0680 QBENCHMARK { 0681 QCOMPARE(c->sameEntry(e1, e2), int(Tellico::EntryComparison::ENTRY_PERFECT_MATCH)); 0682 } 0683 } 0684 0685 void CollectionTest::testMergeBenchmark() { 0686 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("data/movies-many.tc")); 0687 0688 bool structuralChange; 0689 Tellico::Data::EntryList entriesToAdd; 0690 QBENCHMARK { 0691 Tellico::Import::TellicoImporter importer1(url, false /* load all images */); 0692 Tellico::Data::CollPtr coll1 = importer1.collection(); 0693 QVERIFY(coll1); 0694 0695 Tellico::Import::TellicoImporter importer2(url); 0696 Tellico::Data::CollPtr coll2 = importer2.collection(); 0697 QVERIFY(coll2); 0698 0699 entriesToAdd.clear(); 0700 for(int i = 0; i < 500; ++i) { 0701 Tellico::Data::EntryPtr entryToAdd(new Tellico::Data::Entry(coll2)); 0702 entryToAdd->setField(QStringLiteral("title"), QString::number(QRandomGenerator::global()->generate())); 0703 entryToAdd->setField(QStringLiteral("studio"), QString::number(i)); 0704 entriesToAdd += entryToAdd; 0705 } 0706 coll2->addEntries(entriesToAdd); 0707 0708 Tellico::Data::Document::mergeCollection(coll1, coll2, &structuralChange); 0709 } 0710 } 0711 0712 void CollectionTest::testMatchScore() { 0713 QFETCH(QString, field); 0714 QFETCH(QString, value); 0715 QFETCH(Tellico::EntryComparison::MatchValue, score); 0716 0717 QVERIFY(m_coll); 0718 Tellico::Data::EntryPtr e(new Tellico::Data::Entry(m_coll)); 0719 e->setField(field, value); 0720 QCOMPARE(Tellico::EntryComparison::score(m_entry, e, field, m_coll.data()), int(score)); 0721 } 0722 0723 void CollectionTest::testMatchScore_data() { 0724 QTest::addColumn<QString>("field"); 0725 QTest::addColumn<QString>("value"); 0726 QTest::addColumn<Tellico::EntryComparison::MatchValue>("score"); 0727 0728 QTest::newRow("empty title") << QStringLiteral("title") << QString() << Tellico::EntryComparison::MATCH_VALUE_NONE; 0729 QTest::newRow("title match") << QStringLiteral("title") << QStringLiteral("title1") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0730 QTest::newRow("title match case") << QStringLiteral("title") << QStringLiteral("TITLE1") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0731 // QTest::newRow("title match articles") << QStringLiteral("title") << QStringLiteral("THE TITLE1") << Tellico::EntryComparison::MATCH_VALUE_WEAK; 0732 QTest::newRow("title match non alphanum") << QStringLiteral("title") << QStringLiteral("title1.") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0733 // QTest::newRow("title match paren") << QStringLiteral("title") << QStringLiteral("title1 (old)") << Tellico::EntryComparison::MATCH_VALUE_WEAK; 0734 QTest::newRow("isbn match") << QStringLiteral("isbn") << QStringLiteral("1234367890") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0735 QTest::newRow("isbn match formatted") << QStringLiteral("isbn") << QStringLiteral("1-234-36789-0") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0736 QTest::newRow("lccn match") << QStringLiteral("lccn") << QStringLiteral("89456") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0737 QTest::newRow("lccn match formatted") << QStringLiteral("lccn") << QStringLiteral("89-456") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0738 QTest::newRow("arxiv") << QStringLiteral("arxiv") << QStringLiteral("hep-lat/0110180") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0739 QTest::newRow("arxiv format1") << QStringLiteral("arxiv") << QStringLiteral("hep-lat/0110180v1") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0740 QTest::newRow("arxiv format2") << QStringLiteral("arxiv") << QStringLiteral("arxiv:hep-lat/0110180v1") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0741 QTest::newRow("author") << QStringLiteral("author") << QStringLiteral("John Doe") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0742 QTest::newRow("author formatted") << QStringLiteral("author") << QStringLiteral("Doe, John") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0743 QTest::newRow("author formatted2") << QStringLiteral("author") << QStringLiteral("doe, john") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0744 QTest::newRow("author multiple") << QStringLiteral("author") << QStringLiteral("John Doe; Jane Doe") << Tellico::EntryComparison::MATCH_VALUE_STRONG; 0745 } 0746 0747 void CollectionTest::testGamePlatform() { 0748 // test that the platform name guessing heuristic works on its own names 0749 for(int i = 1; i < Tellico::Data::GameCollection::LastPlatform; i++) { 0750 QString pName = Tellico::Data::GameCollection::platformName(Tellico::Data::GameCollection::GamePlatform(i)); 0751 int pGuess = Tellico::Data::GameCollection::guessPlatform(pName); 0752 QCOMPARE(i, pGuess); 0753 } 0754 0755 // test some specific platform names that some data sources might return 0756 // thegamesdb.net returns "Nintendo Game Boy" 0757 int gameBoy = Tellico::Data::GameCollection::guessPlatform(QStringLiteral("Nintendo Game Boy")); 0758 QCOMPARE(gameBoy, int(Tellico::Data::GameCollection::GameBoy)); 0759 gameBoy = Tellico::Data::GameCollection::guessPlatform(QStringLiteral("gameboy")); 0760 QCOMPARE(gameBoy, int(Tellico::Data::GameCollection::GameBoy)); 0761 gameBoy = Tellico::Data::GameCollection::guessPlatform(QStringLiteral("gameboy color")); 0762 QCOMPARE(gameBoy, int(Tellico::Data::GameCollection::GameBoyColor)); 0763 gameBoy = Tellico::Data::GameCollection::guessPlatform(QStringLiteral("Gameboy Advance")); 0764 QCOMPARE(gameBoy, int(Tellico::Data::GameCollection::GameBoyAdvance)); 0765 0766 // don't match Nintendo Virtual Boy with Nintendo 0767 int guess = Tellico::Data::GameCollection::guessPlatform(QStringLiteral("Nintendo Virtual Boy")); 0768 QCOMPARE(guess, int(Tellico::Data::GameCollection::UnknownPlatform)); 0769 guess = Tellico::Data::GameCollection::guessPlatform(QStringLiteral("Nintendo Entertainment System")); 0770 QCOMPARE(guess, int(Tellico::Data::GameCollection::Nintendo)); 0771 }