Warning, file /frameworks/kservice/autotests/kservicetest.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: 2006 David Faure <faure@kde.org> 0003 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #include "kservicetest.h" 0009 0010 #include "setupxdgdirs.h" 0011 0012 #include <locale.h> 0013 0014 #include <QTest> 0015 0016 #include <../src/services/kserviceutil_p.h> // for KServiceUtilPrivate 0017 #include <KConfig> 0018 #include <KConfigGroup> 0019 #include <KDesktopFile> 0020 #include <kapplicationtrader.h> 0021 #include <kbuildsycoca_p.h> 0022 #include <ksycoca.h> 0023 0024 #include <KPluginMetaData> 0025 #include <kplugininfo.h> 0026 #include <kservicegroup.h> 0027 #include <kservicetype.h> 0028 #include <kservicetypeprofile.h> 0029 #include <kservicetypetrader.h> 0030 0031 #include <QFile> 0032 #include <QSignalSpy> 0033 #include <QStandardPaths> 0034 #include <QThread> 0035 0036 #include <QDebug> 0037 #include <QLoggingCategory> 0038 #include <QMimeDatabase> 0039 0040 QTEST_MAIN(KServiceTest) 0041 0042 extern KSERVICE_EXPORT int ksycoca_ms_between_checks; 0043 0044 static void eraseProfiles() 0045 { 0046 QString profilerc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String{"/profilerc"}; 0047 if (!profilerc.isEmpty()) { 0048 QFile::remove(profilerc); 0049 } 0050 0051 profilerc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String{"/servicetype_profilerc"}; 0052 if (!profilerc.isEmpty()) { 0053 QFile::remove(profilerc); 0054 } 0055 } 0056 0057 void KServiceTest::initTestCase() 0058 { 0059 // Set up a layer in the bin dir so ksycoca finds the KPluginInfo and Application servicetypes 0060 setupXdgDirs(); 0061 QStandardPaths::setTestModeEnabled(true); 0062 0063 QLoggingCategory::setFilterRules(QStringLiteral("*.debug=true")); 0064 0065 // A non-C locale is necessary for some tests. 0066 // This locale must have the following properties: 0067 // - some character other than dot as decimal separator 0068 // If it cannot be set, locale-dependent tests are skipped. 0069 setlocale(LC_ALL, "fr_FR.utf8"); 0070 m_hasNonCLocale = (setlocale(LC_ALL, nullptr) == QByteArray("fr_FR.utf8")); 0071 if (!m_hasNonCLocale) { 0072 qDebug() << "Setting locale to fr_FR.utf8 failed"; 0073 } 0074 0075 eraseProfiles(); 0076 0077 if (!KSycoca::isAvailable()) { 0078 runKBuildSycoca(); 0079 } 0080 0081 // Create some fake services for the tests below, and ensure they are in ksycoca. 0082 0083 bool mustUpdateKSycoca = false; 0084 0085 // fakeservice: deleted and recreated by testDeletingService, don't use in other tests 0086 const QString fakeServiceDeleteMe = 0087 QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/fakeservice_deleteme.desktop"); 0088 if (!QFile::exists(fakeServiceDeleteMe)) { 0089 mustUpdateKSycoca = true; 0090 createFakeService(QStringLiteral("fakeservice_deleteme.desktop"), QString()); 0091 } 0092 0093 // fakeservice: a plugin that implements FakePluginType 0094 const QString fakeService = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/fakeservice.desktop"); 0095 if (!QFile::exists(fakeService)) { 0096 mustUpdateKSycoca = true; 0097 createFakeService(QStringLiteral("fakeservice.desktop"), QStringLiteral("FakePluginType")); 0098 } 0099 0100 // fakepart: a readwrite part, like katepart 0101 const QString fakePart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservices5/fakepart.desktop"}; 0102 if (!QFile::exists(fakePart)) { 0103 mustUpdateKSycoca = true; 0104 KDesktopFile file(fakePart); 0105 KConfigGroup group = file.desktopGroup(); 0106 group.writeEntry("Name", "FakePart"); 0107 group.writeEntry("Type", "Service"); 0108 group.writeEntry("X-KDE-Library", "fakepart"); 0109 group.writeEntry("X-KDE-Protocols", "http,ftp"); 0110 group.writeEntry("X-KDE-ServiceTypes", "FakeBasePart,FakeDerivedPart"); 0111 group.writeEntry("MimeType", "text/plain;text/html;"); 0112 group.writeEntry("X-KDE-FormFactors", "tablet,handset"); 0113 } 0114 0115 const QString fakePart2 = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservices5/fakepart2.desktop"}; 0116 if (!QFile::exists(fakePart2)) { 0117 mustUpdateKSycoca = true; 0118 KDesktopFile file(fakePart2); 0119 KConfigGroup group = file.desktopGroup(); 0120 group.writeEntry("Name", "FakePart2"); 0121 group.writeEntry("Type", "Service"); 0122 group.writeEntry("X-KDE-Library", "fakepart2"); 0123 group.writeEntry("X-KDE-ServiceTypes", "FakeBasePart"); 0124 group.writeEntry("MimeType", "text/plain;"); 0125 group.writeEntry("X-KDE-TestList", QStringList() << QStringLiteral("item1") << QStringLiteral("item2")); 0126 group.writeEntry("X-KDE-FormFactors", "tablet,handset"); 0127 } 0128 0129 const QString preferredPart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservices5/preferredpart.desktop"}; 0130 if (!QFile::exists(preferredPart)) { 0131 mustUpdateKSycoca = true; 0132 KDesktopFile file(preferredPart); 0133 KConfigGroup group = file.desktopGroup(); 0134 group.writeEntry("Name", "PreferredPart"); 0135 group.writeEntry("Type", "Service"); 0136 group.writeEntry("X-KDE-Library", "preferredpart"); 0137 group.writeEntry("X-KDE-ServiceTypes", "FakeBasePart"); 0138 group.writeEntry("MimeType", "text/plain;"); 0139 } 0140 0141 const QString otherPart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservices5/otherpart.desktop"}; 0142 if (!QFile::exists(otherPart)) { 0143 mustUpdateKSycoca = true; 0144 KDesktopFile file(otherPart); 0145 KConfigGroup group = file.desktopGroup(); 0146 group.writeEntry("Name", "OtherPart"); 0147 group.writeEntry("Type", "Service"); 0148 group.writeEntry("X-KDE-Library", "otherpart"); 0149 group.writeEntry("X-KDE-ServiceTypes", "FakeBasePart"); 0150 group.writeEntry("MimeType", "text/plain;"); 0151 } 0152 0153 // faketextplugin: a ktexteditor plugin 0154 const QString fakeTextplugin = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservices5/faketextplugin.desktop"}; 0155 if (!QFile::exists(fakeTextplugin)) { 0156 mustUpdateKSycoca = true; 0157 KDesktopFile file(fakeTextplugin); 0158 KConfigGroup group = file.desktopGroup(); 0159 group.writeEntry("Name", "FakeTextPlugin"); 0160 group.writeEntry("Type", "Service"); 0161 group.writeEntry("X-KDE-Library", "faketextplugin"); 0162 group.writeEntry("X-KDE-ServiceTypes", "FakePluginType"); 0163 group.writeEntry("MimeType", "text/plain;"); 0164 } 0165 0166 // fakeplugintype: a servicetype 0167 const QString fakePluginType = 0168 QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservicetypes5/fakeplugintype.desktop"}; 0169 if (!QFile::exists(fakePluginType)) { 0170 mustUpdateKSycoca = true; 0171 KDesktopFile file(fakePluginType); 0172 KConfigGroup group = file.desktopGroup(); 0173 group.writeEntry("Comment", "Fake Text Plugin"); 0174 group.writeEntry("Type", "ServiceType"); 0175 group.writeEntry("X-KDE-ServiceType", "FakePluginType"); 0176 file.group("PropertyDef::X-KDE-Version").writeEntry("Type", "double"); // like in ktexteditorplugin.desktop 0177 group.writeEntry("X-KDE-FormFactors", "tablet,handset"); 0178 } 0179 0180 // fakebasepart: a servicetype (like ReadOnlyPart) 0181 const QString fakeBasePart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservicetypes5/fakebasepart.desktop"}; 0182 if (!QFile::exists(fakeBasePart)) { 0183 mustUpdateKSycoca = true; 0184 KDesktopFile file(fakeBasePart); 0185 KConfigGroup group = file.desktopGroup(); 0186 group.writeEntry("Comment", "Fake Base Part"); 0187 group.writeEntry("Type", "ServiceType"); 0188 group.writeEntry("X-KDE-ServiceType", "FakeBasePart"); 0189 0190 KConfigGroup listGroup(&file, "PropertyDef::X-KDE-TestList"); 0191 listGroup.writeEntry("Type", "QStringList"); 0192 } 0193 0194 // fakederivedpart: a servicetype deriving from FakeBasePart (like ReadWritePart derives from ReadOnlyPart) 0195 const QString fakeDerivedPart = 0196 QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservicetypes5/fakederivedpart.desktop"}; 0197 if (!QFile::exists(fakeDerivedPart)) { 0198 mustUpdateKSycoca = true; 0199 KDesktopFile file(fakeDerivedPart); 0200 KConfigGroup group = file.desktopGroup(); 0201 group.writeEntry("Comment", "Fake Derived Part"); 0202 group.writeEntry("Type", "ServiceType"); 0203 group.writeEntry("X-KDE-ServiceType", "FakeDerivedPart"); 0204 group.writeEntry("X-KDE-Derived", "FakeBasePart"); 0205 } 0206 0207 // fakekdedmodule 0208 const QString fakeKdedModule = 0209 QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservicetypes5/fakekdedmodule.desktop"}; 0210 if (!QFile::exists(fakeKdedModule)) { 0211 const QString src = QFINDTESTDATA("fakekdedmodule.desktop"); 0212 QVERIFY(QFile::copy(src, fakeKdedModule)); 0213 mustUpdateKSycoca = true; 0214 } 0215 0216 // faketestapp.desktop 0217 const QString testApp = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + QLatin1String("/org.kde.faketestapp.desktop"); 0218 if (!QFile::exists(testApp)) { 0219 QVERIFY(QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation))); 0220 const QString src = QFINDTESTDATA("org.kde.faketestapp.desktop"); 0221 QVERIFY(!src.isEmpty()); 0222 QVERIFY2(QFile::copy(src, testApp), qPrintable(testApp)); 0223 qDebug() << "Created" << testApp; 0224 mustUpdateKSycoca = true; 0225 } 0226 0227 // otherfakeapp.desktop 0228 const QString otherTestApp = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + QLatin1String("/org.kde.otherfakeapp.desktop"); 0229 if (!QFile::exists(otherTestApp)) { 0230 QVERIFY(QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation))); 0231 const QString src = QFINDTESTDATA("org.kde.otherfakeapp.desktop"); 0232 QVERIFY(!src.isEmpty()); 0233 QVERIFY2(QFile::copy(src, otherTestApp), qPrintable(otherTestApp)); 0234 qDebug() << "Created" << otherTestApp; 0235 mustUpdateKSycoca = true; 0236 } 0237 0238 if (mustUpdateKSycoca) { 0239 // Update ksycoca in ~/.qttest after creating the above 0240 runKBuildSycoca(true); 0241 } 0242 QVERIFY(KServiceType::serviceType(QStringLiteral("FakePluginType"))); 0243 QVERIFY(KServiceType::serviceType(QStringLiteral("FakeBasePart"))); 0244 QVERIFY(KServiceType::serviceType(QStringLiteral("FakeDerivedPart"))); 0245 QVERIFY(KService::serviceByDesktopName(QStringLiteral("org.kde.faketestapp"))); 0246 QVERIFY(KService::serviceByDesktopName(QStringLiteral("org.kde.otherfakeapp"))); 0247 } 0248 0249 void KServiceTest::runKBuildSycoca(bool noincremental) 0250 { 0251 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 80) 0252 QSignalSpy spy(KSycoca::self(), qOverload<const QStringList &>(&KSycoca::databaseChanged)); 0253 #else 0254 QSignalSpy spy(KSycoca::self(), &KSycoca::databaseChanged); 0255 #endif 0256 0257 KBuildSycoca builder; 0258 QVERIFY(builder.recreate(!noincremental)); 0259 if (spy.isEmpty()) { 0260 qDebug() << "waiting for signal"; 0261 QVERIFY(spy.wait(10000)); 0262 qDebug() << "got signal"; 0263 } 0264 } 0265 0266 void KServiceTest::cleanupTestCase() 0267 { 0268 // If I want the konqueror unit tests to work, then I better not have a non-working part 0269 // as the preferred part for text/plain... 0270 const QStringList services = QStringList() << QStringLiteral("fakeservice.desktop") << QStringLiteral("fakepart.desktop") 0271 << QStringLiteral("faketextplugin.desktop") << QStringLiteral("fakeservice_querymustrebuild.desktop"); 0272 for (const QString &service : services) { 0273 const QString fakeService = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + service; 0274 QFile::remove(fakeService); 0275 } 0276 const QStringList serviceTypes = QStringList() << QStringLiteral("fakeplugintype.desktop"); 0277 for (const QString &serviceType : serviceTypes) { 0278 const QString fakeServiceType = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservicetypes5/") + serviceType; 0279 // QFile::remove(fakeServiceType); 0280 } 0281 KBuildSycoca builder; 0282 builder.recreate(); 0283 } 0284 0285 void KServiceTest::testByName() 0286 { 0287 if (!KSycoca::isAvailable()) { 0288 QSKIP("ksycoca not available"); 0289 } 0290 0291 KServiceType::Ptr s0 = KServiceType::serviceType(QStringLiteral("FakeBasePart")); 0292 QVERIFY2(s0, "KServiceType::serviceType(\"FakeBasePart\") failed!"); 0293 QCOMPARE(s0->name(), QStringLiteral("FakeBasePart")); 0294 0295 KService::Ptr myService = KService::serviceByDesktopPath(QStringLiteral("fakepart.desktop")); 0296 QVERIFY(myService); 0297 QCOMPARE(myService->name(), QStringLiteral("FakePart")); 0298 } 0299 0300 void KServiceTest::testConstructorFullPath() 0301 { 0302 // Requirement: text/html must be a known mimetype 0303 QVERIFY(QMimeDatabase().mimeTypeForName(QStringLiteral("text/html")).isValid()); 0304 const QString fakePart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservices5/fakepart.desktop"}; 0305 QVERIFY(QFile::exists(fakePart)); 0306 KService service(fakePart); 0307 QVERIFY(service.isValid()); 0308 QCOMPARE(service.mimeTypes(), (QStringList{QStringLiteral("text/plain"), QStringLiteral("text/html")})); 0309 } 0310 0311 void KServiceTest::testConstructorKDesktopFileFullPath() 0312 { 0313 const QString fakePart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservices5/fakepart.desktop"}; 0314 QVERIFY(QFile::exists(fakePart)); 0315 KDesktopFile desktopFile(fakePart); 0316 KService service(&desktopFile); 0317 QVERIFY(service.isValid()); 0318 QCOMPARE(service.mimeTypes(), (QStringList{QStringLiteral("text/plain"), QStringLiteral("text/html")})); 0319 } 0320 0321 void KServiceTest::testConstructorKDesktopFile() // as happens inside kbuildsycoca.cpp 0322 { 0323 KDesktopFile desktopFile(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/fakepart.desktop")); 0324 QCOMPARE(KService(&desktopFile, QStringLiteral("kservices5/fakepart.desktop")).mimeTypes(), 0325 (QStringList{QStringLiteral("text/plain"), QStringLiteral("text/html")})); 0326 } 0327 0328 void KServiceTest::testCopyConstructor() 0329 { 0330 const QString fakePart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservices5/fakepart.desktop"}; 0331 QVERIFY(QFile::exists(fakePart)); 0332 KDesktopFile desktopFile(fakePart); 0333 // KRun needs to make a copy of a KService that will go out of scope, let's test that here. 0334 KService::Ptr service; 0335 { 0336 KService origService(&desktopFile); 0337 service = new KService(origService); 0338 } 0339 QVERIFY(service->isValid()); 0340 QCOMPARE(service->mimeTypes(), (QStringList{QStringLiteral("text/plain"), QStringLiteral("text/html")})); 0341 } 0342 0343 void KServiceTest::testCopyInvalidService() 0344 { 0345 KService::Ptr service; 0346 { 0347 KService origService{QString()}; // this still sets a d_ptr, so no problem; 0348 QVERIFY(!origService.isValid()); 0349 service = new KService(origService); 0350 } 0351 QVERIFY(!service->isValid()); 0352 } 0353 0354 void KServiceTest::testProperty() 0355 { 0356 ksycoca_ms_between_checks = 0; 0357 0358 // Let's try creating a desktop file and ensuring it's noticed by the timestamp check 0359 QTest::qWait(1000); 0360 const QString fakeCookie = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservices5/kded/fakekcookiejar.desktop"}; 0361 if (!QFile::exists(fakeCookie)) { 0362 KDesktopFile file(fakeCookie); 0363 KConfigGroup group = file.desktopGroup(); 0364 group.writeEntry("Name", "OtherPart"); 0365 group.writeEntry("Type", "Service"); 0366 group.writeEntry("X-KDE-ServiceTypes", "FakeKDEDModule"); 0367 group.writeEntry("X-KDE-Library", "kcookiejar"); 0368 group.writeEntry("X-KDE-Kded-autoload", "false"); 0369 group.writeEntry("X-KDE-Kded-load-on-demand", "true"); 0370 qDebug() << "created" << fakeCookie; 0371 } 0372 0373 KService::Ptr kdedkcookiejar = KService::serviceByDesktopPath(QStringLiteral("kded/fakekcookiejar.desktop")); 0374 QVERIFY(kdedkcookiejar); 0375 QCOMPARE(kdedkcookiejar->entryPath(), QStringLiteral("kded/fakekcookiejar.desktop")); 0376 0377 QCOMPARE(kdedkcookiejar->property(QStringLiteral("ServiceTypes")).toStringList().join(QLatin1Char(',')), QStringLiteral("FakeKDEDModule")); 0378 QCOMPARE(kdedkcookiejar->property(QStringLiteral("X-KDE-Kded-autoload")).toBool(), false); 0379 QCOMPARE(kdedkcookiejar->property(QStringLiteral("X-KDE-Kded-load-on-demand")).toBool(), true); 0380 QVERIFY(!kdedkcookiejar->property(QStringLiteral("Name")).toString().isEmpty()); 0381 QVERIFY(!kdedkcookiejar->property(QStringLiteral("Name[fr]"), QMetaType::QString).isValid()); 0382 0383 // TODO: for this we must install a servicetype desktop file... 0384 // KService::Ptr kjavaappletviewer = KService::serviceByDesktopPath("kjavaappletviewer.desktop"); 0385 // QVERIFY(kjavaappletviewer); 0386 // QCOMPARE(kjavaappletviewer->property("X-KDE-BrowserView-PluginsInfo").toString(), QString("kjava/pluginsinfo")); 0387 0388 // Test property("X-KDE-Protocols"), which triggers the KServiceReadProperty code. 0389 KService::Ptr fakePart = KService::serviceByDesktopPath(QStringLiteral("fakepart.desktop")); 0390 QVERIFY(fakePart); // see initTestCase; it should be found. 0391 QVERIFY(fakePart->propertyNames().contains(QLatin1String("X-KDE-Protocols"))); 0392 QCOMPARE(fakePart->mimeTypes(), 0393 QStringList() << QStringLiteral("text/plain") << QStringLiteral("text/html")); // okular relies on subclasses being kept here 0394 const QStringList protocols = fakePart->property(QStringLiteral("X-KDE-Protocols")).toStringList(); 0395 QCOMPARE(protocols, QStringList() << QStringLiteral("http") << QStringLiteral("ftp")); 0396 0397 // Restore value 0398 ksycoca_ms_between_checks = 1500; 0399 } 0400 0401 void KServiceTest::testAllServiceTypes() 0402 { 0403 if (!KSycoca::isAvailable()) { 0404 QSKIP("ksycoca not available"); 0405 } 0406 0407 const KServiceType::List allServiceTypes = KServiceType::allServiceTypes(); 0408 0409 // A bit of checking on the allServiceTypes list itself 0410 for (const KServiceType::Ptr &servtype : allServiceTypes) { 0411 const QString name = servtype->name(); 0412 QVERIFY(!name.isEmpty()); 0413 QVERIFY(servtype->sycocaType() == KST_KServiceType); 0414 } 0415 } 0416 0417 void KServiceTest::testAllServices() 0418 { 0419 if (!KSycoca::isAvailable()) { 0420 QSKIP("ksycoca not available"); 0421 } 0422 const KService::List lst = KService::allServices(); 0423 QVERIFY(!lst.isEmpty()); 0424 bool foundTestApp = false; 0425 0426 for (const KService::Ptr &service : lst) { 0427 QVERIFY(service->isType(KST_KService)); 0428 0429 const QString name = service->name(); 0430 const QString entryPath = service->entryPath(); 0431 if (entryPath.contains(QLatin1String{"fake"})) { 0432 qDebug() << name << "entryPath=" << entryPath << "menuId=" << service->menuId(); 0433 } 0434 QVERIFY(!name.isEmpty()); 0435 QVERIFY(!entryPath.isEmpty()); 0436 0437 KService::Ptr lookedupService = KService::serviceByDesktopPath(entryPath); 0438 QVERIFY(lookedupService); // not null 0439 QCOMPARE(lookedupService->entryPath(), entryPath); 0440 0441 if (service->isApplication()) { 0442 const QString menuId = service->menuId(); 0443 if (menuId.isEmpty()) { 0444 qWarning("%s has an empty menuId!", qPrintable(entryPath)); 0445 } else if (menuId == QLatin1String{"org.kde.faketestapp.desktop"}) { 0446 foundTestApp = true; 0447 } 0448 QVERIFY(!menuId.isEmpty()); 0449 lookedupService = KService::serviceByMenuId(menuId); 0450 QVERIFY(lookedupService); // not null 0451 QCOMPARE(lookedupService->menuId(), menuId); 0452 } 0453 } 0454 QVERIFY(foundTestApp); 0455 } 0456 0457 // Helper method for all the trader tests 0458 static bool offerListHasService(const KService::List &offers, const QString &entryPath) 0459 { 0460 bool found = false; 0461 for (const auto &servicePtr : offers) { 0462 if (servicePtr->entryPath() == entryPath) { 0463 if (found) { // should be there only once 0464 qWarning("ERROR: %s was found twice in the list", qPrintable(entryPath)); 0465 return false; // make test fail 0466 } 0467 found = true; 0468 } 0469 } 0470 return found; 0471 } 0472 0473 void KServiceTest::testDBUSStartupType() 0474 { 0475 if (!KSycoca::isAvailable()) { 0476 QSKIP("ksycoca not available"); 0477 } 0478 KService::Ptr testapp = KService::serviceByDesktopName(QStringLiteral("org.kde.faketestapp")); 0479 QVERIFY(testapp); 0480 QCOMPARE(testapp->menuId(), QStringLiteral("org.kde.faketestapp.desktop")); 0481 // qDebug() << testapp->entryPath(); 0482 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 102) 0483 QCOMPARE(int(testapp->dbusStartupType()), int(KService::DBusUnique)); 0484 #endif 0485 } 0486 0487 void KServiceTest::testByStorageId() 0488 { 0489 if (!KSycoca::isAvailable()) { 0490 QSKIP("ksycoca not available"); 0491 } 0492 QVERIFY(!QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QStringLiteral("org.kde.faketestapp.desktop")).isEmpty()); 0493 QVERIFY(KService::serviceByMenuId(QStringLiteral("org.kde.faketestapp.desktop"))); 0494 QVERIFY(!KService::serviceByMenuId(QStringLiteral("org.kde.faketestapp"))); // doesn't work, extension mandatory 0495 QVERIFY(!KService::serviceByMenuId(QStringLiteral("faketestapp.desktop"))); // doesn't work, full filename mandatory 0496 QVERIFY(KService::serviceByStorageId(QStringLiteral("org.kde.faketestapp.desktop"))); 0497 QVERIFY(KService::serviceByStorageId(QStringLiteral("org.kde.faketestapp"))); 0498 0499 QVERIFY(KService::serviceByDesktopName(QStringLiteral("org.kde.faketestapp"))); 0500 QCOMPARE(KService::serviceByDesktopName(QStringLiteral("org.kde.faketestapp"))->menuId(), QStringLiteral("org.kde.faketestapp.desktop")); 0501 } 0502 0503 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 90) 0504 void KServiceTest::testServiceTypeTraderForReadOnlyPart() 0505 { 0506 if (!KSycoca::isAvailable()) { 0507 QSKIP("ksycoca not available"); 0508 } 0509 0510 // Querying trader for services associated with FakeBasePart 0511 KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("FakeBasePart")); 0512 QVERIFY(offers.count() > 0); 0513 0514 if (!offerListHasService(offers, QStringLiteral("fakepart.desktop")) // 0515 || !offerListHasService(offers, QStringLiteral("fakepart2.desktop")) // 0516 || !offerListHasService(offers, QStringLiteral("otherpart.desktop")) // 0517 || !offerListHasService(offers, QStringLiteral("preferredpart.desktop"))) { 0518 for (KService::Ptr service : std::as_const(offers)) { 0519 qDebug("%s %s", qPrintable(service->name()), qPrintable(service->entryPath())); 0520 } 0521 } 0522 0523 m_firstOffer = offers[0]->entryPath(); 0524 0525 QVERIFY(offerListHasService(offers, QStringLiteral("fakepart.desktop"))); 0526 QVERIFY(offerListHasService(offers, QStringLiteral("fakepart2.desktop"))); 0527 QVERIFY(offerListHasService(offers, QStringLiteral("otherpart.desktop"))); 0528 QVERIFY(offerListHasService(offers, QStringLiteral("preferredpart.desktop"))); 0529 0530 // Check ordering according to InitialPreference 0531 int lastPreference = -1; 0532 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 67) 0533 bool lastAllowedAsDefault = true; 0534 #endif 0535 for (KService::Ptr service : std::as_const(offers)) { 0536 const int preference = service->initialPreference(); // ## might be wrong if we use per-servicetype preferences... 0537 // qDebug( "%s has preference %d, allowAsDefault=%d", qPrintable( path ), preference, service->allowAsDefault() ); 0538 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 67) 0539 if (lastAllowedAsDefault && !service->allowAsDefault()) { 0540 // first "not allowed as default" offer 0541 lastAllowedAsDefault = false; 0542 lastPreference = -1; // restart 0543 } 0544 #endif 0545 if (lastPreference != -1) { 0546 QVERIFY(preference <= lastPreference); 0547 } 0548 lastPreference = preference; 0549 } 0550 0551 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 90) 0552 // Now look for any FakePluginType 0553 offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType")); 0554 QVERIFY(offerListHasService(offers, QStringLiteral("fakeservice.desktop"))); 0555 QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); 0556 #endif 0557 } 0558 0559 void KServiceTest::testTraderConstraints() 0560 { 0561 if (!KSycoca::isAvailable()) { 0562 QSKIP("ksycoca not available"); 0563 } 0564 0565 KService::List offers; 0566 0567 // Baseline: no constraints 0568 offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType")); 0569 QCOMPARE(offers.count(), 2); 0570 QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); 0571 QVERIFY(offerListHasService(offers, QStringLiteral("fakeservice.desktop"))); 0572 0573 // String-based constraint 0574 offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("Library == 'faketextplugin'")); 0575 QCOMPARE(offers.count(), 1); 0576 QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); 0577 0578 // Match case insensitive 0579 offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("Library =~ 'fAkEteXtpLuGin'")); 0580 QCOMPARE(offers.count(), 1); 0581 QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); 0582 0583 // "contains" 0584 offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), 0585 QStringLiteral("'textplugin' ~ Library")); // note: "is contained in", not "contains"... 0586 QCOMPARE(offers.count(), 1); 0587 QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); 0588 0589 // "contains" case insensitive 0590 offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), 0591 QStringLiteral("'teXtPluGin' ~~ Library")); // note: "is contained in", not "contains"... 0592 QCOMPARE(offers.count(), 1); 0593 QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); 0594 0595 // sub-sequence case sensitive 0596 offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("'txtlug' subseq Library")); 0597 QCOMPARE(offers.count(), 1); 0598 QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); 0599 0600 // sub-sequence case insensitive 0601 offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("'tXtLuG' ~subseq Library")); 0602 QCOMPARE(offers.count(), 1); 0603 QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); 0604 0605 if (m_hasNonCLocale) { 0606 // Test float parsing, must use dot as decimal separator independent of locale. 0607 offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("([X-KDE-Version] > 4.559) and ([X-KDE-Version] < 4.561)")); 0608 QCOMPARE(offers.count(), 1); 0609 QVERIFY(offerListHasService(offers, QStringLiteral("fakeservice.desktop"))); 0610 } 0611 0612 // A test with an invalid query, to test for memleaks 0613 offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("A == B OR C == D AND OR Foo == 'Parse Error'")); 0614 QVERIFY(offers.isEmpty()); 0615 } 0616 #endif 0617 0618 void KServiceTest::testSubseqConstraints() 0619 { 0620 auto test = [](const char *pattern, const char *text, bool sensitive) { 0621 return KApplicationTrader::isSubsequence(QString::fromLatin1(pattern), QString::fromLatin1(text), sensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); 0622 }; 0623 0624 // Case Sensitive 0625 QVERIFY2(!test("", "", 1), "both empty"); 0626 QVERIFY2(!test("", "something", 1), "empty pattern"); 0627 QVERIFY2(!test("something", "", 1), "empty text"); 0628 QVERIFY2(test("lngfile", "somereallylongfile", 1), "match ending"); 0629 QVERIFY2(test("somelong", "somereallylongfile", 1), "match beginning"); 0630 QVERIFY2(test("reallylong", "somereallylongfile", 1), "match middle"); 0631 QVERIFY2(test("across", "a 23 c @#! r o01 o 5 s_s", 1), "match across"); 0632 QVERIFY2(!test("nocigar", "soclosebutnociga", 1), "close but no match"); 0633 QVERIFY2(!test("god", "dog", 1), "incorrect letter order"); 0634 QVERIFY2(!test("mismatch", "mIsMaTcH", 1), "case sensitive mismatch"); 0635 0636 // Case insensitive 0637 QVERIFY2(test("mismatch", "mIsMaTcH", 0), "case insensitive match"); 0638 QVERIFY2(test("tryhards", "Try Your Hardest", 0), "uppercase text"); 0639 QVERIFY2(test("TRYHARDS", "try your hardest", 0), "uppercase pattern"); 0640 } 0641 0642 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 104) 0643 void KServiceTest::testHasServiceType1() // with services constructed with a full path (rare) 0644 { 0645 QString fakepartPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/fakepart.desktop")); 0646 QVERIFY(!fakepartPath.isEmpty()); 0647 KService fakepart(fakepartPath); 0648 QVERIFY(fakepart.hasServiceType(QStringLiteral("FakeBasePart"))); 0649 QVERIFY(fakepart.hasServiceType(QStringLiteral("FakeDerivedPart"))); 0650 QCOMPARE(fakepart.mimeTypes(), QStringList() << QStringLiteral("text/plain") << QStringLiteral("text/html")); 0651 0652 QString faketextPluginPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/faketextplugin.desktop")); 0653 QVERIFY(!faketextPluginPath.isEmpty()); 0654 KService faketextPlugin(faketextPluginPath); 0655 QVERIFY(faketextPlugin.hasServiceType(QStringLiteral("FakePluginType"))); 0656 QVERIFY(!faketextPlugin.hasServiceType(QStringLiteral("FakeBasePart"))); 0657 } 0658 0659 void KServiceTest::testHasServiceType2() // with services coming from ksycoca 0660 { 0661 KService::Ptr fakepart = KService::serviceByDesktopPath(QStringLiteral("fakepart.desktop")); 0662 QVERIFY(fakepart); 0663 QVERIFY(fakepart->hasServiceType(QStringLiteral("FakeBasePart"))); 0664 QVERIFY(fakepart->hasServiceType(QStringLiteral("FakeDerivedPart"))); 0665 QCOMPARE(fakepart->mimeTypes(), QStringList() << QStringLiteral("text/plain") << QStringLiteral("text/html")); 0666 0667 KService::Ptr faketextPlugin = KService::serviceByDesktopPath(QStringLiteral("faketextplugin.desktop")); 0668 QVERIFY(faketextPlugin); 0669 QVERIFY(faketextPlugin->hasServiceType(QStringLiteral("FakePluginType"))); 0670 QVERIFY(!faketextPlugin->hasServiceType(QStringLiteral("FakeBasePart"))); 0671 } 0672 #endif 0673 0674 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 66) 0675 void KServiceTest::testWriteServiceTypeProfile() 0676 { 0677 const QString serviceType = QStringLiteral("FakeBasePart"); 0678 KService::List services; 0679 KService::List disabledServices; 0680 services.append(KService::serviceByDesktopPath(QStringLiteral("preferredpart.desktop"))); 0681 services.append(KService::serviceByDesktopPath(QStringLiteral("fakepart.desktop"))); 0682 disabledServices.append(KService::serviceByDesktopPath(QStringLiteral("fakepart2.desktop"))); 0683 0684 for (const KService::Ptr &serv : std::as_const(services)) { 0685 QVERIFY(serv); 0686 } 0687 for (const KService::Ptr &serv : std::as_const(disabledServices)) { 0688 QVERIFY(serv); 0689 } 0690 0691 KServiceTypeProfile::writeServiceTypeProfile(serviceType, services, disabledServices); 0692 0693 // Check that the file got written 0694 QString profilerc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String{"/servicetype_profilerc"}; 0695 QVERIFY(!profilerc.isEmpty()); 0696 QVERIFY(QFile::exists(profilerc)); 0697 0698 KService::List offers = KServiceTypeTrader::self()->query(serviceType); 0699 QVERIFY(offers.count() > 0); // not empty 0700 0701 // foreach( KService::Ptr service, offers ) 0702 // qDebug( "%s %s", qPrintable( service->name() ), qPrintable( service->entryPath() ) ); 0703 0704 QVERIFY(offers.count() >= 2); 0705 QCOMPARE(offers[0]->entryPath(), QStringLiteral("preferredpart.desktop")); 0706 QCOMPARE(offers[1]->entryPath(), QStringLiteral("fakepart.desktop")); 0707 QVERIFY(offerListHasService(offers, QStringLiteral("otherpart.desktop"))); // should still be somewhere in there 0708 QVERIFY(!offerListHasService(offers, QStringLiteral("fakepart2.desktop"))); // it got disabled above 0709 } 0710 #endif 0711 0712 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 90) 0713 void KServiceTest::testDefaultOffers() 0714 { 0715 // Now that we have a user-profile, let's see if defaultOffers indeed gives us the default ordering. 0716 const QString serviceType = QStringLiteral("FakeBasePart"); 0717 KService::List offers = KServiceTypeTrader::self()->defaultOffers(serviceType); 0718 QVERIFY(offers.count() > 0); // not empty 0719 QVERIFY(offerListHasService(offers, QStringLiteral("fakepart2.desktop"))); // it's here even though it's disabled in the profile 0720 QVERIFY(offerListHasService(offers, QStringLiteral("otherpart.desktop"))); 0721 if (m_firstOffer.isEmpty()) { 0722 QSKIP("testServiceTypeTraderForReadOnlyPart not run"); 0723 } 0724 QCOMPARE(offers[0]->entryPath(), m_firstOffer); 0725 } 0726 #endif 0727 0728 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 66) 0729 void KServiceTest::testDeleteServiceTypeProfile() 0730 { 0731 const QString serviceType = QStringLiteral("FakeBasePart"); 0732 KServiceTypeProfile::deleteServiceTypeProfile(serviceType); 0733 0734 KService::List offers = KServiceTypeTrader::self()->query(serviceType); 0735 QVERIFY(offers.count() > 0); // not empty 0736 QVERIFY(offerListHasService(offers, QStringLiteral("fakepart2.desktop"))); // it's back 0737 0738 if (m_firstOffer.isEmpty()) { 0739 QSKIP("testServiceTypeTraderForReadOnlyPart not run"); 0740 } 0741 QCOMPARE(offers[0]->entryPath(), m_firstOffer); 0742 } 0743 #endif 0744 0745 void KServiceTest::testActionsAndDataStream() 0746 { 0747 KService::Ptr service = KService::serviceByStorageId(QStringLiteral("org.kde.faketestapp.desktop")); 0748 QVERIFY(service); 0749 QVERIFY(!service->property(QStringLiteral("Name[fr]"), QMetaType::QString).isValid()); 0750 const QList<KServiceAction> actions = service->actions(); 0751 QCOMPARE(actions.count(), 2); // NewWindow, NewTab 0752 const KServiceAction newTabAction = actions.at(1); 0753 QCOMPARE(newTabAction.name(), QStringLiteral("NewTab")); 0754 QCOMPARE(newTabAction.exec(), QStringLiteral("konsole --new-tab")); 0755 QCOMPARE(newTabAction.icon(), QStringLiteral("tab-new")); 0756 QCOMPARE(newTabAction.noDisplay(), false); 0757 QVERIFY(!newTabAction.isSeparator()); 0758 QCOMPARE(newTabAction.service()->name(), service->name()); 0759 } 0760 0761 void KServiceTest::testServiceGroups() 0762 { 0763 KServiceGroup::Ptr root = KServiceGroup::root(); 0764 QVERIFY(root); 0765 qDebug() << root->groupEntries().count(); 0766 0767 KServiceGroup::Ptr group = root; 0768 QVERIFY(group); 0769 const KServiceGroup::List list = group->entries(true, // sorted 0770 true, // exclude no display entries, 0771 false, // allow separators 0772 true); // sort by generic name 0773 0774 qDebug() << list.count(); 0775 for (KServiceGroup::SPtr s : list) { 0776 qDebug() << s->name() << s->entryPath(); 0777 } 0778 0779 // No unit test here yet, but at least this can be valgrinded for errors. 0780 } 0781 0782 void KServiceTest::testDeletingService() 0783 { 0784 // workaround unexplained inotify issue (in CI only...) 0785 QTest::qWait(1000); 0786 0787 const QString serviceName = QStringLiteral("fakeservice_deleteme.desktop"); 0788 KService::Ptr fakeService = KService::serviceByDesktopPath(serviceName); 0789 QVERIFY(fakeService); // see initTestCase; it should be found. 0790 0791 // Test deleting a service 0792 const QString servPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + serviceName; 0793 QVERIFY(QFile::exists(servPath)); 0794 QFile::remove(servPath); 0795 runKBuildSycoca(); 0796 ksycoca_ms_between_checks = 0; // need it to check the ksycoca mtime 0797 QVERIFY(!KService::serviceByDesktopPath(serviceName)); // not in ksycoca anymore 0798 0799 // Restore value 0800 ksycoca_ms_between_checks = 1500; 0801 0802 QVERIFY(fakeService); // the whole point of refcounting is that this KService instance is still valid. 0803 QVERIFY(!QFile::exists(servPath)); 0804 0805 // Recreate it, for future tests 0806 createFakeService(serviceName, QString()); 0807 QVERIFY(QFile::exists(servPath)); 0808 qDebug() << "executing kbuildsycoca (2)"; 0809 0810 runKBuildSycoca(); 0811 0812 if (QThread::currentThread() != QCoreApplication::instance()->thread()) { 0813 m_sycocaUpdateDone.ref(); 0814 } 0815 } 0816 0817 void KServiceTest::createFakeService(const QString &filename, const QString &serviceType) 0818 { 0819 const QString fakeService = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + filename; 0820 KDesktopFile file(fakeService); 0821 KConfigGroup group = file.desktopGroup(); 0822 group.writeEntry("Name", "FakePlugin"); 0823 group.writeEntry("Type", "Service"); 0824 group.writeEntry("X-KDE-Library", "fakeservice"); 0825 group.writeEntry("X-KDE-Version", "4.56"); 0826 group.writeEntry("ServiceTypes", serviceType); 0827 group.writeEntry("MimeType", "text/plain;"); 0828 } 0829 0830 #include <QFutureSynchronizer> 0831 #include <QThreadPool> 0832 #include <QtConcurrentRun> 0833 0834 // Testing for concurrent access to ksycoca from multiple threads 0835 // It's especially interesting to run this test as ./kservicetest testThreads 0836 // so that even the ksycoca initialization is happening from N threads at the same time. 0837 // Use valgrind --tool=helgrind to see the race conditions. 0838 0839 void KServiceTest::testReaderThreads() 0840 { 0841 QThreadPool::globalInstance()->setMaxThreadCount(10); 0842 QFutureSynchronizer<void> sync; 0843 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0844 sync.addFuture(QtConcurrent::run(&KServiceTest::testAllServices, this)); 0845 sync.addFuture(QtConcurrent::run(&KServiceTest::testAllServices, this)); 0846 sync.addFuture(QtConcurrent::run(&KServiceTest::testAllServices, this)); 0847 sync.addFuture(QtConcurrent::run(&KServiceTest::testHasServiceType1, this)); 0848 sync.addFuture(QtConcurrent::run(&KServiceTest::testAllServices, this)); 0849 sync.addFuture(QtConcurrent::run(&KServiceTest::testAllServices, this)); 0850 #else 0851 sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); 0852 sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); 0853 sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); 0854 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 104) 0855 sync.addFuture(QtConcurrent::run(this, &KServiceTest::testHasServiceType1)); 0856 #endif 0857 sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); 0858 sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); 0859 #endif 0860 sync.waitForFinished(); 0861 QThreadPool::globalInstance()->setMaxThreadCount(1); // delete those threads 0862 } 0863 0864 void KServiceTest::testThreads() 0865 { 0866 QThreadPool::globalInstance()->setMaxThreadCount(10); 0867 QFutureSynchronizer<void> sync; 0868 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0869 sync.addFuture(QtConcurrent::run(&KServiceTest::testAllServices, this)); 0870 sync.addFuture(QtConcurrent::run(&KServiceTest::testHasServiceType1, this)); 0871 sync.addFuture(QtConcurrent::run(&KServiceTest::testDeletingService, this)); 0872 #else 0873 sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); 0874 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 104) 0875 sync.addFuture(QtConcurrent::run(this, &KServiceTest::testHasServiceType1)); 0876 #endif 0877 sync.addFuture(QtConcurrent::run(this, &KServiceTest::testDeletingService)); 0878 0879 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 90) 0880 sync.addFuture(QtConcurrent::run(this, &KServiceTest::testTraderConstraints)); 0881 #endif 0882 #endif // QT_VERSION 0883 0884 // process events (DBus, inotify...), until we get all expected signals 0885 QTRY_COMPARE_WITH_TIMEOUT(m_sycocaUpdateDone.loadRelaxed(), 1, 15000); // not using a bool, just to silence helgrind 0886 qDebug() << "Joining all threads"; 0887 sync.waitForFinished(); 0888 } 0889 0890 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 86) 0891 void KServiceTest::testOperatorKPluginName() 0892 { 0893 QT_WARNING_PUSH 0894 QT_WARNING_DISABLE_DEPRECATED 0895 KService fservice(QFINDTESTDATA("fakeplugin.desktop")); 0896 KPluginName fname(fservice); 0897 QVERIFY(fname.isValid()); 0898 QCOMPARE(fname.name(), QStringLiteral("fakeplugin")); 0899 KPluginLoader fplugin(fservice); 0900 QVERIFY(fplugin.factory()); 0901 0902 // make sure constness doesn't break anything 0903 const KService const_fservice(QFINDTESTDATA("fakeplugin.desktop")); 0904 KPluginName const_fname(const_fservice); 0905 QVERIFY(const_fname.isValid()); 0906 QCOMPARE(const_fname.name(), QStringLiteral("fakeplugin")); 0907 KPluginLoader const_fplugin(const_fservice); 0908 QVERIFY(const_fplugin.factory()); 0909 0910 KService nservice(QFINDTESTDATA("noplugin.desktop")); 0911 KPluginName nname(nservice); 0912 QVERIFY(!nname.isValid()); 0913 QVERIFY2(nname.name().isEmpty(), qPrintable(nname.name())); 0914 QVERIFY(!nname.errorString().isEmpty()); 0915 KPluginLoader nplugin(nservice); 0916 QVERIFY(!nplugin.factory()); 0917 0918 KService iservice(QStringLiteral("idonotexist.desktop")); 0919 KPluginName iname(iservice); 0920 QVERIFY(!iname.isValid()); 0921 QVERIFY2(iname.name().isEmpty(), qPrintable(iname.name())); 0922 QVERIFY(!iname.errorString().isEmpty()); 0923 KPluginLoader iplugin(iservice); 0924 QVERIFY(!iplugin.factory()); 0925 QT_WARNING_POP 0926 } 0927 #endif 0928 0929 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 90) 0930 void KServiceTest::testKPluginInfoQuery() 0931 { 0932 KPluginInfo info(KPluginMetaData::fromDesktopFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) 0933 + QLatin1String{"/kservices5/fakepart2.desktop"})); 0934 0935 QCOMPARE(info.property(QStringLiteral("X-KDE-TestList")).toStringList().size(), 2); 0936 } 0937 #endif 0938 0939 void KServiceTest::testCompleteBaseName() 0940 { 0941 QCOMPARE(KServiceUtilPrivate::completeBaseName(QStringLiteral("/home/x/.qttest/share/kservices5/fakepart2.desktop")), QStringLiteral("fakepart2")); 0942 // dots in filename before .desktop extension: 0943 QCOMPARE(KServiceUtilPrivate::completeBaseName(QStringLiteral("/home/x/.qttest/share/kservices5/org.kde.fakeapp.desktop")), 0944 QStringLiteral("org.kde.fakeapp")); 0945 } 0946 0947 void KServiceTest::testEntryPathToName() 0948 { 0949 QCOMPARE(KService(QStringLiteral("c.desktop")).name(), QStringLiteral("c")); 0950 QCOMPARE(KService(QStringLiteral("a.b.c.desktop")).name(), QStringLiteral("a.b.c")); // dots in filename before .desktop extension 0951 QCOMPARE(KService(QStringLiteral("/hallo/a.b.c.desktop")).name(), QStringLiteral("a.b.c")); 0952 } 0953 0954 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0) 0955 void KServiceTest::testKPluginMetaData() 0956 { 0957 const QString fakePart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kservices5/fakepart.desktop"}; 0958 KPluginMetaData md = KPluginMetaData::fromDesktopFile(fakePart); 0959 KService::Ptr service(new KService(fakePart)); 0960 KPluginInfo info(service); 0961 auto info_md = info.toMetaData(); 0962 QCOMPARE(info_md.formFactors(), md.formFactors()); 0963 } 0964 #endif 0965 0966 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 90) 0967 void KServiceTest::testTraderQueryMustRebuildSycoca() 0968 { 0969 QVERIFY(!KServiceTypeProfile::hasProfile(QStringLiteral("FakeBasePart"))); 0970 QTest::qWait(1000); 0971 createFakeService(QStringLiteral("fakeservice_querymustrebuild.desktop"), QString()); // just to touch the dir 0972 KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("FakeBasePart")); 0973 QVERIFY(offers.count() > 0); 0974 } 0975 #endif 0976 0977 void KServiceTest::testAliasFor() 0978 { 0979 if (!KSycoca::isAvailable()) { 0980 QSKIP("ksycoca not available"); 0981 } 0982 KService::Ptr testapp = KService::serviceByDesktopName(QStringLiteral("org.kde.faketestapp")); 0983 QVERIFY(testapp); 0984 QCOMPARE(testapp->aliasFor(), QStringLiteral("org.kde.okular")); 0985 } 0986 0987 void KServiceTest::testMimeType() 0988 { 0989 if (!KSycoca::isAvailable()) { 0990 QSKIP("ksycoca not available"); 0991 } 0992 0993 KService::Ptr testapp = KService::serviceByDesktopName(QStringLiteral("org.kde.otherfakeapp")); 0994 QVERIFY(testapp); 0995 QCOMPARE(testapp->mimeTypes(), {QStringLiteral("application/pdf")}); 0996 } 0997 0998 void KServiceTest::testProtocols() 0999 { 1000 if (!KSycoca::isAvailable()) { 1001 QSKIP("ksycoca not available"); 1002 } 1003 1004 KService::Ptr testapp = KService::serviceByDesktopName(QStringLiteral("org.kde.otherfakeapp")); 1005 QVERIFY(testapp); 1006 QStringList expectedProtocols{QStringLiteral("http"), QStringLiteral("tel")}; 1007 QCOMPARE(testapp->supportedProtocols(), expectedProtocols); 1008 } 1009 1010 void KServiceTest::testServiceActionService() 1011 { 1012 if (!KSycoca::isAvailable()) { 1013 QSKIP("ksycoca not available"); 1014 } 1015 1016 const QString filePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("applications/org.kde.faketestapp.desktop")); 1017 QVERIFY(QFile::exists(filePath)); 1018 KService service(filePath); 1019 QVERIFY(service.isValid()); 1020 1021 const KServiceAction action = service.actions().first(); 1022 QCOMPARE(action.service()->property(QStringLiteral("DBusActivatable"), QMetaType::Bool).toBool(), true); 1023 QCOMPARE(action.service()->actions().size(), 2); 1024 }