File indexing completed on 2025-01-05 04:59:56
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org> 0003 SPDX-FileCopyrightText: 2018 David Faure <faure@kde.org> 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 0008 #include <testlib/qtest_zanshin.h> 0009 0010 #include "domain/livequery.h" 0011 0012 #include "utils/jobhandler.h" 0013 0014 #include "testlib/fakejob.h" 0015 0016 using namespace Domain; 0017 0018 typedef QSharedPointer<QObject> QObjectPtr; 0019 static const char objectIdPropName[] = "objectId"; 0020 0021 class LiveRelationshipQueryTest : public QObject 0022 { 0023 Q_OBJECT 0024 private: 0025 0026 QObject *createObject(int id, const QString &name) 0027 { 0028 QObject *obj = new QObject(this); 0029 obj->setObjectName(name); 0030 obj->setProperty(objectIdPropName, id); 0031 return obj; 0032 } 0033 0034 static bool compareObjectIds(QObject *obj1, QObject *obj2) 0035 { 0036 return obj1->property(objectIdPropName).toInt() == obj2->property(objectIdPropName).toInt(); 0037 } 0038 0039 static bool isProject(QObject *obj) 0040 { 0041 return obj->objectName().startsWith(QLatin1StringView("Project")); 0042 } 0043 0044 static QPair<int, QString> convertToPair(QObject *object) 0045 { 0046 return qMakePair(object->property(objectIdPropName).toInt(), object->objectName()); 0047 } 0048 0049 static bool representsPair(QObject *object, const QPair<int, QString> &output) { 0050 return object->property(objectIdPropName).toInt() == output.first; 0051 }; 0052 0053 private slots: 0054 void shouldHaveInitialFetchFunctionAndPredicate() 0055 { 0056 // GIVEN 0057 Domain::LiveRelationshipQuery<QObject*, QPair<int, QString>> query; 0058 query.setFetchFunction([this] (const Domain::LiveQueryInput<QObject*>::AddFunction &add) { 0059 Utils::JobHandler::install(new FakeJob, [this, add] { 0060 add(createObject(0, QStringLiteral("ProjectA"))); 0061 add(createObject(1, QStringLiteral("ItemA"))); 0062 add(createObject(2, QStringLiteral("ParentA"))); 0063 add(createObject(3, QStringLiteral("ProjectB"))); 0064 add(createObject(4, QStringLiteral("ItemB"))); 0065 add(createObject(5, QStringLiteral("ParentB"))); 0066 add(createObject(6, QStringLiteral("ProjectC"))); 0067 add(createObject(7, QStringLiteral("ItemC"))); 0068 add(createObject(8, QStringLiteral("ParentC"))); 0069 }); 0070 }); 0071 query.setConvertFunction(convertToPair); 0072 query.setPredicateFunction(isProject); 0073 query.setCompareFunction(compareObjectIds); 0074 0075 // WHEN 0076 Domain::QueryResult<QPair<int, QString>>::Ptr result = query.result(); 0077 result->data(); 0078 result = query.result(); // Should not cause any problem or wrong data 0079 QVERIFY(result->data().isEmpty()); 0080 0081 // THEN 0082 QList<QPair<int, QString>> expected; 0083 expected << QPair<int, QString>(0, QStringLiteral("ProjectA")) 0084 << QPair<int, QString>(3, QStringLiteral("ProjectB")) 0085 << QPair<int, QString>(6, QStringLiteral("ProjectC")); 0086 QTRY_COMPARE(result->data(), expected); 0087 } 0088 0089 void shouldFilterOutNullRawPointers() 0090 { 0091 // GIVEN 0092 auto query = Domain::LiveRelationshipQuery<QString, QObject*>(); 0093 query.setFetchFunction([] (const Domain::LiveQueryInput<QString>::AddFunction &add) { 0094 Utils::JobHandler::install(new FakeJob, [add] { 0095 add(QStringLiteral("0")); 0096 add(QStringLiteral("1")); 0097 add(QString()); 0098 add(QStringLiteral("a")); 0099 add(QStringLiteral("2")); 0100 }); 0101 }); 0102 query.setConvertFunction([this] (const QString &s) -> QObject* { 0103 bool ok = false; 0104 const int id = s.toInt(&ok); 0105 if (ok) { 0106 auto object = new QObject(this); 0107 object->setProperty("id", id); 0108 return object; 0109 } else { 0110 return nullptr; 0111 } 0112 }); 0113 query.setPredicateFunction([] (const QString &s) { 0114 return !s.isEmpty(); 0115 }); 0116 0117 // WHEN 0118 auto result = query.result(); 0119 result->data(); 0120 result = query.result(); // Should not cause any problem or wrong data 0121 QVERIFY(result->data().isEmpty()); 0122 0123 // THEN 0124 QTRY_COMPARE(result->data().size(), 3); 0125 QCOMPARE(result->data().at(0)->property("id").toInt(), 0); 0126 QCOMPARE(result->data().at(1)->property("id").toInt(), 1); 0127 QCOMPARE(result->data().at(2)->property("id").toInt(), 2); 0128 } 0129 0130 void shouldFilterOutNullSharedPointers() 0131 { 0132 // GIVEN 0133 auto query = Domain::LiveRelationshipQuery<QString, QObjectPtr>(); 0134 query.setFetchFunction([] (const Domain::LiveQueryInput<QString>::AddFunction &add) { 0135 Utils::JobHandler::install(new FakeJob, [add] { 0136 add(QStringLiteral("0")); 0137 add(QStringLiteral("1")); 0138 add(QString()); 0139 add(QStringLiteral("a")); 0140 add(QStringLiteral("2")); 0141 }); 0142 }); 0143 query.setConvertFunction([] (const QString &s) { 0144 bool ok = false; 0145 const int id = s.toInt(&ok); 0146 if (ok) { 0147 auto object = QObjectPtr::create(); 0148 object->setProperty("id", id); 0149 return object; 0150 } else { 0151 return QObjectPtr(); 0152 } 0153 }); 0154 query.setPredicateFunction([] (const QString &s) { 0155 return !s.isEmpty(); 0156 }); 0157 0158 // WHEN 0159 auto result = query.result(); 0160 result->data(); 0161 result = query.result(); // Should not cause any problem or wrong data 0162 QVERIFY(result->data().isEmpty()); 0163 0164 // THEN 0165 QTRY_COMPARE(result->data().size(), 3); 0166 QCOMPARE(result->data().at(0)->property("id").toInt(), 0); 0167 QCOMPARE(result->data().at(1)->property("id").toInt(), 1); 0168 QCOMPARE(result->data().at(2)->property("id").toInt(), 2); 0169 } 0170 0171 void shouldDealWithSeveralFetchesProperly() 0172 { 0173 // GIVEN 0174 Domain::LiveRelationshipQuery<QObject*, QPair<int, QString>> query; 0175 query.setFetchFunction([this] (const Domain::LiveRelationshipQuery<QObject*, QString>::AddFunction &add) { 0176 Utils::JobHandler::install(new FakeJob, [this, add] { 0177 add(createObject(0, QStringLiteral("ProjectA"))); 0178 add(createObject(1, QStringLiteral("ItemA"))); 0179 add(createObject(2, QStringLiteral("ParentA"))); 0180 add(createObject(3, QStringLiteral("ProjectB"))); 0181 add(createObject(4, QStringLiteral("ItemB"))); 0182 add(createObject(5, QStringLiteral("ParentB"))); 0183 add(createObject(6, QStringLiteral("ProjectC"))); 0184 add(createObject(7, QStringLiteral("ItemC"))); 0185 add(createObject(8, QStringLiteral("ParentC"))); 0186 }); 0187 }); 0188 query.setConvertFunction(convertToPair); 0189 query.setPredicateFunction(isProject); 0190 0191 for (int i = 0; i < 2; i++) { 0192 // WHEN * 2 0193 Domain::QueryResult<QPair<int, QString>>::Ptr result = query.result(); 0194 0195 // THEN * 2 0196 QVERIFY(result->data().isEmpty()); 0197 QList<QPair<int, QString>> expected; 0198 expected << QPair<int, QString>(0, QStringLiteral("ProjectA")) 0199 << QPair<int, QString>(3, QStringLiteral("ProjectB")) 0200 << QPair<int, QString>(6, QStringLiteral("ProjectC")); 0201 QTRY_COMPARE(result->data(), expected); 0202 } 0203 } 0204 0205 void shouldClearProviderWhenDeleted() 0206 { 0207 // GIVEN 0208 auto query = new Domain::LiveRelationshipQuery<QObject*, QPair<int, QString>>; 0209 query->setFetchFunction([this] (const Domain::LiveRelationshipQuery<QObject*, QString>::AddFunction &add) { 0210 Utils::JobHandler::install(new FakeJob, [this, add] { 0211 add(createObject(0, QStringLiteral("ProjectA"))); 0212 add(createObject(1, QStringLiteral("ItemA"))); 0213 add(createObject(2, QStringLiteral("ParentA"))); 0214 }); 0215 }); 0216 query->setConvertFunction(convertToPair); 0217 query->setPredicateFunction(isProject); 0218 query->setCompareFunction(compareObjectIds); 0219 0220 Domain::QueryResult<QPair<int, QString>>::Ptr result = query->result(); 0221 QTRY_COMPARE(result->data().count(), 1); 0222 0223 // WHEN 0224 delete query; 0225 0226 // THEN 0227 QVERIFY(result->data().isEmpty()); 0228 } 0229 0230 void shouldReactToAdds() 0231 { 0232 // GIVEN 0233 Domain::LiveRelationshipQuery<QObject*, QPair<int, QString>> query; 0234 query.setFetchFunction([this] (const Domain::LiveRelationshipQuery<QObject*, QString>::AddFunction &add) { 0235 Utils::JobHandler::install(new FakeJob, [this, add] { 0236 add(createObject(0, QStringLiteral("ProjectA"))); 0237 add(createObject(1, QStringLiteral("ItemA"))); 0238 add(createObject(2, QStringLiteral("ParentA"))); 0239 }); 0240 }); 0241 query.setConvertFunction(convertToPair); 0242 query.setPredicateFunction(isProject); 0243 query.setCompareFunction(compareObjectIds); 0244 0245 Domain::QueryResult<QPair<int, QString>>::Ptr result = query.result(); 0246 QList<QPair<int, QString>> expected{ qMakePair(0, QString::fromLatin1("ProjectA")) }; 0247 QTRY_COMPARE(result->data(), expected); 0248 0249 // WHEN 0250 query.onAdded(createObject(3, QStringLiteral("ProjectB"))); 0251 query.onAdded(createObject(4, QStringLiteral("ItemB"))); 0252 query.onAdded(createObject(5, QStringLiteral("ParentB"))); 0253 0254 // THEN 0255 expected << QPair<int, QString>(3, QStringLiteral("ProjectB")); 0256 QCOMPARE(result->data(), expected); 0257 } 0258 0259 void shouldReactToRemoves() 0260 { 0261 // GIVEN 0262 Domain::LiveRelationshipQuery<QObject*, QPair<int, QString>> query; 0263 query.setFetchFunction([this] (const Domain::LiveRelationshipQuery<QObject*, QString>::AddFunction &add) { 0264 Utils::JobHandler::install(new FakeJob, [this, add] { 0265 add(createObject(0, QStringLiteral("ProjectA"))); 0266 add(createObject(1, QStringLiteral("ItemA"))); 0267 add(createObject(2, QStringLiteral("ParentA"))); 0268 }); 0269 }); 0270 query.setConvertFunction(convertToPair); 0271 query.setPredicateFunction(isProject); 0272 query.setCompareFunction(compareObjectIds); 0273 query.setRepresentsFunction(representsPair); 0274 0275 Domain::QueryResult<QPair<int, QString>>::Ptr result = query.result(); 0276 QList<QPair<int, QString>> expected{ qMakePair(0, QString::fromLatin1("ProjectA")) }; 0277 QTRY_COMPARE(result->data(), expected); 0278 0279 // WHEN 0280 query.setFetchFunction([] (const Domain::LiveRelationshipQuery<QObject*, QString>::AddFunction &) { 0281 Utils::JobHandler::install(new FakeJob, [] {}); 0282 }); 0283 0284 // unrelated remove -> ignore 0285 query.onRemoved(createObject(3, QStringLiteral("ItemB"))); 0286 QTRY_COMPARE(result->data(), expected); 0287 0288 // remove item -> reset happens 0289 query.onRemoved(createObject(1, QStringLiteral("ItemA"))); 0290 0291 // THEN 0292 expected.clear(); 0293 QTRY_COMPARE(result->data(), expected); 0294 } 0295 0296 void shouldReactToChanges() 0297 { 0298 // GIVEN 0299 Domain::LiveRelationshipQuery<QObject*, QPair<int, QString>> query; 0300 query.setFetchFunction([this] (const Domain::LiveRelationshipQuery<QObject*, QString>::AddFunction &add) { 0301 Utils::JobHandler::install(new FakeJob, [this, add] { 0302 add(createObject(0, QStringLiteral("ProjectA"))); 0303 add(createObject(1, QStringLiteral("ItemA"))); 0304 add(createObject(2, QStringLiteral("ParentA"))); 0305 add(createObject(3, QStringLiteral("ProjectB"))); 0306 add(createObject(4, QStringLiteral("ItemB"))); 0307 add(createObject(5, QStringLiteral("ParentB"))); 0308 add(createObject(6, QStringLiteral("ProjectC"))); 0309 add(createObject(7, QStringLiteral("ItemC"))); 0310 add(createObject(8, QStringLiteral("ParentC"))); 0311 }); 0312 }); 0313 query.setConvertFunction(convertToPair); 0314 query.setPredicateFunction(isProject); 0315 query.setCompareFunction(compareObjectIds); 0316 query.setRepresentsFunction(representsPair); 0317 0318 Domain::QueryResult<QPair<int, QString>>::Ptr result = query.result(); 0319 QList<QPair<int, QString>> expected{ qMakePair(0, QString::fromLatin1("ProjectA")), 0320 qMakePair(3, QString::fromLatin1("ProjectB")), 0321 qMakePair(6, QString::fromLatin1("ProjectC")) }; 0322 QTRY_COMPARE(result->data(), expected); 0323 0324 // WHEN 0325 query.setFetchFunction([this] (const Domain::LiveRelationshipQuery<QObject*, QString>::AddFunction &add) { 0326 Utils::JobHandler::install(new FakeJob, [this, add] { 0327 add(createObject(0, QStringLiteral("ProjectA"))); 0328 add(createObject(1, QStringLiteral("ItemA"))); 0329 add(createObject(2, QStringLiteral("ParentA"))); 0330 add(createObject(3, QStringLiteral("ProjectB-Renamed"))); 0331 add(createObject(4, QStringLiteral("ItemB"))); 0332 add(createObject(5, QStringLiteral("ParentB"))); 0333 add(createObject(6, QStringLiteral("ProjectC"))); 0334 add(createObject(7, QStringLiteral("ItemC"))); 0335 add(createObject(8, QStringLiteral("ParentC"))); 0336 }); 0337 }); 0338 query.onChanged(createObject(3, QStringLiteral("whatever"))); 0339 0340 // THEN 0341 expected[1] = qMakePair(3, QString::fromLatin1("ProjectB-Renamed")); 0342 QTRY_COMPARE(result->data(), expected); 0343 } 0344 0345 void shouldIgnoreUnrelatedChangesWhenEmpty() 0346 { 0347 // GIVEN 0348 Domain::LiveRelationshipQuery<QObject*, QPair<int, QString>> query; 0349 bool listingDone = false; 0350 query.setFetchFunction([&listingDone] (const Domain::LiveRelationshipQuery<QObject*, QString>::AddFunction &add) { 0351 Q_UNUSED(add); 0352 Utils::JobHandler::install(new FakeJob, [&listingDone] { 0353 listingDone = true; 0354 }); 0355 }); 0356 query.setConvertFunction(convertToPair); 0357 query.setPredicateFunction(isProject); 0358 query.setCompareFunction(compareObjectIds); 0359 query.setRepresentsFunction(representsPair); 0360 0361 Domain::QueryResult<QPair<int, QString>>::Ptr result = query.result(); 0362 QTRY_VERIFY(listingDone); 0363 listingDone = false; 0364 QVERIFY(result->data().isEmpty()); 0365 0366 // WHEN 0367 query.onChanged(createObject(1, QStringLiteral("ProjectA"))); 0368 0369 // THEN 0370 QTest::qWait(150); 0371 QVERIFY(!listingDone); 0372 QVERIFY(result->data().isEmpty()); 0373 } 0374 0375 void shouldAddWhenChangesMakeInputSuitableForQuery() 0376 { 0377 // GIVEN 0378 Domain::LiveRelationshipQuery<QObject*, QPair<int, QString>> query; 0379 bool listingDone = false; 0380 query.setFetchFunction([this, &listingDone] (const Domain::LiveRelationshipQuery<QObject*, QString>::AddFunction &add) { 0381 Utils::JobHandler::install(new FakeJob, [this, add, &listingDone] { 0382 add(createObject(1, QStringLiteral("ItemA"))); 0383 add(createObject(2, QStringLiteral("ParentA"))); 0384 listingDone = true; 0385 }); 0386 }); 0387 query.setConvertFunction(convertToPair); 0388 query.setPredicateFunction(isProject); 0389 query.setCompareFunction(compareObjectIds); 0390 query.setRepresentsFunction(representsPair); 0391 0392 Domain::QueryResult<QPair<int, QString>>::Ptr result = query.result(); 0393 QList<QPair<int, QString>> expected; 0394 QTRY_VERIFY(listingDone); 0395 QCOMPARE(result->data(), expected); 0396 0397 // WHEN 0398 query.setFetchFunction([this] (const Domain::LiveRelationshipQuery<QObject*, QString>::AddFunction &add) { 0399 Utils::JobHandler::install(new FakeJob, [this, add] { 0400 add(createObject(1, QStringLiteral("ItemA"))); 0401 add(createObject(2, QStringLiteral("ProjectA"))); // parent promoted to project 0402 }); 0403 }); 0404 query.onChanged(createObject(2, QStringLiteral("whatever"))); 0405 0406 // Then 0407 expected << qMakePair(2, QStringLiteral("ProjectA")); 0408 QTRY_COMPARE(result->data(), expected); 0409 } 0410 0411 void shouldEmptyAndFetchAgainOnReset() 0412 { 0413 // GIVEN 0414 bool afterReset = false; 0415 0416 Domain::LiveRelationshipQuery<QObject*, QPair<int, QString>> query; 0417 query.setFetchFunction([this, &afterReset] (const Domain::LiveRelationshipQuery<QObject*, QString>::AddFunction &add) { 0418 Utils::JobHandler::install(new FakeJob, [this, &afterReset, add] { 0419 add(createObject(0, QStringLiteral("ProjectA"))); 0420 add(createObject(1, QStringLiteral("ItemA"))); 0421 add(createObject(2, QStringLiteral("ParentA"))); 0422 add(createObject(3, QStringLiteral("ProjectB"))); 0423 add(createObject(4, QStringLiteral("ItemB"))); 0424 add(createObject(5, QStringLiteral("ParentB"))); 0425 0426 if (afterReset) { 0427 add(createObject(6, QStringLiteral("ProjectC"))); 0428 add(createObject(7, QStringLiteral("ItemC"))); 0429 add(createObject(8, QStringLiteral("ParentC"))); 0430 } 0431 }); 0432 }); 0433 query.setConvertFunction(convertToPair); 0434 query.setPredicateFunction([&afterReset] (QObject *object) { 0435 if (afterReset) 0436 return object->objectName().startsWith(QLatin1StringView("Item")); 0437 else 0438 return object->objectName().startsWith(QLatin1StringView("Project")); 0439 }); 0440 query.setCompareFunction(compareObjectIds); 0441 0442 Domain::QueryResult<QPair<int, QString>>::Ptr result = query.result(); 0443 int removeHandlerCallCount = 0; 0444 result->addPostRemoveHandler([&removeHandlerCallCount](const QPair<int, QString> &, int) { 0445 removeHandlerCallCount++; 0446 }); 0447 0448 QTRY_VERIFY(!result->data().isEmpty()); 0449 QCOMPARE(removeHandlerCallCount, 0); 0450 0451 // WHEN 0452 query.reset(); 0453 afterReset = true; 0454 0455 // THEN 0456 const QList<QPair<int, QString>> expected = { qMakePair(1, QStringLiteral("ItemA")), 0457 qMakePair(4, QStringLiteral("ItemB")), 0458 qMakePair(7, QStringLiteral("ItemC")) }; 0459 QTRY_COMPARE(result->data(), expected); 0460 QCOMPARE(removeHandlerCallCount, 2); 0461 } 0462 }; 0463 0464 ZANSHIN_TEST_MAIN(LiveRelationshipQueryTest) 0465 0466 #include "liverelationshipquerytest.moc"