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