File indexing completed on 2024-05-12 05:26:26
0001 #include <QTest> 0002 0003 #include <QString> 0004 #include <QSignalSpy> 0005 0006 #include "resource.h" 0007 #include "store.h" 0008 #include "resourcecontrol.h" 0009 #include "commands.h" 0010 #include "resourceconfig.h" 0011 #include "log.h" 0012 #include "modelresult.h" 0013 #include "test.h" 0014 #include "applicationdomaintype.h" 0015 #include "queryrunner.h" 0016 #include "adaptorfactoryregistry.h" 0017 #include "fulltextindex.h" 0018 0019 #include <KMime/Message> 0020 #include <KCalendarCore/Event> 0021 #include <KCalendarCore/ICalFormat> 0022 0023 using namespace Sink; 0024 using namespace Sink::ApplicationDomain; 0025 0026 /** 0027 * Test of the query system using the dummy resource. 0028 * 0029 * This test requires the dummy resource installed. 0030 */ 0031 class QueryTest : public QObject 0032 { 0033 Q_OBJECT 0034 private slots: 0035 void initTestCase() 0036 { 0037 qRegisterMetaType<QList<QPersistentModelIndex>>("QList<QPersistentModelIndex>"); 0038 qRegisterMetaType<QAbstractItemModel::LayoutChangeHint>("QAbstractItemModel::LayoutChangeHint"); 0039 Sink::Test::initTest(); 0040 auto factory = Sink::ResourceFactory::load("sink.dummy"); 0041 QVERIFY(factory); 0042 ResourceConfig::addResource("sink.dummy.instance1", "sink.dummy"); 0043 ResourceConfig::configureResource("sink.dummy.instance1", {{"populate", true}}); 0044 VERIFYEXEC(Sink::Store::removeDataFromDisk(QByteArray("sink.dummy.instance1"))); 0045 } 0046 0047 void cleanup() 0048 { 0049 VERIFYEXEC(Sink::Store::removeDataFromDisk(QByteArray("sink.dummy.instance1"))); 0050 } 0051 0052 void init() 0053 { 0054 qDebug(); 0055 qDebug() << "-----------------------------------------"; 0056 qDebug(); 0057 } 0058 0059 void testSerialization() 0060 { 0061 0062 auto type = QByteArray("type"); 0063 auto sort = QByteArray("sort"); 0064 0065 Sink::QueryBase::Filter filter; 0066 filter.ids << "id"; 0067 filter.propertyFilter.insert({"foo"}, QVariant::fromValue(QByteArray("bar"))); 0068 0069 Sink::Query query; 0070 query.setFilter(filter); 0071 query.setType(type); 0072 query.setSortProperty(sort); 0073 0074 QByteArray data; 0075 { 0076 QDataStream stream(&data, QIODevice::WriteOnly); 0077 stream << query; 0078 } 0079 0080 Sink::Query deserializedQuery; 0081 { 0082 QDataStream stream(&data, QIODevice::ReadOnly); 0083 stream >> deserializedQuery; 0084 } 0085 0086 QCOMPARE(deserializedQuery.type(), type); 0087 QCOMPARE(deserializedQuery.sortProperty(), sort); 0088 QCOMPARE(deserializedQuery.getFilter().ids, filter.ids); 0089 QCOMPARE(deserializedQuery.getFilter().propertyFilter.keys(), filter.propertyFilter.keys()); 0090 QCOMPARE(deserializedQuery.getFilter().propertyFilter, filter.propertyFilter); 0091 } 0092 0093 void testNoResources() 0094 { 0095 // Test 0096 Sink::Query query; 0097 query.resourceFilter("foobar"); 0098 query.setFlags(Query::LiveQuery); 0099 0100 // We fetch before the data is available and rely on the live query mechanism to deliver the actual data 0101 auto model = Sink::Store::loadModel<Mail>(query); 0102 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0103 QCOMPARE(model->rowCount(), 0); 0104 } 0105 0106 0107 void testSingle() 0108 { 0109 // Setup 0110 auto mail = Mail("sink.dummy.instance1"); 0111 mail.setExtractedMessageId("test1"); 0112 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 0113 0114 // Test 0115 Sink::Query query; 0116 query.resourceFilter("sink.dummy.instance1"); 0117 query.setFlags(Query::LiveQuery); 0118 0119 // We fetch before the data is available and rely on the live query mechanism to deliver the actual data 0120 auto model = Sink::Store::loadModel<Mail>(query); 0121 QTRY_COMPARE(model->rowCount(), 1); 0122 } 0123 0124 void testSingleWithDelay() 0125 { 0126 // Setup 0127 auto mail = Mail("sink.dummy.instance1"); 0128 mail.setExtractedMessageId("test1"); 0129 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 0130 0131 // Test 0132 Sink::Query query; 0133 query.resourceFilter("sink.dummy.instance1"); 0134 0135 // Ensure all local data is processed 0136 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0137 0138 // We fetch after the data is available and don't rely on the live query mechanism to deliver the actual data 0139 auto model = Sink::Store::loadModel<Mail>(query); 0140 0141 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0142 QCOMPARE(model->rowCount(), 1); 0143 } 0144 0145 void testFilter() 0146 { 0147 // Setup 0148 { 0149 Mail mail("sink.dummy.instance1"); 0150 mail.setExtractedMessageId("test1"); 0151 mail.setFolder("folder1"); 0152 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 0153 } 0154 { 0155 Mail mail("sink.dummy.instance1"); 0156 mail.setExtractedMessageId("test2"); 0157 mail.setFolder("folder2"); 0158 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 0159 } 0160 0161 // Test 0162 Sink::Query query; 0163 query.resourceFilter("sink.dummy.instance1"); 0164 query.setFlags(Query::LiveQuery); 0165 query.filter<Mail::Folder>("folder1"); 0166 0167 // We fetch before the data is available and rely on the live query mechanism to deliver the actual data 0168 auto model = Sink::Store::loadModel<Mail>(query); 0169 QTRY_COMPARE(model->rowCount(), 1); 0170 0171 auto mail = model->index(0, 0, QModelIndex()).data(Sink::Store::DomainObjectRole).value<Mail::Ptr>(); 0172 { 0173 mail->setFolder("folder2"); 0174 VERIFYEXEC(Sink::Store::modify<Mail>(*mail)); 0175 } 0176 QTRY_COMPARE(model->rowCount(), 0); 0177 0178 { 0179 mail->setFolder("folder1"); 0180 VERIFYEXEC(Sink::Store::modify<Mail>(*mail)); 0181 } 0182 QTRY_COMPARE(model->rowCount(), 1); 0183 } 0184 0185 void testById() 0186 { 0187 QByteArray id; 0188 // Setup 0189 { 0190 Mail mail("sink.dummy.instance1"); 0191 mail.setExtractedMessageId("test1"); 0192 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 0193 mail.setExtractedMessageId("test2"); 0194 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 0195 0196 Sink::Query query; 0197 query.resourceFilter("sink.dummy.instance1"); 0198 0199 // Ensure all local data is processed 0200 Sink::Store::synchronize(query).exec().waitForFinished(); 0201 0202 // We fetch before the data is available and rely on the live query mechanism to deliver the actual data 0203 auto model = Sink::Store::loadModel<Mail>(query); 0204 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0205 QVERIFY(model->rowCount() >= 1); 0206 id = model->index(0, 0).data(Sink::Store::DomainObjectRole).value<Mail::Ptr>()->identifier(); 0207 } 0208 0209 // Test 0210 { 0211 Sink::Query query; 0212 query.resourceFilter("sink.dummy.instance1"); 0213 query.filter(id); 0214 auto model = Sink::Store::loadModel<Mail>(query); 0215 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0216 QCOMPARE(model->rowCount(), 1); 0217 } 0218 0219 { 0220 Sink::Query query; 0221 query.resourceFilter("sink.dummy.instance1"); 0222 //Try a non-existing id 0223 query.filter("{87fcea5e-8d2e-408e-bb8d-b27b9dcf5e92}"); 0224 auto model = Sink::Store::loadModel<Mail>(query); 0225 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0226 QCOMPARE(model->rowCount(), 0); 0227 } 0228 } 0229 0230 void testFolder() 0231 { 0232 // Setup 0233 { 0234 Folder folder("sink.dummy.instance1"); 0235 VERIFYEXEC(Sink::Store::create<Folder>(folder)); 0236 } 0237 0238 // Test 0239 Sink::Query query; 0240 query.resourceFilter("sink.dummy.instance1"); 0241 query.setFlags(Query::LiveQuery); 0242 0243 // We fetch before the data is available and rely on the live query mechanism to deliver the actual data 0244 auto model = Sink::Store::loadModel<Folder>(query); 0245 QTRY_COMPARE(model->rowCount(), 1); 0246 auto folderEntity = model->index(0, 0).data(Sink::Store::DomainObjectRole).value<Folder::Ptr>(); 0247 QVERIFY(!folderEntity->identifier().isEmpty()); 0248 } 0249 0250 void testFolderTree() 0251 { 0252 // Setup 0253 { 0254 auto folder = ApplicationDomainType::createEntity<Folder>("sink.dummy.instance1"); 0255 VERIFYEXEC(Sink::Store::create<Folder>(folder)); 0256 auto subfolder = ApplicationDomainType::createEntity<Folder>("sink.dummy.instance1"); 0257 subfolder.setParent(folder.identifier()); 0258 VERIFYEXEC(Sink::Store::create<Folder>(subfolder)); 0259 // Ensure all local data is processed 0260 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0261 } 0262 0263 // Test 0264 Sink::Query query; 0265 query.resourceFilter("sink.dummy.instance1"); 0266 query.requestTree<Folder::Parent>(); 0267 0268 // Ensure all local data is processed 0269 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0270 0271 // We fetch after the data is available and don't rely on the live query mechanism to deliver the actual data 0272 auto model = Sink::Store::loadModel<Folder>(query); 0273 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0274 QCOMPARE(model->rowCount(), 1); 0275 QCOMPARE(model->rowCount(model->index(0, 0)), 1); 0276 } 0277 0278 void testIncrementalFolderTree() 0279 { 0280 // Setup 0281 auto folder = ApplicationDomainType::createEntity<Folder>("sink.dummy.instance1"); 0282 VERIFYEXEC(Sink::Store::create<Folder>(folder)); 0283 // Ensure all local data is processed 0284 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0285 0286 // Test 0287 Sink::Query query{Sink::Query::LiveQuery}; 0288 query.resourceFilter("sink.dummy.instance1"); 0289 query.requestTree<Folder::Parent>(); 0290 0291 auto model = Sink::Store::loadModel<Folder>(query); 0292 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0293 QCOMPARE(model->rowCount(), 1); 0294 0295 auto subfolder = ApplicationDomainType::createEntity<Folder>("sink.dummy.instance1"); 0296 subfolder.setParent(folder.identifier()); 0297 VERIFYEXEC(Sink::Store::create<Folder>(subfolder)); 0298 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0299 0300 //Ensure the folder appears 0301 QTRY_COMPARE(model->rowCount(model->index(0, 0)), 1); 0302 0303 //...and dissapears again after removal 0304 VERIFYEXEC(Sink::Store::remove<Folder>(subfolder)); 0305 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0306 QTRY_COMPARE(model->rowCount(model->index(0, 0)), 0); 0307 } 0308 0309 void testMailByMessageId() 0310 { 0311 // Setup 0312 { 0313 Mail mail("sink.dummy.instance1"); 0314 mail.setExtractedMessageId("test1"); 0315 mail.setProperty("sender", "doe@example.org"); 0316 Sink::Store::create<Mail>(mail).exec().waitForFinished(); 0317 } 0318 0319 { 0320 Mail mail("sink.dummy.instance1"); 0321 mail.setExtractedMessageId("test2"); 0322 mail.setProperty("sender", "doe@example.org"); 0323 Sink::Store::create<Mail>(mail).exec().waitForFinished(); 0324 } 0325 0326 // Test 0327 Sink::Query query; 0328 query.resourceFilter("sink.dummy.instance1"); 0329 query.filter<Mail::MessageId>("test1"); 0330 0331 // Ensure all local data is processed 0332 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0333 0334 // We fetch before the data is available and rely on the live query mechanism to deliver the actual data 0335 auto model = Sink::Store::loadModel<Mail>(query); 0336 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0337 QCOMPARE(model->rowCount(), 1); 0338 } 0339 0340 void testMailByFolder() 0341 { 0342 // Setup 0343 Folder::Ptr folderEntity; 0344 { 0345 Folder folder("sink.dummy.instance1"); 0346 Sink::Store::create<Folder>(folder).exec().waitForFinished(); 0347 0348 Sink::Query query; 0349 query.resourceFilter("sink.dummy.instance1"); 0350 0351 // Ensure all local data is processed 0352 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0353 0354 auto model = Sink::Store::loadModel<Folder>(query); 0355 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0356 QCOMPARE(model->rowCount(), 1); 0357 0358 folderEntity = model->index(0, 0).data(Sink::Store::DomainObjectRole).value<Folder::Ptr>(); 0359 QVERIFY(!folderEntity->identifier().isEmpty()); 0360 0361 Mail mail("sink.dummy.instance1"); 0362 mail.setExtractedMessageId("test1"); 0363 mail.setFolder(folderEntity->identifier()); 0364 Sink::Store::create<Mail>(mail).exec().waitForFinished(); 0365 } 0366 0367 // Test 0368 Sink::Query query; 0369 query.resourceFilter("sink.dummy.instance1"); 0370 query.filter<Mail::Folder>(*folderEntity); 0371 0372 // Ensure all local data is processed 0373 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0374 0375 // We fetch before the data is available and rely on the live query mechanism to deliver the actual data 0376 auto model = Sink::Store::loadModel<Mail>(query); 0377 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0378 QCOMPARE(model->rowCount(), 1); 0379 } 0380 0381 /* 0382 * Filter by two properties to make sure that we also use a non-index based filter. 0383 */ 0384 void testMailByMessageIdAndFolder() 0385 { 0386 // Setup 0387 Folder::Ptr folderEntity; 0388 { 0389 Folder folder("sink.dummy.instance1"); 0390 Sink::Store::create<Folder>(folder).exec().waitForFinished(); 0391 0392 Sink::Query query; 0393 query.resourceFilter("sink.dummy.instance1"); 0394 0395 // Ensure all local data is processed 0396 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0397 0398 auto model = Sink::Store::loadModel<Folder>(query); 0399 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0400 QCOMPARE(model->rowCount(), 1); 0401 0402 folderEntity = model->index(0, 0).data(Sink::Store::DomainObjectRole).value<Folder::Ptr>(); 0403 QVERIFY(!folderEntity->identifier().isEmpty()); 0404 0405 Mail mail("sink.dummy.instance1"); 0406 mail.setExtractedMessageId("test1"); 0407 mail.setFolder(folderEntity->identifier()); 0408 Sink::Store::create<Mail>(mail).exec().waitForFinished(); 0409 0410 Mail mail1("sink.dummy.instance1"); 0411 mail1.setExtractedMessageId("test1"); 0412 mail1.setFolder("foobar"); 0413 Sink::Store::create<Mail>(mail1).exec().waitForFinished(); 0414 0415 Mail mail2("sink.dummy.instance1"); 0416 mail2.setExtractedMessageId("test2"); 0417 mail2.setFolder(folderEntity->identifier()); 0418 Sink::Store::create<Mail>(mail2).exec().waitForFinished(); 0419 } 0420 0421 // Test 0422 Sink::Query query; 0423 query.resourceFilter("sink.dummy.instance1"); 0424 query.filter<Mail::Folder>(*folderEntity); 0425 query.filter<Mail::MessageId>("test1"); 0426 0427 // Ensure all local data is processed 0428 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0429 0430 // We fetch before the data is available and rely on the live query mechanism to deliver the actual data 0431 auto model = Sink::Store::loadModel<Mail>(query); 0432 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0433 QCOMPARE(model->rowCount(), 1); 0434 } 0435 0436 void testMailByFolderSortedByDate() 0437 { 0438 // Setup 0439 Folder::Ptr folderEntity; 0440 const auto date = QDateTime(QDate(2015, 7, 7), QTime(12, 0)); 0441 { 0442 Folder folder("sink.dummy.instance1"); 0443 Sink::Store::create<Folder>(folder).exec().waitForFinished(); 0444 0445 Sink::Query query; 0446 query.resourceFilter("sink.dummy.instance1"); 0447 0448 // Ensure all local data is processed 0449 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0450 0451 auto model = Sink::Store::loadModel<Folder>(query); 0452 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0453 QCOMPARE(model->rowCount(), 1); 0454 0455 folderEntity = model->index(0, 0).data(Sink::Store::DomainObjectRole).value<Folder::Ptr>(); 0456 QVERIFY(!folderEntity->identifier().isEmpty()); 0457 0458 { 0459 Mail mail("sink.dummy.instance1"); 0460 mail.setExtractedMessageId("testSecond"); 0461 mail.setFolder(folderEntity->identifier()); 0462 mail.setExtractedDate(date.addDays(-1)); 0463 Sink::Store::create<Mail>(mail).exec().waitForFinished(); 0464 } 0465 { 0466 Mail mail("sink.dummy.instance1"); 0467 mail.setExtractedMessageId("testLatest"); 0468 mail.setFolder(folderEntity->identifier()); 0469 mail.setExtractedDate(date); 0470 Sink::Store::create<Mail>(mail).exec().waitForFinished(); 0471 } 0472 { 0473 Mail mail("sink.dummy.instance1"); 0474 mail.setExtractedMessageId("testLast"); 0475 mail.setFolder(folderEntity->identifier()); 0476 mail.setExtractedDate(date.addDays(-2)); 0477 Sink::Store::create<Mail>(mail).exec().waitForFinished(); 0478 } 0479 } 0480 0481 // Test 0482 Sink::Query query; 0483 query.resourceFilter("sink.dummy.instance1"); 0484 query.filter<Mail::Folder>(*folderEntity); 0485 query.sort<Mail::Date>(); 0486 query.limit(1); 0487 query.setFlags(Query::LiveQuery); 0488 query.reduce<ApplicationDomain::Mail::ThreadId>(Query::Reduce::Selector::max<ApplicationDomain::Mail::Date>()) 0489 .count("count") 0490 .collect<ApplicationDomain::Mail::Unread>("unreadCollected") 0491 .collect<ApplicationDomain::Mail::Important>("importantCollected"); 0492 0493 // Ensure all local data is processed 0494 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0495 0496 auto model = Sink::Store::loadModel<Mail>(query); 0497 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0498 // The model is not sorted, but the limited set is sorted, so we can only test for the latest result. 0499 QCOMPARE(model->rowCount(), 1); 0500 QCOMPARE(model->index(0, 0).data(Sink::Store::DomainObjectRole).value<Mail::Ptr>()->getProperty("messageId").toByteArray(), QByteArray("testLatest")); 0501 0502 model->fetchMore(QModelIndex()); 0503 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0504 QCOMPARE(model->rowCount(), 2); 0505 // We can't make any assumptions about the order of the indexes 0506 // QCOMPARE(model->index(1, 0).data(Sink::Store::DomainObjectRole).value<Mail::Ptr>()->getProperty("messageId").toByteArray(), QByteArray("testSecond")); 0507 0508 //New revisions always go through 0509 { 0510 Mail mail("sink.dummy.instance1"); 0511 mail.setExtractedMessageId("testInjected"); 0512 mail.setFolder(folderEntity->identifier()); 0513 mail.setExtractedDate(date.addDays(-2)); 0514 Sink::Store::create<Mail>(mail).exec().waitForFinished(); 0515 } 0516 QTRY_COMPARE(model->rowCount(), 3); 0517 0518 //Ensure we can continue fetching after the incremental update 0519 model->fetchMore(QModelIndex()); 0520 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0521 QCOMPARE(model->rowCount(), 4); 0522 0523 //Ensure we have fetched all 0524 model->fetchMore(QModelIndex()); 0525 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0526 QCOMPARE(model->rowCount(), 4); 0527 } 0528 0529 void testReactToNewResource() 0530 { 0531 Sink::Query query; 0532 query.setFlags(Query::LiveQuery); 0533 auto model = Sink::Store::loadModel<Folder>(query); 0534 QTRY_COMPARE(model->rowCount(QModelIndex()), 0); 0535 0536 auto res = DummyResource::create(""); 0537 VERIFYEXEC(Sink::Store::create(res)); 0538 auto folder = Folder::create(res.identifier()); 0539 VERIFYEXEC(Sink::Store::create(folder)); 0540 QTRY_COMPARE(model->rowCount(QModelIndex()), 1); 0541 0542 VERIFYEXEC(Sink::Store::remove(res)); 0543 } 0544 0545 void testAccountFilter() 0546 { 0547 using namespace Sink; 0548 using namespace Sink::ApplicationDomain; 0549 0550 //Setup 0551 QString accountName("name"); 0552 QString accountIcon("icon"); 0553 auto account1 = ApplicationDomainType::createEntity<SinkAccount>(); 0554 account1.setAccountType("maildir"); 0555 account1.setName(accountName); 0556 account1.setIcon(accountIcon); 0557 VERIFYEXEC(Store::create(account1)); 0558 0559 auto account2 = ApplicationDomainType::createEntity<SinkAccount>(); 0560 account2.setAccountType("maildir"); 0561 account2.setName(accountName); 0562 account2.setIcon(accountIcon); 0563 VERIFYEXEC(Store::create(account2)); 0564 0565 auto resource1 = ApplicationDomainType::createEntity<SinkResource>(); 0566 resource1.setResourceType("sink.dummy"); 0567 resource1.setAccount(account1); 0568 Store::create(resource1).exec().waitForFinished(); 0569 0570 auto resource2 = ApplicationDomainType::createEntity<SinkResource>(); 0571 resource2.setResourceType("sink.dummy"); 0572 resource2.setAccount(account2); 0573 Store::create(resource2).exec().waitForFinished(); 0574 0575 { 0576 Folder folder1(resource1.identifier()); 0577 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 0578 Folder folder2(resource2.identifier()); 0579 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 0580 } 0581 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << resource1.identifier())); 0582 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << resource2.identifier())); 0583 0584 // Test 0585 Sink::Query query; 0586 query.resourceFilter<SinkResource::Account>(account1); 0587 0588 auto folders = Sink::Store::read<Folder>(query); 0589 QCOMPARE(folders.size(), 1); 0590 } 0591 0592 void testSubquery() 0593 { 0594 // Setup 0595 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0596 folder1.setSpecialPurpose(QByteArrayList() << "purpose1"); 0597 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 0598 0599 auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0600 folder2.setSpecialPurpose(QByteArrayList() << "purpose2"); 0601 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 0602 0603 { 0604 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 0605 mail.setExtractedMessageId("mail1"); 0606 mail.setFolder(folder1); 0607 VERIFYEXEC(Sink::Store::create(mail)); 0608 } 0609 { 0610 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 0611 mail.setExtractedMessageId("mail2"); 0612 mail.setFolder(folder2); 0613 VERIFYEXEC(Sink::Store::create(mail)); 0614 } 0615 0616 // Ensure all local data is processed 0617 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0618 0619 //Setup two folders with a mail each, ensure we only get the mail from the folder that matches the folder filter. 0620 Query query; 0621 query.filter<Mail::Folder>(Sink::Query().containsFilter<Folder::SpecialPurpose>("purpose1")); 0622 query.request<Mail::MessageId>(); 0623 0624 auto mails = Sink::Store::read<Mail>(query); 0625 QCOMPARE(mails.size(), 1); 0626 QCOMPARE(mails.first().getMessageId(), QByteArray("mail1")); 0627 } 0628 0629 void testLiveSubquery() 0630 { 0631 // Setup 0632 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0633 folder1.setSpecialPurpose(QByteArrayList() << "purpose1"); 0634 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 0635 0636 auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0637 folder2.setSpecialPurpose(QByteArrayList() << "purpose2"); 0638 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 0639 0640 { 0641 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 0642 mail.setExtractedMessageId("mail1"); 0643 mail.setFolder(folder1); 0644 VERIFYEXEC(Sink::Store::create(mail)); 0645 } 0646 { 0647 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 0648 mail.setExtractedMessageId("mail2"); 0649 mail.setFolder(folder2); 0650 VERIFYEXEC(Sink::Store::create(mail)); 0651 } 0652 0653 // Ensure all local data is processed 0654 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 0655 0656 //Setup two folders with a mail each, ensure we only get the mail from the folder that matches the folder filter. 0657 Query query; 0658 query.filter<Mail::Folder>(Sink::Query().containsFilter<Folder::SpecialPurpose>("purpose1")); 0659 query.request<Mail::MessageId>(); 0660 query.setFlags(Query::LiveQuery); 0661 0662 auto model = Sink::Store::loadModel<Mail>(query); 0663 QTRY_COMPARE(model->rowCount(), 1); 0664 0665 //This folder should not make it through the query 0666 { 0667 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 0668 mail.setExtractedMessageId("mail3"); 0669 mail.setFolder(folder2); 0670 VERIFYEXEC(Sink::Store::create(mail)); 0671 } 0672 0673 //But this one should 0674 { 0675 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 0676 mail.setExtractedMessageId("mail4"); 0677 mail.setFolder(folder1); 0678 VERIFYEXEC(Sink::Store::create(mail)); 0679 } 0680 QTRY_COMPARE(model->rowCount(), 2); 0681 0682 } 0683 0684 void testResourceSubQuery() 0685 { 0686 using namespace Sink; 0687 using namespace Sink::ApplicationDomain; 0688 0689 //Setup 0690 auto resource1 = ApplicationDomainType::createEntity<SinkResource>(); 0691 resource1.setResourceType("sink.dummy"); 0692 resource1.setCapabilities(QByteArrayList() << "cap1"); 0693 VERIFYEXEC(Store::create(resource1)); 0694 0695 auto resource2 = ApplicationDomainType::createEntity<SinkResource>(); 0696 resource2.setCapabilities(QByteArrayList() << "cap2"); 0697 resource2.setResourceType("sink.dummy"); 0698 VERIFYEXEC(Store::create(resource2)); 0699 0700 VERIFYEXEC(Sink::Store::create<Folder>(Folder{resource1.identifier()})); 0701 VERIFYEXEC(Sink::Store::create<Folder>(Folder{resource2.identifier()})); 0702 0703 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(resource1.identifier())); 0704 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(resource2.identifier())); 0705 0706 // We fetch before the data is available and rely on the live query mechanism to deliver the actual data 0707 auto folders = Sink::Store::read<Folder>(Sink::Query{}.resourceContainsFilter<SinkResource::Capabilities>("cap1")); 0708 QCOMPARE(folders.size(), 1); 0709 0710 //TODO this should be part of the regular cleanup between tests 0711 VERIFYEXEC(Store::remove(resource1)); 0712 VERIFYEXEC(Store::remove(resource2)); 0713 } 0714 0715 void testFilteredLiveResourceSubQuery() 0716 { 0717 using namespace Sink; 0718 using namespace Sink::ApplicationDomain; 0719 0720 //Setup 0721 auto resource1 = ApplicationDomainType::createEntity<SinkResource>(); 0722 resource1.setResourceType("sink.dummy"); 0723 resource1.setCapabilities(QByteArrayList() << "cap1"); 0724 VERIFYEXEC(Store::create(resource1)); 0725 VERIFYEXEC(Store::create<Folder>(Folder{resource1.identifier()})); 0726 VERIFYEXEC(ResourceControl::flushMessageQueue(resource1.identifier())); 0727 0728 auto model = Sink::Store::loadModel<Folder>(Query{Query::LiveQuery}.resourceContainsFilter<SinkResource::Capabilities>("cap1")); 0729 QTRY_COMPARE(model->rowCount(), 1); 0730 0731 auto resource2 = ApplicationDomainType::createEntity<SinkResource>(); 0732 resource2.setCapabilities(QByteArrayList() << "cap2"); 0733 resource2.setResourceType("sink.dummy"); 0734 VERIFYEXEC(Store::create(resource2)); 0735 VERIFYEXEC(Store::create<Folder>(Folder{resource2.identifier()})); 0736 VERIFYEXEC(ResourceControl::flushMessageQueue(resource2.identifier())); 0737 0738 //The new resource should be filtered and thus not make it in here 0739 QCOMPARE(model->rowCount(), 1); 0740 0741 //TODO this should be part of the regular cleanup between tests 0742 VERIFYEXEC(Store::remove(resource1)); 0743 VERIFYEXEC(Store::remove(resource2)); 0744 } 0745 0746 void testLivequeryUnmatchInThread() 0747 { 0748 // Setup 0749 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0750 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 0751 0752 auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0753 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 0754 0755 auto mail1 = Mail::createEntity<Mail>("sink.dummy.instance1"); 0756 mail1.setExtractedMessageId("mail1"); 0757 mail1.setFolder(folder1); 0758 VERIFYEXEC(Sink::Store::create(mail1)); 0759 // Ensure all local data is processed 0760 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0761 0762 //Setup two folders with a mail each, ensure we only get the mail from the folder that matches the folder filter. 0763 Query query; 0764 query.setId("testLivequeryUnmatch"); 0765 query.filter<Mail::Folder>(folder1); 0766 query.reduce<Mail::ThreadId>(Query::Reduce::Selector::max<Mail::Date>()).count("count").collect<Mail::Sender>("senders"); 0767 query.sort<Mail::Date>(); 0768 query.setFlags(Query::LiveQuery); 0769 auto model = Sink::Store::loadModel<Mail>(query); 0770 QTRY_COMPARE(model->rowCount(), 1); 0771 0772 //After the modifcation the mail should have vanished. 0773 { 0774 0775 mail1.setFolder(folder2); 0776 VERIFYEXEC(Sink::Store::modify(mail1)); 0777 } 0778 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0779 QTRY_COMPARE(model->rowCount(), 0); 0780 } 0781 0782 void testLivequeryFilterUnrelated() 0783 { 0784 // Setup 0785 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0786 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 0787 0788 auto mail1 = Mail::createEntity<Mail>("sink.dummy.instance1"); 0789 mail1.setExtractedMessageId("mail1"); 0790 mail1.setFolder(folder1); 0791 VERIFYEXEC(Sink::Store::create(mail1)); 0792 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0793 0794 Query query; 0795 query.setId("testLivequeryUnmatch"); 0796 query.filter(mail1.identifier()); 0797 query.setFlags(Query::LiveQuery); 0798 auto model = Sink::Store::loadModel<Mail>(query); 0799 QTRY_COMPARE(model->rowCount(), 1); 0800 0801 //Create another mail and make sure it doesn't show up in the query 0802 auto mail2 = Mail::createEntity<Mail>("sink.dummy.instance1"); 0803 mail2.setExtractedMessageId("mail2"); 0804 mail2.setFolder(folder1); 0805 VERIFYEXEC(Sink::Store::create(mail2)); 0806 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0807 0808 QCOMPARE(model->rowCount(), 1); 0809 0810 //A removal should still make it though 0811 { 0812 VERIFYEXEC(Sink::Store::remove(mail1)); 0813 } 0814 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0815 QTRY_COMPARE(model->rowCount(), 0); 0816 } 0817 0818 0819 void testLivequeryRemoveOneInThread() 0820 { 0821 // Setup 0822 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0823 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 0824 0825 auto mail1 = Mail::createEntity<Mail>("sink.dummy.instance1"); 0826 mail1.setExtractedMessageId("mail1"); 0827 mail1.setFolder(folder1); 0828 VERIFYEXEC(Sink::Store::create(mail1)); 0829 auto mail2 = Mail::createEntity<Mail>("sink.dummy.instance1"); 0830 mail2.setExtractedMessageId("mail2"); 0831 mail2.setFolder(folder1); 0832 VERIFYEXEC(Sink::Store::create(mail2)); 0833 // Ensure all local data is processed 0834 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0835 0836 //Setup two folders with a mail each, ensure we only get the mail from the folder that matches the folder filter. 0837 Query query; 0838 query.setId("testLivequeryUnmatch"); 0839 query.reduce<Mail::Folder>(Query::Reduce::Selector::max<Mail::Date>()).count("count").collect<Mail::Sender>("senders"); 0840 query.sort<Mail::Date>(); 0841 query.setFlags(Query::LiveQuery); 0842 auto model = Sink::Store::loadModel<Mail>(query); 0843 QTRY_COMPARE(model->rowCount(), 1); 0844 QCOMPARE(model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>()->getProperty("count").toInt(), 2); 0845 0846 //After the removal, the thread size should be reduced by one 0847 { 0848 0849 VERIFYEXEC(Sink::Store::remove(mail1)); 0850 } 0851 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0852 QTRY_COMPARE(model->rowCount(), 1); 0853 QTRY_COMPARE(model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>()->getProperty("count").toInt(), 1); 0854 0855 //After the second removal, the thread should be gone 0856 { 0857 VERIFYEXEC(Sink::Store::remove(mail2)); 0858 } 0859 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0860 QTRY_COMPARE(model->rowCount(), 0); 0861 } 0862 0863 void testDontUpdateNonLiveQuery() 0864 { 0865 // Setup 0866 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0867 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 0868 0869 auto mail1 = Mail::createEntity<Mail>("sink.dummy.instance1"); 0870 mail1.setExtractedMessageId("mail1"); 0871 mail1.setFolder(folder1); 0872 mail1.setUnread(false); 0873 VERIFYEXEC(Sink::Store::create(mail1)); 0874 0875 // Ensure all local data is processed 0876 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0877 0878 Query query; 0879 //Not a live query 0880 query.setFlags(Query::Flags{}); 0881 query.setId("testNoLiveQuery"); 0882 query.filter<Mail::Folder>(folder1); 0883 query.reduce<Mail::ThreadId>(Query::Reduce::Selector::max<Mail::Date>()).count("count").collect<Mail::Sender>("senders"); 0884 query.sort<Mail::Date>(); 0885 query.request<Mail::Unread>(); 0886 QVERIFY(!query.liveQuery()); 0887 0888 auto model = Sink::Store::loadModel<Mail>(query); 0889 QTRY_COMPARE(model->rowCount(), 1); 0890 0891 //After the modifcation the mail should have vanished. 0892 { 0893 mail1.setUnread(true); 0894 VERIFYEXEC(Sink::Store::modify(mail1)); 0895 } 0896 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0897 QTRY_COMPARE(model->rowCount(), 1); 0898 auto mail = model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>(); 0899 QTest::qWait(100); 0900 QCOMPARE(mail->getUnread(), false); 0901 } 0902 0903 void testLivequeryModifcationUpdateInThread() 0904 { 0905 // Setup 0906 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0907 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 0908 0909 auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0910 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 0911 0912 auto mail1 = Mail::createEntity<Mail>("sink.dummy.instance1"); 0913 mail1.setExtractedMessageId("mail1"); 0914 mail1.setFolder(folder1); 0915 mail1.setUnread(false); 0916 VERIFYEXEC(Sink::Store::create(mail1)); 0917 0918 // Ensure all local data is processed 0919 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0920 0921 Query query; 0922 query.setId("testLivequeryUnmatch"); 0923 query.filter<Mail::Folder>(folder1); 0924 query.reduce<Mail::ThreadId>(Query::Reduce::Selector::max<Mail::Date>()).count("count").collect<Mail::Folder>("folders"); 0925 query.sort<Mail::Date>(); 0926 query.setFlags(Query::LiveQuery); 0927 query.request<Mail::Unread>(); 0928 0929 auto model = Sink::Store::loadModel<Mail>(query); 0930 QTRY_COMPARE(model->rowCount(), 1); 0931 0932 //After the modifcation the mail should have vanished. 0933 { 0934 mail1.setUnread(true); 0935 VERIFYEXEC(Sink::Store::modify(mail1)); 0936 } 0937 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0938 QTRY_COMPARE(model->rowCount(), 1); 0939 auto mail = model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>(); 0940 QTRY_COMPARE(mail->getUnread(), true); 0941 QCOMPARE(mail->getProperty("count").toInt(), 1); 0942 QCOMPARE(mail->getProperty("folders").toList().size(), 1); 0943 } 0944 0945 void testReductionUpdate() 0946 { 0947 // Setup 0948 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0949 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 0950 0951 auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); 0952 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 0953 0954 QDateTime now{QDate{2017, 2, 3}, QTime{10, 0, 0}}; 0955 QDateTime later{QDate{2017, 2, 3}, QTime{11, 0, 0}}; 0956 0957 auto mail1 = Mail::createEntity<Mail>("sink.dummy.instance1"); 0958 mail1.setExtractedMessageId("mail1"); 0959 mail1.setFolder(folder1); 0960 mail1.setUnread(false); 0961 mail1.setExtractedDate(now); 0962 VERIFYEXEC(Sink::Store::create(mail1)); 0963 0964 // Ensure all local data is processed 0965 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0966 0967 Query query; 0968 query.setId("testLivequeryUnmatch"); 0969 query.setFlags(Query::LiveQuery); 0970 query.filter<Mail::Folder>(folder1); 0971 query.reduce<Mail::Folder>(Query::Reduce::Selector::max<Mail::Date>()).count("count").collect<Mail::Folder>("folders"); 0972 query.sort<Mail::Date>(); 0973 query.request<Mail::Unread>(); 0974 query.request<Mail::MessageId>(); 0975 0976 auto model = Sink::Store::loadModel<Mail>(query); 0977 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 0978 QCOMPARE(model->rowCount(), 1); 0979 0980 QSignalSpy insertedSpy(model.data(), &QAbstractItemModel::rowsInserted); 0981 QSignalSpy removedSpy(model.data(), &QAbstractItemModel::rowsRemoved); 0982 QSignalSpy changedSpy(model.data(), &QAbstractItemModel::dataChanged); 0983 QSignalSpy layoutChangedSpy(model.data(), &QAbstractItemModel::layoutChanged); 0984 QSignalSpy resetSpy(model.data(), &QAbstractItemModel::modelReset); 0985 0986 //The leader should change to mail2 after the modification 0987 { 0988 auto mail2 = Mail::createEntity<Mail>("sink.dummy.instance1"); 0989 mail2.setExtractedMessageId("mail2"); 0990 mail2.setFolder(folder1); 0991 mail2.setUnread(false); 0992 mail2.setExtractedDate(later); 0993 VERIFYEXEC(Sink::Store::create(mail2)); 0994 } 0995 0996 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 0997 QTRY_COMPARE(model->rowCount(), 1); 0998 auto mail = model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>(); 0999 QTRY_COMPARE(mail->getMessageId(), QByteArray{"mail2"}); 1000 QCOMPARE(mail->getProperty("count").toInt(), 2); 1001 QCOMPARE(mail->getProperty("folders").toList().size(), 2); 1002 1003 //This should eventually be just one modification instead of remove + add (See datastorequery reduce component) 1004 QCOMPARE(insertedSpy.size(), 1); 1005 QCOMPARE(removedSpy.size(), 1); 1006 QCOMPARE(changedSpy.size(), 0); 1007 QCOMPARE(layoutChangedSpy.size(), 0); 1008 QCOMPARE(resetSpy.size(), 0); 1009 } 1010 1011 void testFilteredReductionUpdate() 1012 { 1013 // Setup 1014 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1015 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 1016 1017 auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1018 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 1019 1020 // Ensure all local data is processed 1021 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1022 1023 Query query; 1024 query.setId("testFilteredReductionUpdate"); 1025 query.setFlags(Query::LiveQuery); 1026 query.filter<Mail::Folder>(folder1); 1027 query.reduce<Mail::Folder>(Query::Reduce::Selector::max<Mail::Date>()).count("count").collect<Mail::Folder>("folders"); 1028 query.sort<Mail::Date>(); 1029 1030 auto model = Sink::Store::loadModel<Mail>(query); 1031 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 1032 QCOMPARE(model->rowCount(), 0); 1033 1034 QSignalSpy insertedSpy(model.data(), &QAbstractItemModel::rowsInserted); 1035 QSignalSpy removedSpy(model.data(), &QAbstractItemModel::rowsRemoved); 1036 QSignalSpy changedSpy(model.data(), &QAbstractItemModel::dataChanged); 1037 QSignalSpy layoutChangedSpy(model.data(), &QAbstractItemModel::layoutChanged); 1038 QSignalSpy resetSpy(model.data(), &QAbstractItemModel::modelReset); 1039 1040 //Ensure we don't end up with a mail in the thread that was filtered 1041 //This tests the case of an otherwise emtpy thread on purpose. 1042 { 1043 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1044 mail.setExtractedMessageId("filtered"); 1045 mail.setFolder(folder2); 1046 mail.setExtractedDate(QDateTime{QDate{2017, 2, 3}, QTime{11, 0, 0}}); 1047 VERIFYEXEC(Sink::Store::create(mail)); 1048 } 1049 1050 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1051 QCOMPARE(model->rowCount(), 0); 1052 1053 //Ensure the non-filtered still get through. 1054 { 1055 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1056 mail.setExtractedMessageId("not-filtered"); 1057 mail.setFolder(folder1); 1058 mail.setExtractedDate(QDateTime{QDate{2017, 2, 3}, QTime{11, 0, 0}}); 1059 VERIFYEXEC(Sink::Store::create(mail)); 1060 } 1061 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1062 QTRY_COMPARE(model->rowCount(), 1); 1063 } 1064 1065 /* 1066 * Two messages in the same thread. The first get's filtered, the second one makes it. 1067 */ 1068 void testFilteredReductionUpdateInSameThread() 1069 { 1070 // Setup 1071 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1072 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 1073 1074 auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1075 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 1076 1077 // Ensure all local data is processed 1078 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1079 1080 Query query; 1081 query.setId("testFilteredReductionUpdate"); 1082 query.setFlags(Query::LiveQuery); 1083 query.filter<Mail::Folder>(folder1); 1084 query.reduce<Mail::MessageId>(Query::Reduce::Selector::max<Mail::Date>()).count("count"); 1085 1086 auto model = Sink::Store::loadModel<Mail>(query); 1087 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 1088 QCOMPARE(model->rowCount(), 0); 1089 1090 //The first message will be filtered (but would be aggreagted together with the message that passes) 1091 { 1092 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1093 mail.setExtractedMessageId("aggregatedId"); 1094 mail.setFolder(folder2); 1095 VERIFYEXEC(Sink::Store::create(mail)); 1096 1097 //Ensure that we can deal with a modification to the filtered message 1098 mail.setUnread(true); 1099 VERIFYEXEC(Sink::Store::modify(mail)); 1100 } 1101 1102 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1103 QCOMPARE(model->rowCount(), 0); 1104 1105 //Ensure the non-filtered still gets through. 1106 { 1107 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1108 mail.setExtractedMessageId("aggregatedId"); 1109 mail.setFolder(folder1); 1110 VERIFYEXEC(Sink::Store::create(mail)); 1111 1112 //Ensure that we can deal with a modification to the filtered message 1113 mail.setUnread(true); 1114 VERIFYEXEC(Sink::Store::modify(mail)); 1115 } 1116 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1117 QTRY_COMPARE(model->rowCount(), 1); 1118 QCOMPARE(model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>()->getProperty("count").toInt(), 1); 1119 1120 //Ensure another entity still results in a modification 1121 { 1122 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1123 mail.setExtractedMessageId("aggregatedId"); 1124 mail.setFolder(folder1); 1125 VERIFYEXEC(Sink::Store::create(mail)); 1126 } 1127 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1128 QTRY_COMPARE(model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>()->getProperty("count").toInt(), 2); 1129 } 1130 1131 void testBloom() 1132 { 1133 // Setup 1134 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1135 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 1136 1137 auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1138 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 1139 1140 auto mail1 = Mail::createEntity<Mail>("sink.dummy.instance1"); 1141 mail1.setExtractedMessageId("mail1"); 1142 mail1.setFolder(folder1); 1143 VERIFYEXEC(Sink::Store::create(mail1)); 1144 1145 // Ensure all local data is processed 1146 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1147 1148 { 1149 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1150 mail.setExtractedMessageId("mail2"); 1151 mail.setFolder(folder1); 1152 VERIFYEXEC(Sink::Store::create(mail)); 1153 } 1154 { 1155 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1156 mail.setExtractedMessageId("mail3"); 1157 mail.setFolder(folder2); 1158 VERIFYEXEC(Sink::Store::create(mail)); 1159 } 1160 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1161 1162 Query query; 1163 query.resourceFilter("sink.dummy.instance1"); 1164 query.setId("testFilterCreationInThread"); 1165 query.filter(mail1.identifier()); 1166 query.bloom<Mail::Folder>(); 1167 query.request<Mail::Folder>(); 1168 1169 auto model = Sink::Store::loadModel<Mail>(query); 1170 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 1171 QCOMPARE(model->rowCount(), 2); 1172 } 1173 1174 //Live query bloom filter 1175 void testLivequeryFilterCreationInThread() 1176 { 1177 // Setup 1178 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1179 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 1180 1181 auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1182 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 1183 1184 auto mail1 = Mail::createEntity<Mail>("sink.dummy.instance1"); 1185 mail1.setExtractedMessageId("mail1"); 1186 mail1.setFolder(folder1); 1187 mail1.setUnread(true); 1188 VERIFYEXEC(Sink::Store::create(mail1)); 1189 1190 // Ensure all local data is processed 1191 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1192 1193 Query query; 1194 query.setId("testFilterCreationInThread"); 1195 query.resourceFilter("sink.dummy.instance1"); 1196 query.filter(mail1.identifier()); 1197 query.bloom<Mail::Folder>(); 1198 query.sort<Mail::Date>(); 1199 query.setFlags(Query::LiveQuery); 1200 query.request<Mail::Unread>(); 1201 query.request<Mail::Folder>(); 1202 1203 auto model = Sink::Store::loadModel<Mail>(query); 1204 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 1205 QCOMPARE(model->rowCount(), 1); 1206 1207 QSignalSpy insertedSpy(model.data(), &QAbstractItemModel::rowsInserted); 1208 QSignalSpy removedSpy(model.data(), &QAbstractItemModel::rowsRemoved); 1209 QSignalSpy changedSpy(model.data(), &QAbstractItemModel::dataChanged); 1210 QSignalSpy layoutChangedSpy(model.data(), &QAbstractItemModel::layoutChanged); 1211 QSignalSpy resetSpy(model.data(), &QAbstractItemModel::modelReset); 1212 1213 //This modification should make it through 1214 { 1215 //This should not trigger an entity already in model warning 1216 mail1.setUnread(false); 1217 VERIFYEXEC(Sink::Store::modify(mail1)); 1218 } 1219 1220 //This mail should make it through 1221 { 1222 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1223 mail.setExtractedMessageId("mail2"); 1224 mail.setFolder(folder1); 1225 VERIFYEXEC(Sink::Store::create(mail)); 1226 } 1227 1228 //This mail shouldn't make it through 1229 { 1230 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1231 mail.setExtractedMessageId("mail3"); 1232 mail.setFolder(folder2); 1233 VERIFYEXEC(Sink::Store::create(mail)); 1234 } 1235 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1236 1237 QTRY_COMPARE(model->rowCount(), 2); 1238 QTest::qWait(100); 1239 QCOMPARE(model->rowCount(), 2); 1240 1241 //From mail2 1242 QCOMPARE(insertedSpy.size(), 1); 1243 QCOMPARE(removedSpy.size(), 0); 1244 //From the modification 1245 QCOMPARE(changedSpy.size(), 1); 1246 QCOMPARE(layoutChangedSpy.size(), 0); 1247 QCOMPARE(resetSpy.size(), 0); 1248 } 1249 1250 //Live query reduction 1251 void testLivequeryThreadleaderChange() 1252 { 1253 // Setup 1254 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1255 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 1256 1257 auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1258 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 1259 1260 QDateTime earlier{QDate{2017, 2, 3}, QTime{9, 0, 0}}; 1261 QDateTime now{QDate{2017, 2, 3}, QTime{10, 0, 0}}; 1262 QDateTime later{QDate{2017, 2, 3}, QTime{11, 0, 0}}; 1263 1264 auto mail1 = Mail::createEntity<Mail>("sink.dummy.instance1"); 1265 mail1.setExtractedMessageId("mail1"); 1266 mail1.setFolder(folder1); 1267 mail1.setExtractedDate(now); 1268 VERIFYEXEC(Sink::Store::create(mail1)); 1269 1270 // Ensure all local data is processed 1271 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1272 1273 Query query; 1274 query.setId("testLivequeryThreadleaderChange"); 1275 query.setFlags(Query::LiveQuery); 1276 query.reduce<Mail::Folder>(Query::Reduce::Selector::max<Mail::Date>()).count("count").collect<Mail::Folder>("folders"); 1277 query.sort<Mail::Date>(); 1278 query.request<Mail::MessageId>(); 1279 1280 auto model = Sink::Store::loadModel<Mail>(query); 1281 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 1282 QCOMPARE(model->rowCount(), 1); 1283 1284 QSignalSpy insertedSpy(model.data(), &QAbstractItemModel::rowsInserted); 1285 QSignalSpy removedSpy(model.data(), &QAbstractItemModel::rowsRemoved); 1286 QSignalSpy changedSpy(model.data(), &QAbstractItemModel::dataChanged); 1287 QSignalSpy layoutChangedSpy(model.data(), &QAbstractItemModel::layoutChanged); 1288 QSignalSpy resetSpy(model.data(), &QAbstractItemModel::modelReset); 1289 1290 //The leader shouldn't change to mail2 after the modification 1291 { 1292 auto mail2 = Mail::createEntity<Mail>("sink.dummy.instance1"); 1293 mail2.setExtractedMessageId("mail2"); 1294 mail2.setFolder(folder1); 1295 mail2.setExtractedDate(earlier); 1296 VERIFYEXEC(Sink::Store::create(mail2)); 1297 } 1298 1299 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1300 QTRY_COMPARE(model->rowCount(), 1); 1301 { 1302 auto mail = model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>(); 1303 QTRY_COMPARE(mail->getMessageId(), QByteArray{"mail1"}); 1304 QTRY_COMPARE(mail->getProperty("count").toInt(), 2); 1305 QCOMPARE(mail->getProperty("folders").toList().size(), 2); 1306 } 1307 1308 1309 QCOMPARE(insertedSpy.size(), 0); 1310 QCOMPARE(removedSpy.size(), 0); 1311 QCOMPARE(changedSpy.size(), 1); 1312 QCOMPARE(layoutChangedSpy.size(), 0); 1313 QCOMPARE(resetSpy.size(), 0); 1314 1315 //The leader should change to mail3 after the modification 1316 { 1317 auto mail3 = Mail::createEntity<Mail>("sink.dummy.instance1"); 1318 mail3.setExtractedMessageId("mail3"); 1319 mail3.setFolder(folder1); 1320 mail3.setExtractedDate(later); 1321 VERIFYEXEC(Sink::Store::create(mail3)); 1322 } 1323 1324 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1325 QTRY_COMPARE(model->rowCount(), 1); 1326 { 1327 auto mail = model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>(); 1328 QTRY_COMPARE(mail->getMessageId(), QByteArray{"mail3"}); 1329 QCOMPARE(mail->getProperty("count").toInt(), 3); 1330 QCOMPARE(mail->getProperty("folders").toList().size(), 3); 1331 } 1332 1333 //This should eventually be just one modification instead of remove + add (See datastorequery reduce component) 1334 QCOMPARE(insertedSpy.size(), 1); 1335 QCOMPARE(removedSpy.size(), 1); 1336 QCOMPARE(changedSpy.size(), 1); 1337 QCOMPARE(layoutChangedSpy.size(), 0); 1338 QCOMPARE(resetSpy.size(), 0); 1339 1340 //Nothing should change on third mail in separate folder 1341 { 1342 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1343 mail.setExtractedMessageId("mail4"); 1344 mail.setFolder(folder2); 1345 mail.setExtractedDate(now); 1346 VERIFYEXEC(Sink::Store::create(mail)); 1347 } 1348 1349 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1350 QTRY_COMPARE(model->rowCount(), 2); 1351 1352 //This should eventually be just one modification instead of remove + add (See datastorequery reduce component) 1353 QCOMPARE(insertedSpy.size(), 2); 1354 QCOMPARE(removedSpy.size(), 1); 1355 QCOMPARE(changedSpy.size(), 1); 1356 QCOMPARE(layoutChangedSpy.size(), 0); 1357 QCOMPARE(resetSpy.size(), 0); 1358 } 1359 1360 /* 1361 * Ensure that we handle the situation properly if the thread-leader doesn't match a property filter. 1362 */ 1363 void testFilteredThreadLeader() 1364 { 1365 // Setup 1366 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1367 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 1368 1369 auto folder2 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1370 VERIFYEXEC(Sink::Store::create<Folder>(folder2)); 1371 1372 QDateTime earlier{QDate{2017, 2, 3}, QTime{9, 0, 0}}; 1373 QDateTime now{QDate{2017, 2, 3}, QTime{10, 0, 0}}; 1374 QDateTime later{QDate{2017, 2, 3}, QTime{11, 0, 0}}; 1375 1376 auto createMail = [] (const QByteArray &messageid, const Folder &folder, const QDateTime &date, bool important) { 1377 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1378 mail.setExtractedSubject(messageid); 1379 mail.setExtractedMessageId(messageid); 1380 mail.setFolder(folder); 1381 mail.setExtractedDate(date); 1382 mail.setImportant(important); 1383 return mail; 1384 }; 1385 1386 VERIFYEXEC(Sink::Store::create(createMail("mail1", folder1, now, false))); 1387 VERIFYEXEC(Sink::Store::create(createMail("mail2", folder1, earlier, false))); 1388 VERIFYEXEC(Sink::Store::create(createMail("mail3", folder1, later, true))); 1389 1390 // Ensure all local data is processed 1391 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1392 1393 Query query; 1394 query.setId("testLivequeryThreadleaderChange"); 1395 query.setFlags(Query::LiveQuery); 1396 query.reduce<Mail::Folder>(Query::Reduce::Selector::max<Mail::Date>()) 1397 .count() 1398 .collect<Mail::Folder>() 1399 .select<Mail::Subject>(Query::Reduce::Selector::Min, "subjectSelected"); 1400 query.sort<Mail::Date>(); 1401 query.request<Mail::MessageId>(); 1402 query.request<Mail::Subject>(); 1403 query.filter<Mail::Important>(false); 1404 1405 auto model = Sink::Store::loadModel<Mail>(query); 1406 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 1407 1408 QCOMPARE(model->rowCount(), 1); 1409 1410 { 1411 auto mail = model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>(); 1412 QCOMPARE(mail->getMessageId(), QByteArray{"mail1"}); 1413 QCOMPARE(mail->count(), 2); 1414 QCOMPARE(mail->getCollectedProperty<Mail::Folder>().size(), 2); 1415 QCOMPARE(mail->getProperty("subjectSelected").toString(), QString{"mail2"}); 1416 } 1417 } 1418 1419 void testQueryRunnerDontMissUpdates() 1420 { 1421 // Setup 1422 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1423 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 1424 1425 QDateTime now{QDate{2017, 2, 3}, QTime{10, 0, 0}}; 1426 1427 auto createMail = [] (const QByteArray &messageid, const Folder &folder, const QDateTime &date, bool important) { 1428 auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); 1429 mail.setExtractedMessageId(messageid); 1430 mail.setFolder(folder); 1431 mail.setExtractedDate(date); 1432 mail.setImportant(important); 1433 return mail; 1434 }; 1435 1436 VERIFYEXEC(Sink::Store::create(createMail("mail1", folder1, now, false))); 1437 1438 // Ensure all local data is processed 1439 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1440 1441 Query query; 1442 query.setFlags(Query::LiveQuery); 1443 1444 Sink::ResourceContext resourceContext{"sink.dummy.instance1", "sink.dummy", Sink::AdaptorFactoryRegistry::instance().getFactories("sink.dummy")}; 1445 Sink::Log::Context logCtx; 1446 auto runner = new QueryRunner<Mail>(query, resourceContext, ApplicationDomain::getTypeName<Mail>(), logCtx); 1447 runner->delayNextQuery(); 1448 1449 auto emitter = runner->emitter(); 1450 QList<Mail::Ptr> added; 1451 emitter->onAdded([&](Mail::Ptr mail) { 1452 added << mail; 1453 }); 1454 1455 emitter->fetch(); 1456 VERIFYEXEC(Sink::Store::create(createMail("mail2", folder1, now, false))); 1457 QTRY_COMPARE(added.size(), 2); 1458 1459 runner->delayNextQuery(); 1460 VERIFYEXEC(Sink::Store::create(createMail("mail3", folder1, now, false))); 1461 //The second revision update is supposed to come in while the initial revision update is still in the query. 1462 //So wait a bit to make sure the query is currently runnning. 1463 QTest::qWait(500); 1464 VERIFYEXEC(Sink::Store::create(createMail("mail4", folder1, now, false))); 1465 QTRY_COMPARE(added.size(), 4); 1466 } 1467 1468 /* 1469 * This test excercises the scenario where a fetchMore is triggered after 1470 * the revision is already updated in storage, but the incremental query was not run yet. 1471 * This resulted in lost modification updates. 1472 * It also exercised the lower bound protection, because we delay the update, and thus the resource will already have cleaned up. 1473 */ 1474 void testQueryRunnerDontMissUpdatesWithFetchMore() 1475 { 1476 // Setup 1477 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1"); 1478 folder1.setName("name1"); 1479 VERIFYEXEC(Sink::Store::create<Folder>(folder1)); 1480 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1481 1482 Query query; 1483 query.setFlags(Query::LiveQuery); 1484 1485 Sink::ResourceContext resourceContext{"sink.dummy.instance1", "sink.dummy", Sink::AdaptorFactoryRegistry::instance().getFactories("sink.dummy")}; 1486 Sink::Log::Context logCtx; 1487 auto runner = new QueryRunner<Folder>(query, resourceContext, ApplicationDomain::getTypeName<Folder>(), logCtx); 1488 1489 auto emitter = runner->emitter(); 1490 QList<Folder::Ptr> added; 1491 emitter->onAdded([&](Folder::Ptr folder) { 1492 added << folder; 1493 }); 1494 QList<Folder::Ptr> modified; 1495 emitter->onModified([&](Folder::Ptr folder) { 1496 modified << folder; 1497 }); 1498 QList<Folder::Ptr> removed; 1499 emitter->onRemoved([&](Folder::Ptr folder) { 1500 removed << folder; 1501 }); 1502 1503 emitter->fetch(); 1504 QTRY_COMPARE(added.size(), 1); 1505 QCOMPARE(modified.size(), 0); 1506 QCOMPARE(removed.size(), 0); 1507 1508 runner->ignoreRevisionChanges(); 1509 1510 folder1.setName("name2"); 1511 VERIFYEXEC(Sink::Store::modify<Folder>(folder1)); 1512 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1513 1514 emitter->fetch(); 1515 1516 runner->triggerRevisionChange(); 1517 1518 QTRY_COMPARE(added.size(), 1); 1519 QTRY_COMPARE(modified.size(), 1); 1520 QCOMPARE(removed.size(), 0); 1521 1522 runner->ignoreRevisionChanges(); 1523 VERIFYEXEC(Sink::Store::remove<Folder>(folder1)); 1524 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1525 runner->triggerRevisionChange(); 1526 QTRY_COMPARE(removed.size(), 1); 1527 } 1528 1529 /* 1530 * This test is here to ensure we don't crash if we call removeFromDisk with a running query. 1531 */ 1532 void testRemoveFromDiskWithRunningQuery() 1533 { 1534 // FIXME: we currently crash 1535 QSKIP("Skipping because this produces a crash."); 1536 { 1537 // Setup 1538 Folder::Ptr folderEntity; 1539 const auto date = QDateTime(QDate(2015, 7, 7), QTime(12, 0)); 1540 { 1541 Folder folder("sink.dummy.instance1"); 1542 Sink::Store::create<Folder>(folder).exec().waitForFinished(); 1543 1544 Sink::Query query; 1545 query.resourceFilter("sink.dummy.instance1"); 1546 1547 // Ensure all local data is processed 1548 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 1549 1550 auto model = Sink::Store::loadModel<Folder>(query); 1551 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 1552 QCOMPARE(model->rowCount(), 1); 1553 1554 folderEntity = model->index(0, 0).data(Sink::Store::DomainObjectRole).value<Folder::Ptr>(); 1555 QVERIFY(!folderEntity->identifier().isEmpty()); 1556 1557 //Add enough data so the query takes long enough that we remove the data from disk whlie the query is ongoing. 1558 for (int i = 0; i < 100; i++) { 1559 Mail mail("sink.dummy.instance1"); 1560 mail.setExtractedMessageId("test" + QByteArray::number(i)); 1561 mail.setFolder(folderEntity->identifier()); 1562 mail.setExtractedDate(date.addDays(i)); 1563 Sink::Store::create<Mail>(mail).exec().waitForFinished(); 1564 } 1565 } 1566 1567 // Test 1568 Sink::Query query; 1569 query.resourceFilter("sink.dummy.instance1"); 1570 query.filter<Mail::Folder>(*folderEntity); 1571 query.sort<Mail::Date>(); 1572 query.setFlags(Query::LiveQuery); 1573 query.reduce<ApplicationDomain::Mail::ThreadId>(Query::Reduce::Selector::max<ApplicationDomain::Mail::Date>()) 1574 .count("count") 1575 .collect<ApplicationDomain::Mail::Unread>("unreadCollected") 1576 .collect<ApplicationDomain::Mail::Important>("importantCollected"); 1577 1578 // Ensure all local data is processed 1579 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); 1580 1581 auto model = Sink::Store::loadModel<Mail>(query); 1582 } 1583 1584 //FIXME: this will result in a crash in the above still running query. 1585 VERIFYEXEC(Sink::Store::removeDataFromDisk(QByteArray("sink.dummy.instance1"))); 1586 } 1587 1588 void testMailFulltext() 1589 { 1590 QByteArray id1; 1591 QByteArray id2; 1592 // Setup 1593 { 1594 { 1595 auto msg = KMime::Message::Ptr::create(); 1596 msg->subject()->from7BitString("Subject To Search"); 1597 msg->setBody("This is the searchable body bar. unique sender2"); 1598 msg->from()->from7BitString("\"The Sender\"<sender@example.org>"); 1599 msg->to()->from7BitString("\"Foo Bar\"<foo-bar@example.org>"); 1600 msg->assemble(); 1601 1602 auto mail = ApplicationDomainType::createEntity<Mail>("sink.dummy.instance1"); 1603 mail.setExtractedMessageId("test1"); 1604 mail.setFolder("folder1"); 1605 mail.setMimeMessage(msg->encodedContent()); 1606 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 1607 id1 = mail.identifier(); 1608 } 1609 { 1610 auto msg = KMime::Message::Ptr::create(); 1611 msg->subject()->from7BitString("Stuff to Search"); 1612 msg->setBody("Body foo bar"); 1613 msg->from()->from7BitString("\"Another Sender2\"<sender2@unique.com>"); 1614 msg->assemble(); 1615 auto mail = ApplicationDomainType::createEntity<Mail>("sink.dummy.instance1"); 1616 mail.setExtractedMessageId("test2"); 1617 mail.setFolder("folder2"); 1618 mail.setMimeMessage(msg->encodedContent()); 1619 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 1620 id2 = mail.identifier(); 1621 } 1622 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1623 { 1624 FulltextIndex index("sink.dummy.instance1", Sink::Storage::DataStore::ReadOnly); 1625 qInfo() << QString("Found document 1 with terms: ") + index.getIndexContent(id1).terms.join(", "); 1626 qInfo() << QString("Found document 2 with terms: ") + index.getIndexContent(id2).terms.join(", "); 1627 } 1628 } 1629 1630 // Test 1631 // Default search 1632 { 1633 Sink::Query query; 1634 query.resourceFilter("sink.dummy.instance1"); 1635 query.filter<Mail::Subject>(QueryBase::Comparator(QString("Subject To Search"), QueryBase::Comparator::Fulltext)); 1636 const auto list = Sink::Store::read<Mail>(query); 1637 QCOMPARE(list.size(), 1); 1638 QCOMPARE(list.first().identifier(), id1); 1639 } 1640 // Phrase search 1641 { 1642 Sink::Query query; 1643 query.resourceFilter("sink.dummy.instance1"); 1644 query.filter<Mail::Subject>(QueryBase::Comparator(QString("\"Subject To Search\""), QueryBase::Comparator::Fulltext)); 1645 const auto list = Sink::Store::read<Mail>(query); 1646 QCOMPARE(list.size(), 1); 1647 QCOMPARE(list.first().identifier(), id1); 1648 } 1649 { 1650 Sink::Query query; 1651 query.resourceFilter("sink.dummy.instance1"); 1652 query.filter<Mail::Subject>(QueryBase::Comparator(QString("\"Stuff to Search\""), QueryBase::Comparator::Fulltext)); 1653 const auto list = Sink::Store::read<Mail>(query); 1654 QCOMPARE(list.size(), 1); 1655 } 1656 //Operators 1657 { 1658 Sink::Query query; 1659 query.resourceFilter("sink.dummy.instance1"); 1660 query.filter<Mail::Subject>(QueryBase::Comparator(QString("subject AND search"), QueryBase::Comparator::Fulltext)); 1661 const auto list = Sink::Store::read<Mail>(query); 1662 QCOMPARE(list.size(), 1); 1663 QCOMPARE(list.first().identifier(), id1); 1664 } 1665 { 1666 Sink::Query query; 1667 query.resourceFilter("sink.dummy.instance1"); 1668 query.filter<Mail::Subject>(QueryBase::Comparator(QString("subject OR search"), QueryBase::Comparator::Fulltext)); 1669 QCOMPARE(Sink::Store::read<Mail>(query).size(), 2); 1670 } 1671 //Case-insensitive 1672 { 1673 Sink::Query query; 1674 query.resourceFilter("sink.dummy.instance1"); 1675 query.filter<Mail::Subject>(QueryBase::Comparator(QString("Subject"), QueryBase::Comparator::Fulltext)); 1676 const auto list = Sink::Store::read<Mail>(query); 1677 QCOMPARE(list.size(), 1); 1678 QCOMPARE(list.first().identifier(), id1); 1679 } 1680 //Case-insensitive 1681 { 1682 Sink::Query query; 1683 query.resourceFilter("sink.dummy.instance1"); 1684 query.filter<Mail::Subject>(QueryBase::Comparator(QString("subject"), QueryBase::Comparator::Fulltext)); 1685 const auto list = Sink::Store::read<Mail>(query); 1686 QCOMPARE(list.size(), 1); 1687 QCOMPARE(list.first().identifier(), id1); 1688 } 1689 //Partial match 1690 { 1691 Sink::Query query; 1692 query.resourceFilter("sink.dummy.instance1"); 1693 query.filter<Mail::Subject>(QueryBase::Comparator(QString("subj"), QueryBase::Comparator::Fulltext)); 1694 const auto list = Sink::Store::read<Mail>(query); 1695 QCOMPARE(list.size(), 1); 1696 QCOMPARE(list.first().identifier(), id1); 1697 } 1698 //Filter by body 1699 { 1700 Sink::Query query; 1701 query.resourceFilter("sink.dummy.instance1"); 1702 query.filter<Mail::MimeMessage>(QueryBase::Comparator(QString("searchable"), QueryBase::Comparator::Fulltext)); 1703 const auto list = Sink::Store::read<Mail>(query); 1704 QCOMPARE(list.size(), 1); 1705 QCOMPARE(list.first().identifier(), id1); 1706 } 1707 //Filter by folder 1708 { 1709 Sink::Query query; 1710 query.resourceFilter("sink.dummy.instance1"); 1711 query.filter<Mail::Subject>(QueryBase::Comparator(QString("Subject"), QueryBase::Comparator::Fulltext)); 1712 query.filter<Mail::Folder>("folder1"); 1713 const auto list = Sink::Store::read<Mail>(query); 1714 QCOMPARE(list.size(), 1); 1715 QCOMPARE(list.first().identifier(), id1); 1716 } 1717 //Filter by folder 1718 { 1719 Sink::Query query; 1720 query.resourceFilter("sink.dummy.instance1"); 1721 query.filter<Mail::Subject>(QueryBase::Comparator(QString("Subject"), QueryBase::Comparator::Fulltext)); 1722 query.filter<Mail::Folder>("folder2"); 1723 QCOMPARE(Sink::Store::read<Mail>(query).size(), 0); 1724 } 1725 //Filter by sender 1726 { 1727 Sink::Query query; 1728 query.resourceFilter("sink.dummy.instance1"); 1729 query.filter({}, Sink::QueryBase::Comparator(QString("sender"), Sink::QueryBase::Comparator::Fulltext)); 1730 const auto list = Sink::Store::read<Mail>(query); 1731 QCOMPARE(list.size(), 2); 1732 } 1733 //Filter by sender 1734 { 1735 Sink::Query query; 1736 query.resourceFilter("sink.dummy.instance1"); 1737 query.filter({}, Sink::QueryBase::Comparator(QString("Sender"), Sink::QueryBase::Comparator::Fulltext)); 1738 const auto list = Sink::Store::read<Mail>(query); 1739 QCOMPARE(list.size(), 2); 1740 } 1741 //Filter by sender 1742 { 1743 Sink::Query query; 1744 query.resourceFilter("sink.dummy.instance1"); 1745 query.filter({}, Sink::QueryBase::Comparator(QString("sender@example"), Sink::QueryBase::Comparator::Fulltext)); 1746 const auto list = Sink::Store::read<Mail>(query); 1747 QCOMPARE(list.size(), 1); 1748 QCOMPARE(list.first().identifier(), id1); 1749 } 1750 //Filter by sender 1751 { 1752 Sink::Query query; 1753 query.resourceFilter("sink.dummy.instance1"); 1754 query.filter({}, Sink::QueryBase::Comparator(QString("The Sender"), Sink::QueryBase::Comparator::Fulltext)); 1755 const auto list = Sink::Store::read<Mail>(query); 1756 QCOMPARE(list.size(), 1); 1757 } 1758 1759 //Filter by sender 1760 { 1761 Sink::Query query; 1762 query.resourceFilter("sink.dummy.instance1"); 1763 query.filter({}, Sink::QueryBase::Comparator(QString("sender2@unique.com"), Sink::QueryBase::Comparator::Fulltext)); 1764 const auto list = Sink::Store::read<Mail>(query); 1765 QCOMPARE(list.size(), 1); 1766 QCOMPARE(list.first().identifier(), id2); 1767 } 1768 1769 //Filter by recipient 1770 { 1771 Sink::Query query; 1772 query.resourceFilter("sink.dummy.instance1"); 1773 query.filter({}, Sink::QueryBase::Comparator(QString("foo-bar@example.org"), Sink::QueryBase::Comparator::Fulltext)); 1774 const auto list = Sink::Store::read<Mail>(query); 1775 QCOMPARE(list.size(), 1); 1776 QCOMPARE(list.first().identifier(), id1); 1777 } 1778 1779 //Filter by recipient 1780 { 1781 Sink::Query query; 1782 query.resourceFilter("sink.dummy.instance1"); 1783 query.filter({}, Sink::QueryBase::Comparator(QString("foo-bar@example.com"), Sink::QueryBase::Comparator::Fulltext)); 1784 QCOMPARE(Sink::Store::read<Mail>(query).size(), 0); 1785 } 1786 1787 //Filter by subject field 1788 { 1789 Sink::Query query; 1790 query.resourceFilter("sink.dummy.instance1"); 1791 query.filter({}, QueryBase::Comparator(QString("subject:\"Subject To Search\""), QueryBase::Comparator::Fulltext)); 1792 const auto list = Sink::Store::read<Mail>(query); 1793 QCOMPARE(list.size(), 1); 1794 QCOMPARE(list.first().identifier(), id1); 1795 } 1796 //Ensure the query searches the right field 1797 { 1798 Sink::Query query; 1799 query.resourceFilter("sink.dummy.instance1"); 1800 query.filter({}, QueryBase::Comparator(QString("sender:\"Subject To Search\""), QueryBase::Comparator::Fulltext)); 1801 const auto list = Sink::Store::read<Mail>(query); 1802 QCOMPARE(list.size(), 0); 1803 } 1804 } 1805 1806 void testUTF8MailFulltext() 1807 { 1808 QByteArray id1; 1809 // Setup 1810 { 1811 { 1812 auto msg = KMime::Message::Ptr::create(); 1813 msg->subject()->fromUnicodeString("sübject", "utf8"); 1814 msg->setBody("büdi"); 1815 msg->from()->fromUnicodeString("\"John Düderli\"<john@doe.com>", "utf8"); 1816 msg->assemble(); 1817 1818 auto mail = ApplicationDomainType::createEntity<Mail>("sink.dummy.instance1"); 1819 mail.setExtractedMessageId("test1"); 1820 mail.setFolder("folder1"); 1821 mail.setMimeMessage(msg->encodedContent()); 1822 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 1823 id1 = mail.identifier(); 1824 } 1825 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1826 { 1827 FulltextIndex index("sink.dummy.instance1", Sink::Storage::DataStore::ReadOnly); 1828 qInfo() << QString("found document 1 with terms: ") + index.getIndexContent(id1).terms.join(", "); 1829 } 1830 } 1831 { 1832 Sink::Query query; 1833 query.resourceFilter("sink.dummy.instance1"); 1834 query.filter({}, Sink::QueryBase::Comparator(QString("sübject"), Sink::QueryBase::Comparator::Fulltext)); 1835 const auto list = Sink::Store::read<Mail>(query); 1836 QCOMPARE(list.size(), 1); 1837 QCOMPARE(list.first().identifier(), id1); 1838 } 1839 { 1840 Sink::Query query; 1841 query.resourceFilter("sink.dummy.instance1"); 1842 query.filter({}, Sink::QueryBase::Comparator(QString("büdi"), Sink::QueryBase::Comparator::Fulltext)); 1843 const auto list = Sink::Store::read<Mail>(query); 1844 QCOMPARE(list.size(), 1); 1845 QCOMPARE(list.first().identifier(), id1); 1846 } 1847 { 1848 Sink::Query query; 1849 query.resourceFilter("sink.dummy.instance1"); 1850 query.filter({}, Sink::QueryBase::Comparator(QString("düderli"), Sink::QueryBase::Comparator::Fulltext)); 1851 const auto list = Sink::Store::read<Mail>(query); 1852 QCOMPARE(list.size(), 1); 1853 QCOMPARE(list.first().identifier(), id1); 1854 } 1855 } 1856 1857 void testLiveMailFulltext() 1858 { 1859 Sink::Query query; 1860 query.setFlags(Query::LiveQuery); 1861 query.resourceFilter("sink.dummy.instance1"); 1862 query.filter<Mail::Subject>(QueryBase::Comparator(QString("Live Subject To Search"), QueryBase::Comparator::Fulltext)); 1863 1864 auto model = Sink::Store::loadModel<Mail>(query); 1865 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 1866 QCOMPARE(model->rowCount(), 0); 1867 Mail mailToModify; 1868 { 1869 { 1870 auto msg = KMime::Message::Ptr::create(); 1871 msg->subject()->from7BitString("Not a match"); 1872 msg->setBody("This is the searchable body bar. unique sender1"); 1873 msg->from()->from7BitString("\"The Sender\"<sender@example.org>"); 1874 msg->to()->from7BitString("\"Foo Bar\"<foo-bar@example.org>"); 1875 msg->assemble(); 1876 1877 auto mail = ApplicationDomainType::createEntity<Mail>("sink.dummy.instance1"); 1878 mail.setExtractedMessageId("test1"); 1879 mail.setFolder("folder1"); 1880 mail.setMimeMessage(msg->encodedContent()); 1881 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 1882 } 1883 { 1884 auto msg = KMime::Message::Ptr::create(); 1885 msg->subject()->from7BitString("Live Subject To Search"); 1886 msg->setBody("This is the searchable body bar. unique sender2"); 1887 msg->from()->from7BitString("\"The Sender\"<sender@example.org>"); 1888 msg->to()->from7BitString("\"Foo Bar\"<foo-bar@example.org>"); 1889 msg->assemble(); 1890 1891 auto mail = ApplicationDomainType::createEntity<Mail>("sink.dummy.instance1"); 1892 mail.setExtractedMessageId("test1"); 1893 mail.setFolder("folder1"); 1894 mail.setMimeMessage(msg->encodedContent()); 1895 mail.setUnread(true); 1896 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 1897 mailToModify = mail; 1898 } 1899 1900 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 1901 } 1902 QTRY_COMPARE(model->rowCount(), 1); 1903 //Test a modification that shouldn't affect the result 1904 { 1905 QSignalSpy insertedSpy(model.data(), &QAbstractItemModel::rowsInserted); 1906 QSignalSpy removedSpy(model.data(), &QAbstractItemModel::rowsRemoved); 1907 QSignalSpy changedSpy(model.data(), &QAbstractItemModel::dataChanged); 1908 QSignalSpy layoutChangedSpy(model.data(), &QAbstractItemModel::layoutChanged); 1909 QSignalSpy resetSpy(model.data(), &QAbstractItemModel::modelReset); 1910 1911 mailToModify.setUnread(false); 1912 VERIFYEXEC(Sink::Store::modify(mailToModify)); 1913 1914 QTRY_COMPARE(changedSpy.size(), 1); 1915 QCOMPARE(insertedSpy.size(), 0); 1916 QCOMPARE(removedSpy.size(), 0); 1917 QCOMPARE(layoutChangedSpy.size(), 0); 1918 QCOMPARE(resetSpy.size(), 0); 1919 } 1920 //Test a modification that should affect the result 1921 { 1922 QSignalSpy insertedSpy(model.data(), &QAbstractItemModel::rowsInserted); 1923 QSignalSpy removedSpy(model.data(), &QAbstractItemModel::rowsRemoved); 1924 QSignalSpy changedSpy(model.data(), &QAbstractItemModel::dataChanged); 1925 QSignalSpy layoutChangedSpy(model.data(), &QAbstractItemModel::layoutChanged); 1926 QSignalSpy resetSpy(model.data(), &QAbstractItemModel::modelReset); 1927 1928 auto msg = KMime::Message::Ptr::create(); 1929 msg->subject()->from7BitString("No longer a match"); 1930 msg->setBody("This is the searchable body bar. unique sender2"); 1931 msg->from()->from7BitString("\"The Sender\"<sender@example.org>"); 1932 msg->to()->from7BitString("\"Foo Bar\"<foo-bar@example.org>"); 1933 msg->assemble(); 1934 1935 mailToModify.setMimeMessage(msg->encodedContent()); 1936 VERIFYEXEC(Sink::Store::modify(mailToModify)); 1937 1938 QTRY_COMPARE(removedSpy.size(), 1); 1939 QCOMPARE(changedSpy.size(), 0); 1940 QCOMPARE(insertedSpy.size(), 0); 1941 QCOMPARE(layoutChangedSpy.size(), 0); 1942 QCOMPARE(resetSpy.size(), 0); 1943 } 1944 QCOMPARE(model->rowCount(), 0); 1945 } 1946 1947 void testLiveMailFulltextThreaded() 1948 { 1949 Sink::Query query; 1950 query.setFlags(Query::LiveQuery); 1951 query.resourceFilter("sink.dummy.instance1"); 1952 //Rely on partial matching 1953 query.filter<Mail::Subject>(QueryBase::Comparator(QString("LiveSubject"), QueryBase::Comparator::Fulltext)); 1954 query.reduce<Mail::Folder>(Query::Reduce::Selector::max<Mail::Date>()).count("count").collect<Mail::Sender>("senders"); 1955 1956 auto model = Sink::Store::loadModel<Mail>(query); 1957 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 1958 QCOMPARE(model->rowCount(), 0); 1959 Mail mail1; 1960 Mail mail2; 1961 Mail mail3; 1962 { 1963 { 1964 auto msg = KMime::Message::Ptr::create(); 1965 msg->subject()->from7BitString("Not a match"); 1966 msg->setBody("This is the searchable body bar. unique sender1"); 1967 msg->from()->from7BitString("\"The Sender\"<sender@example.org>"); 1968 msg->to()->from7BitString("\"Foo Bar\"<foo-bar@example.org>"); 1969 msg->assemble(); 1970 1971 auto mail = ApplicationDomainType::createEntity<Mail>("sink.dummy.instance1"); 1972 mail.setExtractedMessageId("test1"); 1973 mail.setFolder("folder1"); 1974 mail.setMimeMessage(msg->encodedContent()); 1975 mail.setUnread(true); 1976 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 1977 mail1 = mail; 1978 } 1979 { 1980 auto msg = KMime::Message::Ptr::create(); 1981 msg->subject()->from7BitString("LiveSubjectToSearch"); 1982 msg->setBody("This is the searchable body bar. unique sender2"); 1983 msg->from()->from7BitString("\"The Sender\"<sender@example.org>"); 1984 msg->to()->from7BitString("\"Foo Bar\"<foo-bar@example.org>"); 1985 msg->assemble(); 1986 1987 auto mail = ApplicationDomainType::createEntity<Mail>("sink.dummy.instance1"); 1988 mail.setExtractedMessageId("test2"); 1989 mail.setFolder("folder1"); 1990 mail.setMimeMessage(msg->encodedContent()); 1991 mail.setUnread(true); 1992 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 1993 mail2 = mail; 1994 } 1995 { 1996 auto msg = KMime::Message::Ptr::create(); 1997 msg->subject()->from7BitString("LiveSubjectToSearch"); 1998 msg->setBody("This is the searchable body bar. unique sender2"); 1999 msg->from()->from7BitString("\"The Sender\"<sender@example.org>"); 2000 msg->to()->from7BitString("\"Foo Bar\"<foo-bar@example.org>"); 2001 msg->assemble(); 2002 2003 auto mail = ApplicationDomainType::createEntity<Mail>("sink.dummy.instance1"); 2004 mail.setExtractedMessageId("test3"); 2005 mail.setFolder("folder2"); 2006 mail.setMimeMessage(msg->encodedContent()); 2007 mail.setUnread(true); 2008 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 2009 mail3 = mail; 2010 } 2011 2012 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 2013 } 2014 QTRY_COMPARE(model->rowCount(), 2); 2015 //Test a modification that shouldn't affect the result 2016 { 2017 QSignalSpy insertedSpy(model.data(), &QAbstractItemModel::rowsInserted); 2018 QSignalSpy removedSpy(model.data(), &QAbstractItemModel::rowsRemoved); 2019 QSignalSpy changedSpy(model.data(), &QAbstractItemModel::dataChanged); 2020 QSignalSpy layoutChangedSpy(model.data(), &QAbstractItemModel::layoutChanged); 2021 QSignalSpy resetSpy(model.data(), &QAbstractItemModel::modelReset); 2022 2023 mail2.setUnread(false); 2024 VERIFYEXEC(Sink::Store::modify(mail2)); 2025 2026 QTRY_COMPARE(changedSpy.size(), 1); 2027 QCOMPARE(insertedSpy.size(), 0); 2028 QCOMPARE(removedSpy.size(), 0); 2029 QCOMPARE(layoutChangedSpy.size(), 0); 2030 QCOMPARE(resetSpy.size(), 0); 2031 } 2032 2033 //Test a modification that shouldn't affect the result 2034 { 2035 QSignalSpy insertedSpy(model.data(), &QAbstractItemModel::rowsInserted); 2036 QSignalSpy removedSpy(model.data(), &QAbstractItemModel::rowsRemoved); 2037 QSignalSpy changedSpy(model.data(), &QAbstractItemModel::dataChanged); 2038 QSignalSpy layoutChangedSpy(model.data(), &QAbstractItemModel::layoutChanged); 2039 QSignalSpy resetSpy(model.data(), &QAbstractItemModel::modelReset); 2040 2041 mail1.setUnread(false); 2042 VERIFYEXEC(Sink::Store::modify(mail1)); 2043 2044 QTRY_COMPARE(changedSpy.size(), 1); 2045 QCOMPARE(insertedSpy.size(), 0); 2046 QCOMPARE(removedSpy.size(), 0); 2047 QCOMPARE(layoutChangedSpy.size(), 0); 2048 QCOMPARE(resetSpy.size(), 0); 2049 } 2050 2051 { 2052 QSignalSpy insertedSpy(model.data(), &QAbstractItemModel::rowsInserted); 2053 QSignalSpy removedSpy(model.data(), &QAbstractItemModel::rowsRemoved); 2054 QSignalSpy changedSpy(model.data(), &QAbstractItemModel::dataChanged); 2055 QSignalSpy layoutChangedSpy(model.data(), &QAbstractItemModel::layoutChanged); 2056 QSignalSpy resetSpy(model.data(), &QAbstractItemModel::modelReset); 2057 2058 mail3.setUnread(false); 2059 VERIFYEXEC(Sink::Store::modify(mail3)); 2060 2061 QTRY_COMPARE(changedSpy.size(), 1); 2062 QCOMPARE(insertedSpy.size(), 0); 2063 QCOMPARE(removedSpy.size(), 0); 2064 QCOMPARE(layoutChangedSpy.size(), 0); 2065 QCOMPARE(resetSpy.size(), 0); 2066 } 2067 2068 //Test a modification that should affect the result 2069 { 2070 QSignalSpy insertedSpy(model.data(), &QAbstractItemModel::rowsInserted); 2071 QSignalSpy removedSpy(model.data(), &QAbstractItemModel::rowsRemoved); 2072 QSignalSpy changedSpy(model.data(), &QAbstractItemModel::dataChanged); 2073 QSignalSpy layoutChangedSpy(model.data(), &QAbstractItemModel::layoutChanged); 2074 QSignalSpy resetSpy(model.data(), &QAbstractItemModel::modelReset); 2075 2076 auto msg = KMime::Message::Ptr::create(); 2077 msg->subject()->from7BitString("No longer a match"); 2078 msg->setBody("This is the searchable body bar. unique sender2"); 2079 msg->from()->from7BitString("\"The Sender\"<sender@example.org>"); 2080 msg->to()->from7BitString("\"Foo Bar\"<foo-bar@example.org>"); 2081 msg->assemble(); 2082 2083 mail2.setMimeMessage(msg->encodedContent()); 2084 VERIFYEXEC(Sink::Store::modify(mail2)); 2085 2086 QTRY_COMPARE(removedSpy.size(), 1); 2087 QCOMPARE(changedSpy.size(), 0); 2088 QCOMPARE(insertedSpy.size(), 0); 2089 QCOMPARE(layoutChangedSpy.size(), 0); 2090 QCOMPARE(resetSpy.size(), 0); 2091 } 2092 QCOMPARE(model->rowCount(), 1); 2093 } 2094 2095 void mailsWithDates() 2096 { 2097 { 2098 Mail mail("sink.dummy.instance1"); 2099 mail.setExtractedDate(QDateTime::fromString("2018-05-23T13:49:41Z", Qt::ISODate)); 2100 mail.setExtractedMessageId("message1"); 2101 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 2102 } 2103 { 2104 Mail mail("sink.dummy.instance1"); 2105 mail.setExtractedDate(QDateTime::fromString("2018-05-23T13:50:00Z", Qt::ISODate)); 2106 mail.setExtractedMessageId("message2"); 2107 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 2108 } 2109 { 2110 Mail mail("sink.dummy.instance1"); 2111 mail.setExtractedDate(QDateTime::fromString("2018-05-27T13:50:00Z", Qt::ISODate)); 2112 mail.setExtractedMessageId("message3"); 2113 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 2114 } 2115 { 2116 Mail mail("sink.dummy.instance1"); 2117 mail.setExtractedMessageId("message4"); 2118 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 2119 } 2120 { 2121 Mail mail("sink.dummy.instance1"); 2122 mail.setExtractedDate(QDateTime::fromString("2078-05-23T13:49:41Z", Qt::ISODate)); 2123 mail.setExtractedMessageId("message5"); 2124 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 2125 } 2126 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 2127 } 2128 2129 void testMailDate() 2130 { 2131 mailsWithDates(); 2132 2133 { 2134 Sink::Query query; 2135 query.resourceFilter("sink.dummy.instance1"); 2136 query.filter<Mail::Date>(QDateTime::fromString("2018-05-23T13:49:41Z", Qt::ISODate)); 2137 auto model = Sink::Store::loadModel<Mail>(query); 2138 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2139 QCOMPARE(model->rowCount(), 1); 2140 } 2141 2142 { 2143 Sink::Query query; 2144 query.resourceFilter("sink.dummy.instance1"); 2145 query.filter<Mail::Date>(QDateTime::fromString("2018-05-27T13:49:41Z", Qt::ISODate)); 2146 auto model = Sink::Store::loadModel<Mail>(query); 2147 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2148 QCOMPARE(model->rowCount(), 0); 2149 } 2150 2151 { 2152 Sink::Query query; 2153 query.resourceFilter("sink.dummy.instance1"); 2154 query.filter<Mail::Date>(QDateTime::fromString("2018-05-27T13:50:00Z", Qt::ISODate)); 2155 auto model = Sink::Store::loadModel<Mail>(query); 2156 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2157 QCOMPARE(model->rowCount(), 1); 2158 } 2159 2160 } 2161 2162 void testMailRange() 2163 { 2164 mailsWithDates(); 2165 2166 { 2167 Sink::Query query; 2168 query.resourceFilter("sink.dummy.instance1"); 2169 query.filter<Mail::Date>(QueryBase::Comparator(QVariantList{QDateTime::fromString("2018-05-23T13:49:41Z", Qt::ISODate), QDateTime::fromString("2018-05-23T13:49:41Z", Qt::ISODate)}, QueryBase::Comparator::Within)); 2170 auto model = Sink::Store::loadModel<Mail>(query); 2171 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2172 QCOMPARE(model->rowCount(), 1); 2173 } 2174 2175 { 2176 Sink::Query query; 2177 query.resourceFilter("sink.dummy.instance1"); 2178 query.filter<Mail::Date>(QueryBase::Comparator(QVariantList{QDateTime::fromString("2018-05-22T13:49:41Z", Qt::ISODate), QDateTime::fromString("2018-05-25T13:49:41Z", Qt::ISODate)}, QueryBase::Comparator::Within)); 2179 auto model = Sink::Store::loadModel<Mail>(query); 2180 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2181 QCOMPARE(model->rowCount(), 2); 2182 } 2183 2184 { 2185 Sink::Query query; 2186 query.resourceFilter("sink.dummy.instance1"); 2187 query.filter<Mail::Date>(QueryBase::Comparator(QVariantList{QDateTime::fromString("2018-05-22T13:49:41Z", Qt::ISODate), QDateTime::fromString("2018-05-30T13:49:41Z", Qt::ISODate)}, QueryBase::Comparator::Within)); 2188 auto model = Sink::Store::loadModel<Mail>(query); 2189 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2190 QCOMPARE(model->rowCount(), 3); 2191 } 2192 2193 { 2194 Sink::Query query; 2195 query.resourceFilter("sink.dummy.instance1"); 2196 query.filter<Mail::Date>(QueryBase::Comparator(QVariantList{QDateTime::fromString("2018-05-22T13:49:41Z", Qt::ISODate), QDateTime::fromString("2080-05-30T13:49:41Z", Qt::ISODate)}, QueryBase::Comparator::Within)); 2197 auto model = Sink::Store::loadModel<Mail>(query); 2198 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2199 //This query also finds the mail without date, because we assign a default date of current utc 2200 QCOMPARE(model->rowCount(), 5); 2201 } 2202 } 2203 2204 void testOverlap() 2205 { 2206 auto createEvent = [] (const QString &start, const QString &end) { 2207 auto icalEvent = KCalendarCore::Event::Ptr::create(); 2208 icalEvent->setSummary("test"); 2209 icalEvent->setDtStart(QDateTime::fromString(start, Qt::ISODate)); 2210 icalEvent->setDtEnd(QDateTime::fromString(end, Qt::ISODate)); 2211 2212 Event event("sink.dummy.instance1"); 2213 event.setIcal(KCalendarCore::ICalFormat().toICalString(icalEvent).toUtf8()); 2214 VERIFYEXEC(Sink::Store::create(event)); 2215 }; 2216 2217 createEvent("2018-05-23T12:00:00Z", "2018-05-23T13:00:00Z"); 2218 createEvent("2018-05-23T13:00:00Z", "2018-05-23T14:00:00Z"); 2219 createEvent("2018-05-23T14:00:00Z", "2018-05-23T15:00:00Z"); 2220 createEvent("2018-05-24T12:00:00Z", "2018-05-24T14:00:00Z"); 2221 //Long event that spans multiple buckets 2222 createEvent("2018-05-30T22:00:00", "2019-04-25T03:00:00"); 2223 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 2224 2225 auto findInRange = [] (const QString &start, const QString &end) { 2226 Sink::Query query; 2227 query.resourceFilter("sink.dummy.instance1"); 2228 query.filter<Event::StartTime, Event::EndTime>(QueryBase::Comparator( 2229 QVariantList{ QDateTime::fromString(start, Qt::ISODate), 2230 QDateTime::fromString(end, Qt::ISODate) }, 2231 QueryBase::Comparator::Overlap)); 2232 return Sink::Store::read<Event>(query); 2233 }; 2234 2235 //Find all 2236 QCOMPARE(findInRange("2018-05-22T12:00:00Z", "2018-05-30T13:00:00Z").size(), 4); 2237 //Find none on day without events 2238 QCOMPARE(findInRange("2018-05-22T12:00:00Z", "2018-05-22T13:00:00Z").size(), 0); 2239 //Find none on day with events 2240 QCOMPARE(findInRange("2018-05-24T10:00:00Z", "2018-05-24T11:00:00Z").size(), 0); 2241 //Find on same day 2242 QCOMPARE(findInRange("2018-05-23T12:30:00Z", "2018-05-23T12:31:00Z").size(), 1); 2243 //Find on different days 2244 QCOMPARE(findInRange("2018-05-22T12:30:00Z", "2018-05-23T12:00:00Z").size(), 1); 2245 QCOMPARE(findInRange("2018-05-23T14:30:00Z", "2018-05-23T16:00:00Z").size(), 1); 2246 2247 //Find long range event 2248 QCOMPARE(findInRange("2018-07-23T14:30:00Z", "2018-10-23T16:00:00Z").size(), 1); 2249 } 2250 2251 void testOverlapLive() 2252 { 2253 auto createEvent = [] (const QString &start, const QString &end) { 2254 auto icalEvent = KCalendarCore::Event::Ptr::create(); 2255 icalEvent->setSummary("test"); 2256 icalEvent->setDtStart(QDateTime::fromString(start, Qt::ISODate)); 2257 icalEvent->setDtEnd(QDateTime::fromString(end, Qt::ISODate)); 2258 2259 Event event = Event::createEntity<Event>("sink.dummy.instance1"); 2260 event.setIcal(KCalendarCore::ICalFormat().toICalString(icalEvent).toUtf8()); 2261 VERIFYEXEC_RET(Sink::Store::create(event), {}); 2262 return event; 2263 }; 2264 2265 createEvent("2018-05-23T12:00:00Z", "2018-05-23T13:00:00Z"); 2266 createEvent("2018-05-23T13:00:00Z", "2018-05-23T14:00:00Z"); 2267 createEvent("2018-05-23T14:00:00Z", "2018-05-23T15:00:00Z"); 2268 createEvent("2018-05-24T12:00:00Z", "2018-05-24T14:00:00Z"); 2269 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 2270 2271 { 2272 Sink::Query query; 2273 query.resourceFilter("sink.dummy.instance1"); 2274 query.setFlags(Query::LiveQuery); 2275 query.filter<Event::StartTime, Event::EndTime>(QueryBase::Comparator( 2276 QVariantList{ QDateTime::fromString("2018-05-22T12:00:00Z", Qt::ISODate), 2277 QDateTime::fromString("2018-05-30T13:00:00Z", Qt::ISODate) }, 2278 QueryBase::Comparator::Overlap)); 2279 auto model = Sink::Store::loadModel<Event>(query); 2280 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2281 QCOMPARE(model->rowCount(), 4); 2282 2283 auto event1 = createEvent("2018-05-23T12:00:00Z", "2018-05-23T13:00:00Z"); 2284 auto event2 = createEvent("2018-05-31T12:00:00Z", "2018-05-31T13:00:00Z"); 2285 2286 QTRY_COMPARE(model->rowCount(), 5); 2287 2288 VERIFYEXEC(Sink::Store::remove(event1)); 2289 VERIFYEXEC(Sink::Store::remove(event2)); 2290 2291 QTRY_COMPARE(model->rowCount(), 4); 2292 } 2293 } 2294 2295 void testRecurringEvents() 2296 { 2297 auto icalEvent = KCalendarCore::Event::Ptr::create(); 2298 icalEvent->setSummary("test"); 2299 icalEvent->setDtStart(QDateTime::fromString("2018-05-10T13:00:00Z", Qt::ISODate)); 2300 icalEvent->setDtEnd(QDateTime::fromString("2018-05-10T14:00:00Z", Qt::ISODate)); 2301 icalEvent->recurrence()->setWeekly(3); 2302 2303 Event event = Event::createEntity<Event>("sink.dummy.instance1"); 2304 event.setIcal(KCalendarCore::ICalFormat().toICalString(icalEvent).toUtf8()); 2305 VERIFYEXEC(Sink::Store::create(event)); 2306 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 2307 2308 Sink::Query query; 2309 query.resourceFilter("sink.dummy.instance1"); 2310 query.setFlags(Query::LiveQuery); 2311 query.filter<Event::StartTime, Event::EndTime>(QueryBase::Comparator( 2312 QVariantList{ QDateTime::fromString("2018-05-15T12:00:00Z", Qt::ISODate), 2313 QDateTime::fromString("2018-05-30T13:00:00Z", Qt::ISODate) }, 2314 QueryBase::Comparator::Overlap)); 2315 auto model = Sink::Store::loadModel<Event>(query); 2316 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2317 QCOMPARE(model->rowCount(), 1); 2318 2319 VERIFYEXEC(Sink::Store::remove(event)); 2320 QTRY_COMPARE(model->rowCount(), 0); 2321 } 2322 2323 void testRecurringEventsWithExceptions() 2324 { 2325 { 2326 auto icalEvent = KCalendarCore::Event::Ptr::create(); 2327 icalEvent->setSummary("test"); 2328 icalEvent->setDtStart(QDateTime::fromString("2018-05-10T13:00:00Z", Qt::ISODate)); 2329 icalEvent->setDtEnd(QDateTime::fromString("2018-05-10T14:00:00Z", Qt::ISODate)); 2330 icalEvent->recurrence()->setWeekly(3); 2331 2332 Event event = Event::createEntity<Event>("sink.dummy.instance1"); 2333 event.setIcal(KCalendarCore::ICalFormat().toICalString(icalEvent).toUtf8()); 2334 VERIFYEXEC(Sink::Store::create(event)); 2335 } 2336 2337 2338 //Exception 2339 { 2340 auto icalEvent = KCalendarCore::Event::Ptr::create(); 2341 icalEvent->setSummary("test"); 2342 icalEvent->setRecurrenceId(QDateTime::fromString("2018-05-17T13:00:00Z", Qt::ISODate)); 2343 icalEvent->setDtStart(QDateTime::fromString("2018-07-10T13:00:00Z", Qt::ISODate)); 2344 icalEvent->setDtEnd(QDateTime::fromString("2018-07-10T14:00:00Z", Qt::ISODate)); 2345 2346 Event event = Event::createEntity<Event>("sink.dummy.instance1"); 2347 event.setIcal(KCalendarCore::ICalFormat().toICalString(icalEvent).toUtf8()); 2348 VERIFYEXEC(Sink::Store::create(event)); 2349 } 2350 2351 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); 2352 2353 { 2354 Sink::Query query; 2355 query.resourceFilter("sink.dummy.instance1"); 2356 query.filter<Event::StartTime, Event::EndTime>(QueryBase::Comparator( 2357 QVariantList{ QDateTime::fromString("2018-05-15T12:00:00Z", Qt::ISODate), 2358 QDateTime::fromString("2018-05-30T13:00:00Z", Qt::ISODate) }, 2359 QueryBase::Comparator::Overlap)); 2360 auto model = Sink::Store::loadModel<Event>(query); 2361 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2362 QCOMPARE(model->rowCount(), 2); 2363 } 2364 { 2365 Sink::Query query; 2366 query.resourceFilter("sink.dummy.instance1"); 2367 query.filter<Event::StartTime, Event::EndTime>(QueryBase::Comparator( 2368 QVariantList{ QDateTime::fromString("2018-07-15T12:00:00Z", Qt::ISODate), 2369 QDateTime::fromString("2018-07-30T13:00:00Z", Qt::ISODate) }, 2370 QueryBase::Comparator::Overlap)); 2371 auto model = Sink::Store::loadModel<Event>(query); 2372 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2373 QCOMPARE(model->rowCount(), 1); 2374 } 2375 } 2376 2377 2378 void testQueryUpdate() 2379 { 2380 // Setup 2381 { 2382 Mail mail("sink.dummy.instance1"); 2383 mail.setExtractedMessageId("test1"); 2384 mail.setFolder("folder1"); 2385 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 2386 } 2387 { 2388 Mail mail("sink.dummy.instance1"); 2389 mail.setExtractedMessageId("test2"); 2390 mail.setFolder("folder2"); 2391 VERIFYEXEC(Sink::Store::create<Mail>(mail)); 2392 } 2393 2394 // Test 2395 Sink::Query query; 2396 query.resourceFilter("sink.dummy.instance1"); 2397 query.setFlags(Query::LiveQuery); 2398 query.filter<Mail::Folder>("folder1"); 2399 2400 auto model = Sink::Store::loadModel<Mail>(query); 2401 QTRY_COMPARE(model->rowCount(), 1); 2402 2403 { 2404 Sink::Query newQuery; 2405 newQuery.resourceFilter("sink.dummy.instance1"); 2406 2407 Sink::Store::updateModel<Mail>(newQuery, model); 2408 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2409 QCOMPARE(model->rowCount(), 2); 2410 } 2411 { 2412 2413 Sink::Query newQuery; 2414 newQuery.resourceFilter("sink.dummy.instance1"); 2415 newQuery.filter<Mail::Folder>("folder2"); 2416 2417 Sink::Store::updateModel<Mail>(newQuery, model); 2418 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2419 QCOMPARE(model->rowCount(), 1); 2420 QCOMPARE(model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>()->getMessageId(), "test2"); 2421 } 2422 { 2423 2424 Sink::Query newQuery; 2425 newQuery.resourceFilter("sink.dummy.instance1"); 2426 newQuery.filter<Mail::Folder>("folder1"); 2427 2428 Sink::Store::updateModel<Mail>(newQuery, model); 2429 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2430 QCOMPARE(model->rowCount(), 1); 2431 QCOMPARE(model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>()->getMessageId(), "test1"); 2432 } 2433 //Quickly run two queries without waiting for the first to complete. 2434 { 2435 { 2436 2437 Sink::Query newQuery; 2438 newQuery.resourceFilter("sink.dummy.instance1"); 2439 newQuery.filter<Mail::Folder>("folder2"); 2440 2441 Sink::Store::updateModel<Mail>(newQuery, model); 2442 } 2443 2444 Sink::Query newQuery; 2445 newQuery.resourceFilter("sink.dummy.instance1"); 2446 newQuery.filter<Mail::Folder>("folder1"); 2447 2448 Sink::Store::updateModel<Mail>(newQuery, model); 2449 QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); 2450 QCOMPARE(model->rowCount(), 1); 2451 QCOMPARE(model->data(model->index(0, 0, QModelIndex{}), Sink::Store::DomainObjectRole).value<Mail::Ptr>()->getMessageId(), "test1"); 2452 } 2453 } 2454 2455 }; 2456 2457 QTEST_MAIN(QueryTest) 2458 #include "querytest.moc"