Warning, file /frameworks/kactivities-stats/autotests/ResultSetQuickCheckTest.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2015 Ivan Cukic <ivan.cukic(at)kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "ResultSetQuickCheckTest.h" 0008 0009 #include <QCoreApplication> 0010 #include <QDBusConnection> 0011 #include <QDBusConnectionInterface> 0012 #include <QDebug> 0013 #include <QRandomGenerator> 0014 #include <QString> 0015 #include <QTemporaryDir> 0016 #include <QTest> 0017 #include <QUuid> 0018 0019 #include <boost/range/adaptor/filtered.hpp> 0020 #include <boost/range/algorithm.hpp> 0021 #include <boost/range/algorithm/sort.hpp> 0022 #include <boost/range/numeric.hpp> 0023 0024 #include <query.h> 0025 #include <resultset.h> 0026 0027 #include <iostream> 0028 0029 #include <common/database/Database.h> 0030 #include <common/database/schema/ResourcesDatabaseSchema.h> 0031 0032 #include <utils/qsqlquery_iterator.h> 0033 0034 #define NUMBER_ACTIVITIES 10 0035 #define NUMBER_AGENTS 10 0036 #define NUMBER_RESOURCES 50 0037 #define NUMBER_CACHES 200 0038 0039 namespace KAStats = KActivities::Stats; 0040 0041 static ResultSetQuickCheckTest *instance; 0042 0043 ResultSetQuickCheckTest::ResultSetQuickCheckTest(QObject *parent) 0044 : Test(parent) 0045 , activities(std::make_unique<KActivities::Consumer>()) 0046 { 0047 instance = this; 0048 } 0049 0050 namespace 0051 { 0052 QString resourceTitle(const QString &resource) 0053 { 0054 // We need to find the title 0055 ResourceInfo::Item key; 0056 key.targettedResource = resource; 0057 0058 auto &infos = instance->resourceInfos; 0059 0060 auto ri = infos.lower_bound(key); 0061 0062 return (ri != infos.cend() && ri->targettedResource == resource) ? ri->title : resource; 0063 } 0064 0065 QString toQString(const ResourceScoreCache::Item &item) 0066 { 0067 /* clang-format off */ 0068 return 0069 item.targettedResource 0070 + QLatin1Char(':') + resourceTitle(item.targettedResource) 0071 + QLatin1Char('(') + QString::number(item.cachedScore) + QLatin1Char(')'); 0072 /* clang-format on */ 0073 } 0074 0075 QString toQString(const ResourceLink::Item &item) 0076 { 0077 return item.targettedResource + QLatin1Char(':') + resourceTitle(item.targettedResource); 0078 // + '(' + QString::number(0/* item.score */) + ')' 0079 } 0080 0081 QString toQString(const KAStats::ResultSet::Result &item) 0082 { 0083 /* clang-format off */ 0084 return 0085 item.resource() 0086 + QLatin1Char(':') + item.title() 0087 + QLatin1Char('(') + QString::number(item.score()) + QLatin1Char(')'); 0088 /* clang-format on */ 0089 } 0090 0091 bool operator==(const ResourceScoreCache::Item &left, const KAStats::ResultSet::Result &right) 0092 { 0093 /* clang-format off */ 0094 return left.targettedResource == right.resource() 0095 && resourceTitle(left.targettedResource) == right.title() 0096 && qFuzzyCompare(left.cachedScore, right.score()); 0097 /* clang-format on */ 0098 } 0099 0100 bool operator==(const ResourceLink::Item &left, const KAStats::ResultSet::Result &right) 0101 { 0102 return left.targettedResource == right.resource() // 0103 && resourceTitle(left.targettedResource) == right.title(); 0104 // && qFuzzyCompare(left.cachedScore, right.score); 0105 } 0106 0107 template<typename Left> 0108 void assert_range_equal(const Left &left, const KAStats::ResultSet &right, const char *file, int line) 0109 { 0110 auto leftIt = left.cbegin(); 0111 auto rightIt = right.cbegin(); 0112 auto leftEnd = left.cend(); 0113 auto rightEnd = right.cend(); 0114 0115 bool equal = true; 0116 0117 QString leftLine; 0118 QString rightLine; 0119 0120 for (; leftIt != leftEnd && rightIt != rightEnd; ++leftIt, ++rightIt) { 0121 auto leftString = toQString(*leftIt); 0122 auto rightString = toQString(*rightIt); 0123 0124 if (*leftIt == *rightIt) { 0125 rightString.fill(QLatin1Char('.')); 0126 0127 } else { 0128 equal = false; 0129 } 0130 0131 int longer = qMax(leftString.length(), rightString.length()); 0132 leftString = leftString.leftJustified(longer); 0133 rightString = rightString.leftJustified(longer, QLatin1Char('.')); 0134 0135 leftLine += QStringLiteral(" ") + leftString; 0136 rightLine += QStringLiteral(" ") + rightString; 0137 } 0138 0139 // So far, we are equal, but do we have the same number 0140 // of elements - did we reach the end of both ranges? 0141 if (leftIt != leftEnd) { 0142 for (; leftIt != leftEnd; ++leftIt) { 0143 auto item = toQString(*leftIt); 0144 leftLine += QStringLiteral(" ") + item; 0145 item.fill(QLatin1Char('X')); 0146 rightLine += QStringLiteral(" ") + item; 0147 } 0148 equal = false; 0149 0150 } else if (rightIt != rightEnd) { 0151 for (; rightIt != rightEnd; ++rightIt) { 0152 auto item = toQString(*leftIt); 0153 rightLine += QStringLiteral(" ") + item; 0154 item.fill(QLatin1Char('X')); 0155 leftLine += QStringLiteral(" ") + item; 0156 } 0157 equal = false; 0158 } 0159 0160 if (!equal) { 0161 qDebug() << "Ranges differ:\n" 0162 << "MEM: " << leftLine << '\n' 0163 << "SQL: " << rightLine; 0164 QTest::qFail("Results do not match", file, line); 0165 } 0166 } 0167 0168 #define ASSERT_RANGE_EQUAL(L, R) assert_range_equal(L, R, __FILE__, __LINE__) 0169 } 0170 0171 //_ Data init 0172 void ResultSetQuickCheckTest::initTestCase() 0173 { 0174 QString databaseFile; 0175 0176 int dbArgIndex = QCoreApplication::arguments().indexOf(QStringLiteral("--ResultSetQuickCheckDatabase")); 0177 if (dbArgIndex > 0) { 0178 databaseFile = QCoreApplication::arguments()[dbArgIndex + 1]; 0179 0180 qDebug() << "Using an existing database: " << databaseFile; 0181 Common::ResourcesDatabaseSchema::overridePath(databaseFile); 0182 0183 pullFromDatabase(); 0184 0185 } else { 0186 QTemporaryDir dir(QDir::tempPath() + QStringLiteral("/KActivitiesStatsTest_ResultSetQuickCheckTest_XXXXXX")); 0187 dir.setAutoRemove(false); 0188 0189 if (!dir.isValid()) { 0190 qFatal("Can not create a temporary directory"); 0191 } 0192 0193 databaseFile = dir.path() + QStringLiteral("/database"); 0194 0195 qDebug() << "Creating database in " << databaseFile; 0196 Common::ResourcesDatabaseSchema::overridePath(databaseFile); 0197 0198 while (activities->serviceStatus() == KActivities::Consumer::Unknown) { 0199 QCoreApplication::processEvents(); 0200 } 0201 0202 generateActivitiesList(); 0203 generateAgentsList(); 0204 generateTypesList(); 0205 generateResourcesList(); 0206 0207 generateResourceInfos(); 0208 generateResourceScoreCaches(); 0209 generateResourceLinks(); 0210 0211 pushToDatabase(); 0212 } 0213 0214 if (QCoreApplication::arguments().contains(QLatin1String("--show-data"))) { 0215 QString rscs; 0216 for (const auto &rsc : resourceScoreCaches) { 0217 /* clang-format off */ 0218 rscs += QLatin1Char('(') + rsc.targettedResource 0219 + QLatin1Char(',') + rsc.usedActivity 0220 + QLatin1Char(',') + rsc.initiatingAgent 0221 + QLatin1Char(',') + QString::number(rsc.cachedScore) + QLatin1Char(')'); 0222 /* clang-format on */ 0223 } 0224 0225 QString ris; 0226 for (const auto &ri : resourceInfos) { 0227 /* clang-format off */ 0228 ris += QLatin1Char('(') + ri.targettedResource 0229 + QLatin1Char(',') + ri.title 0230 + QLatin1Char(',') + ri.mimetype + QLatin1Char(')'); 0231 /* clang-format on*/ 0232 } 0233 0234 QString rls; 0235 for (const auto &rl : resourceLinks) { 0236 /* clang-format off */ 0237 rls += QLatin1Char('(') + rl.targettedResource 0238 + QLatin1Char(',') + rl.usedActivity 0239 + QLatin1Char(',') + rl.initiatingAgent + QLatin1Char(')'); 0240 /* clang-format on */ 0241 } 0242 0243 /* clang-format off */ 0244 qDebug() << "\nUsed data: -----------------------------" 0245 << "\nActivities: " << activitiesList 0246 << "\nAgents: " << agentsList 0247 << "\nTypes: " << typesList 0248 << "\nResources: " << resourcesList 0249 << "\n----------------------------------------"; 0250 qDebug() << "\n RSCs: " << rscs; 0251 qDebug() << "\n RIs: " << ris; 0252 qDebug() << "\n RLs: " << rls 0253 << "\n----------------------------------------"; 0254 /* clang-format on */ 0255 } 0256 } 0257 0258 void ResultSetQuickCheckTest::generateActivitiesList() 0259 { 0260 activitiesList = activities->activities(); 0261 0262 while (activitiesList.size() < NUMBER_ACTIVITIES) { 0263 activitiesList << QUuid::createUuid().toString().mid(1, 36); 0264 } 0265 } 0266 0267 void ResultSetQuickCheckTest::generateAgentsList() 0268 { 0269 for (int i = 0; i < NUMBER_AGENTS; ++i) { 0270 agentsList << QStringLiteral("Agent_") + QString::number(i); 0271 } 0272 } 0273 0274 void ResultSetQuickCheckTest::generateTypesList() 0275 { 0276 /* clang-format off */ 0277 typesList 0278 << QStringLiteral("application/postscript") 0279 << QStringLiteral("application/pdf") 0280 << QStringLiteral("image/x-psd") 0281 << QStringLiteral("image/x-sgi") 0282 << QStringLiteral("image/x-tga") 0283 << QStringLiteral("image/x-xbitmap") 0284 << QStringLiteral("image/x-xwindowdump") 0285 << QStringLiteral("image/x-xcf") 0286 << QStringLiteral("image/x-compressed-xcf") 0287 << QStringLiteral("image/tiff") 0288 << QStringLiteral("image/jpeg") 0289 << QStringLiteral("image/x-psp") 0290 << QStringLiteral("image/png") 0291 << QStringLiteral("image/x-icon") 0292 << QStringLiteral("image/x-xpixmap") 0293 << QStringLiteral("image/svg+xml") 0294 << QStringLiteral("application/pdf") 0295 << QStringLiteral("image/x-wmf") 0296 << QStringLiteral("image/jp2") 0297 << QStringLiteral("image/jpeg2000") 0298 << QStringLiteral("image/jpx") 0299 << QStringLiteral("image/x-xcursor"); 0300 /* clang-format on */ 0301 } 0302 0303 void ResultSetQuickCheckTest::generateResourcesList() 0304 { 0305 for (int i = 0; i < NUMBER_RESOURCES; ++i) { 0306 resourcesList << (QStringLiteral("/r") // 0307 + (i < 10 ? QStringLiteral("0") : QString()) + QString::number(i)); 0308 } 0309 } 0310 0311 void ResultSetQuickCheckTest::generateResourceInfos() 0312 { 0313 auto *generator = QRandomGenerator::global(); 0314 for (const QString &resource : std::as_const(resourcesList)) { 0315 // We want every n-th or so to be without the title 0316 if (generator->bounded(3)) { 0317 continue; 0318 } 0319 0320 ResourceInfo::Item ri; 0321 ri.targettedResource = resource; 0322 ri.title = QStringLiteral("Title_") + QString::number(generator->bounded(100)); 0323 ri.mimetype = randItem(typesList); 0324 0325 resourceInfos.insert(ri); 0326 } 0327 } 0328 0329 void ResultSetQuickCheckTest::generateResourceScoreCaches() 0330 { 0331 auto *generator = QRandomGenerator::global(); 0332 for (int i = 0; i < NUMBER_CACHES; ++i) { 0333 ResourceScoreCache::Item rsc; 0334 0335 rsc.usedActivity = randItem(activitiesList); 0336 rsc.initiatingAgent = randItem(agentsList); 0337 rsc.targettedResource = randItem(resourcesList); 0338 0339 rsc.cachedScore = generator->bounded(1000); 0340 rsc.firstUpdate = generator->generate(); 0341 rsc.lastUpdate = generator->generate(); 0342 0343 resourceScoreCaches.insert(rsc); 0344 } 0345 } 0346 0347 void ResultSetQuickCheckTest::generateResourceLinks() 0348 { 0349 auto *generator = QRandomGenerator::global(); 0350 for (const QString &resource : std::as_const(resourcesList)) { 0351 // We don't want all the resources to be linked 0352 // to something 0353 if (generator->bounded(2)) { 0354 continue; 0355 } 0356 0357 ResourceLink::Item rl; 0358 0359 rl.targettedResource = resource; 0360 rl.usedActivity = randItem(activitiesList); 0361 rl.initiatingAgent = randItem(agentsList); 0362 0363 resourceLinks.insert(rl); 0364 } 0365 } 0366 0367 void ResultSetQuickCheckTest::pushToDatabase() 0368 { 0369 auto database = Common::Database::instance(Common::Database::ResourcesDatabase, Common::Database::ReadWrite); 0370 0371 Common::ResourcesDatabaseSchema::initSchema(*database); 0372 0373 // Inserting activities, so that a test can be replicated 0374 database->execQuery(QStringLiteral("CREATE TABLE Activity (activity TEXT)")); 0375 for (const auto &activity : activitiesList) { 0376 database->execQuery(QStringLiteral("INSERT INTO Activity VALUES ('%1')").arg(activity)); 0377 } 0378 0379 // Inserting agent, so that a test can be replicated 0380 database->execQuery(QStringLiteral("CREATE TABLE Agent (agent TEXT)")); 0381 for (const auto &agent : agentsList) { 0382 database->execQuery(QStringLiteral("INSERT INTO Agent VALUES ('%1')").arg(agent)); 0383 } 0384 0385 // Inserting types, so that a test can be replicated 0386 database->execQuery(QStringLiteral("CREATE TABLE Type (type TEXT)")); 0387 for (const auto &type : typesList) { 0388 database->execQuery(QStringLiteral("INSERT INTO Type VALUES ('%1')").arg(type)); 0389 } 0390 0391 // Inserting resources, so that a test can be replicated 0392 database->execQuery(QStringLiteral("CREATE TABLE Resource (resource TEXT)")); 0393 for (const auto &resource : resourcesList) { 0394 database->execQuery(QStringLiteral("INSERT INTO Resource VALUES ('%1')").arg(resource)); 0395 } 0396 0397 // Inserting resource score caches 0398 qDebug() << "Inserting" << resourceScoreCaches.size() << "items into ResourceScoreCache"; 0399 int i = 0; 0400 0401 for (const auto &rsc : std::as_const(resourceScoreCaches)) { 0402 std::cerr << '.'; 0403 0404 if (++i % 10 == 0) { 0405 std::cerr << i; 0406 } 0407 0408 /* clang-format off */ 0409 database->execQuery(QStringLiteral( 0410 "INSERT INTO ResourceScoreCache (" 0411 " usedActivity" 0412 ", initiatingAgent" 0413 ", targettedResource" 0414 ", scoreType" 0415 ", cachedScore" 0416 ", firstUpdate" 0417 ", lastUpdate" 0418 ") VALUES (" 0419 " '%1'" // usedActivity 0420 ", '%2'" // initiatingAgent 0421 ", '%3'" // targettedResource 0422 ", 0 " // scoreType 0423 ", %4 " // cachedScore 0424 ", %5 " // firstUpdate 0425 ", %6 " // lastUpdate 0426 ")" 0427 ) 0428 .arg(rsc.usedActivity) 0429 .arg(rsc.initiatingAgent) 0430 .arg(rsc.targettedResource) 0431 .arg(rsc.cachedScore) 0432 .arg(rsc.firstUpdate) 0433 .arg(rsc.lastUpdate) 0434 ); 0435 /* clang-format on */ 0436 0437 } 0438 std::cerr << std::endl; 0439 0440 // Inserting resource infos 0441 qDebug() << "Inserting" << resourceInfos.size() << "items into ResourceInfo"; 0442 i = 0; 0443 0444 for (const auto &ri : std::as_const(resourceInfos)) { 0445 std::cerr << '.'; 0446 0447 if (++i % 10 == 0) { 0448 std::cerr << i; 0449 } 0450 0451 /* clang-format off */ 0452 database->execQuery(QStringLiteral( 0453 "INSERT INTO ResourceInfo (" 0454 " targettedResource" 0455 ", title" 0456 ", mimetype" 0457 ", autoTitle" 0458 ", autoMimetype" 0459 ") VALUES (" 0460 " '%1' " // targettedResource 0461 ", '%2' " // title 0462 ", '%3' " // mimetype 0463 ", 1 " // autoTitle 0464 ", 1 " // autoMimetype 0465 ")" 0466 ) 0467 .arg(ri.targettedResource) 0468 .arg(ri.title) 0469 .arg(ri.mimetype) 0470 ); 0471 /* clang-format on */ 0472 } 0473 std::cerr << std::endl; 0474 0475 // Inserting resource links 0476 qDebug() << "Inserting" << resourceLinks.size() << "items into ResourceLink"; 0477 i = 0; 0478 0479 for (const auto &rl : std::as_const(resourceLinks)) { 0480 std::cerr << '.'; 0481 0482 if (++i % 10 == 0) { 0483 std::cerr << i; 0484 } 0485 0486 /* clang-format off */ 0487 database->execQuery(QStringLiteral( 0488 "INSERT INTO ResourceLink (" 0489 " targettedResource" 0490 ", usedActivity" 0491 ", initiatingAgent" 0492 ") VALUES (" 0493 " '%1' " // targettedResource 0494 ", '%2' " // usedActivity 0495 ", '%3' " // initiatingAgent 0496 ")" 0497 ) 0498 .arg(rl.targettedResource) 0499 .arg(rl.usedActivity) 0500 .arg(rl.initiatingAgent) 0501 ); 0502 /* clang-format on */ 0503 0504 } 0505 std::cerr << std::endl; 0506 } 0507 0508 void ResultSetQuickCheckTest::pullFromDatabase() 0509 { 0510 auto database = Common::Database::instance(Common::Database::ResourcesDatabase, Common::Database::ReadWrite); 0511 0512 auto activityQuery = database->execQuery(QStringLiteral("SELECT * FROM Activity")); 0513 for (const auto &activity : activityQuery) { 0514 activitiesList << activity[0].toString(); 0515 } 0516 0517 auto agentQuery = database->execQuery(QStringLiteral("SELECT * FROM Agent")); 0518 for (const auto &agent : agentQuery) { 0519 agentsList << agent[0].toString(); 0520 } 0521 0522 auto typeQuery = database->execQuery(QStringLiteral("SELECT * FROM Type")); 0523 for (const auto &type : typeQuery) { 0524 typesList << type[0].toString(); 0525 } 0526 0527 auto resourceQuery = database->execQuery(QStringLiteral("SELECT * FROM Resource")); 0528 for (const auto &resource : resourceQuery) { 0529 resourcesList << resource[0].toString(); 0530 } 0531 0532 auto rscQuery = database->execQuery(QStringLiteral("SELECT * FROM ResourceScoreCache")); 0533 0534 for (const auto &rsc : rscQuery) { 0535 ResourceScoreCache::Item item; 0536 item.usedActivity = rsc[QStringLiteral("usedActivity")].toString(); 0537 item.initiatingAgent = rsc[QStringLiteral("initiatingAgent")].toString(); 0538 item.targettedResource = rsc[QStringLiteral("targettedResource")].toString(); 0539 item.cachedScore = rsc[QStringLiteral("cachedScore")].toDouble(); 0540 item.firstUpdate = rsc[QStringLiteral("firstUpdate")].toInt(); 0541 item.lastUpdate = rsc[QStringLiteral("lastUpdate")].toInt(); 0542 resourceScoreCaches.insert(item); 0543 } 0544 0545 auto riQuery = database->execQuery(QStringLiteral("SELECT * FROM ResourceInfo")); 0546 0547 for (const auto &ri : riQuery) { 0548 ResourceInfo::Item item; 0549 item.targettedResource = ri[QStringLiteral("targettedResource")].toString(); 0550 item.title = ri[QStringLiteral("title")].toString(); 0551 item.mimetype = ri[QStringLiteral("mimetype")].toString(); 0552 resourceInfos.insert(item); 0553 } 0554 0555 auto rlQuery = database->execQuery(QStringLiteral("SELECT * FROM ResourceLink")); 0556 0557 for (const auto &rl : rlQuery) { 0558 ResourceLink::Item item; 0559 item.targettedResource = rl[QStringLiteral("targettedResource")].toString(); 0560 item.usedActivity = rl[QStringLiteral("usedActivity")].toString(); 0561 item.initiatingAgent = rl[QStringLiteral("initiatingAgent")].toString(); 0562 resourceLinks.insert(item); 0563 } 0564 } 0565 0566 void ResultSetQuickCheckTest::cleanupTestCase() 0567 { 0568 Q_EMIT testFinished(); 0569 } 0570 0571 QString ResultSetQuickCheckTest::randItem(const QStringList &choices) const 0572 { 0573 return choices[QRandomGenerator::global()->bounded(choices.size())]; 0574 } 0575 //^ Data init 0576 0577 void ResultSetQuickCheckTest::testUsedResourcesForAgents() 0578 { 0579 using namespace KAStats; 0580 using namespace KAStats::Terms; 0581 using boost::sort; 0582 using boost::adaptors::filtered; 0583 0584 for (const auto &agent : std::as_const(agentsList)) { 0585 auto memItems = ResourceScoreCache::groupByResource(resourceScoreCaches | filtered(ResourceScoreCache::initiatingAgent() == agent)); 0586 0587 auto baseTerm = UsedResources | Agent{agent} | Activity::any(); 0588 0589 #define ORDERING_TEST(Column, Dir, OrderFlag) \ 0590 { \ 0591 sort(memItems, ResourceScoreCache::Column().Dir() | ResourceScoreCache::targettedResource().asc()); \ 0592 ResultSet dbItems = baseTerm | OrderFlag | Limit(100); \ 0593 ASSERT_RANGE_EQUAL(memItems, dbItems); \ 0594 } 0595 0596 ORDERING_TEST(targettedResource, asc, OrderByUrl) 0597 ORDERING_TEST(cachedScore, desc, HighScoredFirst); 0598 ORDERING_TEST(lastUpdate, desc, RecentlyUsedFirst); 0599 ORDERING_TEST(firstUpdate, desc, RecentlyCreatedFirst); 0600 0601 #undef ORDERING_TEST 0602 } 0603 } 0604 0605 void ResultSetQuickCheckTest::testUsedResourcesForActivities() 0606 { 0607 } 0608 0609 void ResultSetQuickCheckTest::testLinkedResourcesForAgents() 0610 { 0611 using namespace KAStats; 0612 using namespace KAStats::Terms; 0613 using boost::sort; 0614 using boost::adaptors::filtered; 0615 0616 for (const auto &agent : std::as_const(agentsList)) { 0617 /* clang-format off */ 0618 auto memItems = ResourceLink::groupByResource( 0619 resourceLinks 0620 | filtered(ResourceLink::initiatingAgent() == agent) 0621 ); 0622 0623 auto baseTerm = LinkedResources | Agent{agent} | Activity::any(); 0624 0625 #define ORDERING_TEST(Column, Dir, OrderFlag) \ 0626 { \ 0627 sort(memItems, ResourceLink::Column().Dir() \ 0628 | ResourceLink::targettedResource().asc()); \ 0629 ResultSet dbItems = baseTerm | OrderFlag; \ 0630 ASSERT_RANGE_EQUAL(memItems, dbItems); \ 0631 } 0632 0633 ORDERING_TEST(targettedResource, asc, OrderByUrl) 0634 // ORDERING_TEST(cachedScore, desc, HighScoredFirst); 0635 // ORDERING_TEST(lastUpdate, desc, RecentlyUsedFirst); 0636 // ORDERING_TEST(firstUpdate, desc, RecentlyCreatedFirst); 0637 0638 #undef ORDERING_TEST 0639 0640 /* clang-format on */ 0641 } 0642 0643 } 0644 0645 // vim: set foldmethod=marker: