File indexing completed on 2024-05-12 03:54:48
0001 /* 0002 SPDX-FileCopyrightText: 2014 Alex Richardson <arichardson.kde@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include <QJsonArray> 0008 #include <QJsonDocument> 0009 #include <QJsonParseError> 0010 #include <QPluginLoader> 0011 #include <QRegularExpression> 0012 #include <QStandardPaths> 0013 #include <QTest> 0014 0015 #include "kcoreaddons_debug.h" 0016 #include <kaboutdata.h> 0017 #include <kpluginmetadata.h> 0018 0019 #include <QLocale> 0020 #include <QLoggingCategory> 0021 0022 class LibraryPathRestorer 0023 { 0024 public: 0025 explicit LibraryPathRestorer(const QStringList &paths) 0026 : mPaths(paths) 0027 { 0028 } 0029 ~LibraryPathRestorer() 0030 { 0031 QCoreApplication::setLibraryPaths(mPaths); 0032 } 0033 0034 private: 0035 QStringList mPaths; 0036 }; 0037 0038 class KPluginMetaDataTest : public QObject 0039 { 0040 Q_OBJECT 0041 0042 bool m_canMessage = false; 0043 0044 Q_REQUIRED_RESULT bool doMessagesWork() 0045 { 0046 // Q_SKIP returns, but since this is called in multiple tests we want to return a bool so the caller can 0047 // return easily. 0048 auto internalCheck = [this] { 0049 // Make sure output is well formed AND generated. To that end we cannot run this test when any of the 0050 // overriding environment variables are set. 0051 // https://bugs.kde.org/show_bug.cgi?id=387006 0052 if (qEnvironmentVariableIsSet("QT_MESSAGE_PATTERN")) { 0053 QSKIP("QT_MESSAGE_PATTERN prevents warning expectations from matching"); 0054 } 0055 if (qEnvironmentVariableIsSet("QT_LOGGING_RULES")) { 0056 QSKIP("QT_LOGGING_RULES prevents warning expectations from matching"); 0057 } 0058 if (qEnvironmentVariableIsSet("QT_LOGGING_CONF")) { 0059 QSKIP("QT_LOGGING_CONF prevents warning expectations from matching"); 0060 } 0061 m_canMessage = true; 0062 // Ensure all frameworks output is enabled so the expectations can match. 0063 // qtlogging.ini may have disabled it but we can fix that because setFilterRules overrides the ini files. 0064 QLoggingCategory::setFilterRules(QStringLiteral("kf.*=true")); 0065 }; 0066 internalCheck(); 0067 return m_canMessage; 0068 } 0069 private Q_SLOTS: 0070 0071 void testFromPluginLoader() 0072 { 0073 QString location; 0074 location = QPluginLoader(QStringLiteral("namespace/jsonplugin_cmake_macro")).fileName(); 0075 QVERIFY2(!location.isEmpty(), "Could not find jsonplugin"); 0076 0077 // now that this file is translated we need to read it instead of hardcoding the contents here 0078 QString jsonLocation = QFINDTESTDATA("data/jsonplugin.json"); 0079 QVERIFY2(!jsonLocation.isEmpty(), "Could not find jsonplugin.json"); 0080 QFile jsonFile(jsonLocation); 0081 QVERIFY(jsonFile.open(QFile::ReadOnly)); 0082 QJsonParseError e; 0083 QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonFile.readAll(), &e); 0084 QCOMPARE(e.error, QJsonParseError::NoError); 0085 0086 location = QFileInfo(location).absoluteFilePath(); 0087 0088 KPluginMetaData fromQPluginLoader(QPluginLoader(QStringLiteral("namespace/jsonplugin_cmake_macro"))); 0089 KPluginMetaData fromFullPath(location); 0090 KPluginMetaData fromRelativePath(QStringLiteral("namespace/jsonplugin_cmake_macro")); 0091 KPluginMetaData fromRawData(jsonDoc.object(), location); 0092 0093 auto description = QStringLiteral("This is a plugin"); 0094 0095 QVERIFY(fromQPluginLoader.isValid()); 0096 QCOMPARE(fromQPluginLoader.description(), description); 0097 QVERIFY(fromFullPath.isValid()); 0098 QCOMPARE(fromFullPath.description(), description); 0099 QVERIFY(fromRelativePath.isValid()); 0100 QCOMPARE(fromRelativePath.description(), description); 0101 QVERIFY(fromRawData.isValid()); 0102 QCOMPARE(fromRawData.description(), description); 0103 0104 // check operator== 0105 QCOMPARE(fromRawData, fromRawData); 0106 QCOMPARE(fromQPluginLoader, fromQPluginLoader); 0107 QCOMPARE(fromFullPath, fromFullPath); 0108 0109 QCOMPARE(fromQPluginLoader, fromFullPath); 0110 QCOMPARE(fromQPluginLoader, fromRawData); 0111 0112 QCOMPARE(fromFullPath, fromQPluginLoader); 0113 QCOMPARE(fromFullPath, fromRawData); 0114 0115 QCOMPARE(fromRawData, fromQPluginLoader); 0116 QCOMPARE(fromRawData, fromFullPath); 0117 0118 QVERIFY(!KPluginMetaData(QPluginLoader(QStringLiteral("doesnotexist"))).isValid()); 0119 QVERIFY(!KPluginMetaData(QJsonObject(), QString()).isValid()); 0120 } 0121 0122 void testAllKeys() 0123 { 0124 QJsonParseError e; 0125 QJsonObject jo = QJsonDocument::fromJson( 0126 "{\n" 0127 " \"KPlugin\": {\n" 0128 " \"Name\": \"Date and Time\",\n" 0129 " \"Description\": \"Date and time by timezone\",\n" 0130 " \"Icon\": \"preferences-system-time\",\n" 0131 " \"Authors\": { \"Name\": \"Aaron Seigo\", \"Email\": \"aseigo@kde.org\" },\n" 0132 " \"Translators\": { \"Name\": \"No One\", \"Email\": \"no.one@kde.org\" },\n" 0133 " \"OtherContributors\": { \"Name\": \"No One\", \"Email\": \"no.one@kde.org\" },\n" 0134 " \"Category\": \"Date and Time\",\n" 0135 " \"EnabledByDefault\": \"true\",\n" 0136 " \"ExtraInformation\": \"Something else\",\n" 0137 " \"License\": \"LGPL\",\n" 0138 " \"Copyright\": \"(c) Alex Richardson 2015\",\n" 0139 " \"Id\": \"time\",\n" 0140 " \"Version\": \"1.0\",\n" 0141 " \"Website\": \"https://plasma.kde.org/\",\n" 0142 " \"MimeTypes\": [ \"image/png\" ]\n" 0143 " }\n}\n", 0144 &e) 0145 .object(); 0146 QCOMPARE(e.error, QJsonParseError::NoError); 0147 KPluginMetaData m(jo, QString()); 0148 QVERIFY(m.isValid()); 0149 QCOMPARE(m.pluginId(), QStringLiteral("time")); 0150 QCOMPARE(m.name(), QStringLiteral("Date and Time")); 0151 QCOMPARE(m.description(), QStringLiteral("Date and time by timezone")); 0152 QCOMPARE(m.iconName(), QStringLiteral("preferences-system-time")); 0153 QCOMPARE(m.category(), QStringLiteral("Date and Time")); 0154 QCOMPARE(m.authors().size(), 1); 0155 QCOMPARE(m.authors().constFirst().name(), QStringLiteral("Aaron Seigo")); 0156 QCOMPARE(m.authors().constFirst().emailAddress(), QStringLiteral("aseigo@kde.org")); 0157 QCOMPARE(m.translators().size(), 1); 0158 QCOMPARE(m.translators().constFirst().name(), QStringLiteral("No One")); 0159 QCOMPARE(m.translators().constFirst().emailAddress(), QStringLiteral("no.one@kde.org")); 0160 QCOMPARE(m.otherContributors().size(), 1); 0161 QCOMPARE(m.otherContributors().constFirst().name(), QStringLiteral("No One")); 0162 QCOMPARE(m.otherContributors().constFirst().emailAddress(), QStringLiteral("no.one@kde.org")); 0163 QVERIFY(m.isEnabledByDefault()); 0164 QCOMPARE(m.license(), QStringLiteral("LGPL")); 0165 QCOMPARE(m.copyrightText(), QStringLiteral("(c) Alex Richardson 2015")); 0166 QCOMPARE(m.version(), QStringLiteral("1.0")); 0167 QCOMPARE(m.website(), QStringLiteral("https://plasma.kde.org/")); 0168 QCOMPARE(m.mimeTypes(), QStringList(QStringLiteral("image/png"))); 0169 } 0170 0171 void testTranslations() 0172 { 0173 QJsonParseError e; 0174 QJsonObject jo = QJsonDocument::fromJson( 0175 "{ \"KPlugin\": {\n" 0176 "\"Name\": \"Name\",\n" 0177 "\"Name[de]\": \"Name (de)\",\n" 0178 "\"Name[de_DE]\": \"Name (de_DE)\",\n" 0179 "\"Description\": \"Description\",\n" 0180 "\"Description[de]\": \"Beschreibung (de)\",\n" 0181 "\"Description[de_DE]\": \"Beschreibung (de_DE)\"\n" 0182 "}\n}", 0183 &e) 0184 .object(); 0185 KPluginMetaData m(jo, QString()); 0186 QLocale::setDefault(QLocale::c()); 0187 QCOMPARE(m.name(), QStringLiteral("Name")); 0188 QCOMPARE(m.description(), QStringLiteral("Description")); 0189 0190 QLocale::setDefault(QLocale(QStringLiteral("de_DE"))); 0191 QCOMPARE(m.name(), QStringLiteral("Name (de_DE)")); 0192 QCOMPARE(m.description(), QStringLiteral("Beschreibung (de_DE)")); 0193 0194 QLocale::setDefault(QLocale(QStringLiteral("de_CH"))); 0195 QCOMPARE(m.name(), QStringLiteral("Name (de)")); 0196 QCOMPARE(m.description(), QStringLiteral("Beschreibung (de)")); 0197 0198 QLocale::setDefault(QLocale(QStringLiteral("fr_FR"))); 0199 QCOMPARE(m.name(), QStringLiteral("Name")); 0200 QCOMPARE(m.description(), QStringLiteral("Description")); 0201 } 0202 0203 void testReadStringList() 0204 { 0205 if (!doMessagesWork()) { 0206 return; 0207 } 0208 QJsonParseError e; 0209 QJsonObject jo = QJsonDocument::fromJson( 0210 "{\n" 0211 "\"String\": \"foo\",\n" 0212 "\"OneArrayEntry\": [ \"foo\" ],\n" 0213 "\"Bool\": true,\n" // make sure booleans are accepted 0214 "\"QuotedBool\": \"true\",\n" // make sure booleans are accepted 0215 "\"Number\": 12345,\n" // number should also work 0216 "\"QuotedNumber\": \"12345\",\n" // number should also work 0217 "\"EmptyArray\": [],\n" 0218 "\"NumberArray\": [1, 2, 3],\n" 0219 "\"BoolArray\": [true, false, true],\n" 0220 "\"StringArray\": [\"foo\", \"bar\"],\n" 0221 "\"Null\": null,\n" // should return empty list 0222 "\"QuotedNull\": \"null\",\n" // this is okay, it is a string 0223 "\"ArrayWithNull\": [ \"foo\", null, \"bar\"],\n" // TODO: null is converted to empty string, is this okay? 0224 "\"Object\": { \"foo\": \"bar\" }\n" // should return empty list 0225 "}", 0226 &e) 0227 .object(); 0228 QCOMPARE(e.error, QJsonParseError::NoError); 0229 QTest::ignoreMessage(QtWarningMsg, QRegularExpression(QStringLiteral("Expected JSON property "))); 0230 KPluginMetaData data(jo, QStringLiteral("test")); 0231 QCOMPARE(data.value(QStringLiteral("String"), QStringList()), QStringList(QStringLiteral("foo"))); 0232 QCOMPARE(data.value(QStringLiteral("OneArrayEntry"), QStringList()), QStringList(QStringLiteral("foo"))); 0233 QCOMPARE(data.value(QStringLiteral("Bool"), QStringList()), QStringList(QStringLiteral("true"))); 0234 QCOMPARE(data.value(QStringLiteral("QuotedBool"), QStringList()), QStringList(QStringLiteral("true"))); 0235 QCOMPARE(data.value(QStringLiteral("Number"), QStringList()), QStringList(QStringLiteral("12345"))); 0236 QCOMPARE(data.value(QStringLiteral("QuotedNumber"), QStringList()), QStringList(QStringLiteral("12345"))); 0237 QCOMPARE(data.value(QStringLiteral("EmptyArray"), QStringList()), QStringList()); 0238 QCOMPARE(data.value(QStringLiteral("NumberArray"), QStringList()), QStringList() << QStringLiteral("1") << QStringLiteral("2") << QStringLiteral("3")); 0239 QCOMPARE(data.value(QStringLiteral("BoolArray"), QStringList()), 0240 QStringList() << QStringLiteral("true") << QStringLiteral("false") << QStringLiteral("true")); 0241 QCOMPARE(data.value(QStringLiteral("StringArray"), QStringList()), QStringList() << QStringLiteral("foo") << QStringLiteral("bar")); 0242 QCOMPARE(data.value(QStringLiteral("Null"), QStringList()), QStringList()); 0243 QCOMPARE(data.value(QStringLiteral("QuotedNull"), QStringList()), QStringList(QStringLiteral("null"))); 0244 QCOMPARE(data.value(QStringLiteral("ArrayWithNull"), QStringList()), QStringList() << QStringLiteral("foo") << QString() << QStringLiteral("bar")); 0245 QCOMPARE(data.value(QStringLiteral("Object"), QStringList()), QStringList()); 0246 } 0247 0248 void testJSONMetadata() 0249 { 0250 const QString inputPath = QFINDTESTDATA("data/testmetadata.json"); 0251 KPluginMetaData md = KPluginMetaData::fromJsonFile(inputPath); 0252 QVERIFY(md.isValid()); 0253 QCOMPARE(md.name(), QStringLiteral("Test")); 0254 0255 QCOMPARE(md.value(QStringLiteral("X-Plasma-MainScript")), QStringLiteral("ui/main.qml")); 0256 QJsonArray expected; 0257 expected.append(QStringLiteral("Export")); 0258 QCOMPARE(md.rawData().value(QStringLiteral("X-Purpose-PluginTypes")).toArray(), expected); 0259 QCOMPARE(md.value(QStringLiteral("SomeInt"), 24), 42); 0260 QCOMPARE(md.value(QStringLiteral("SomeIntAsString"), 24), 42); 0261 QCOMPARE(md.value(QStringLiteral("SomeStringNotAInt"), 24), 24); 0262 QCOMPARE(md.value(QStringLiteral("DoesNotExist"), 24), 24); 0263 0264 QVERIFY(md.value(QStringLiteral("SomeBool"), false)); 0265 QVERIFY(!md.value(QStringLiteral("SomeBoolThatIsFalse"), true)); 0266 QVERIFY(md.value(QStringLiteral("SomeBoolAsString"), false)); 0267 QVERIFY(md.value(QStringLiteral("DoesNotExist"), true)); 0268 } 0269 0270 void testPathIsAbsolute_data() 0271 { 0272 QTest::addColumn<QString>("inputAbsolute"); 0273 QTest::addColumn<QString>("pluginPath"); 0274 0275 // But for the .json based plugin both are the same. 0276 QTest::newRow("json") << QFINDTESTDATA("data/testmetadata.json") << QFINDTESTDATA("data/testmetadata.json"); 0277 // And also for the library with embedded JSON metadata. 0278 QPluginLoader shlibLoader(QCoreApplication::applicationDirPath() + QStringLiteral("/namespace/jsonplugin_cmake_macro")); 0279 QVERIFY2(!shlibLoader.fileName().isEmpty(), "Could not find jsonplugin_cmake_macro"); 0280 QString shlibPath = QFileInfo(shlibLoader.fileName()).absoluteFilePath(); 0281 QTest::newRow("library") << shlibPath << shlibPath; 0282 } 0283 0284 void testPathIsAbsolute() 0285 { 0286 // Test that the fileName() accessor always returns an absolute path if it was used. 0287 QFETCH(QString, inputAbsolute); 0288 QVERIFY2(QDir::isAbsolutePath(inputAbsolute), qPrintable(inputAbsolute)); 0289 QFETCH(QString, pluginPath); 0290 0291 const auto createMetaData = [](const QString &path) { 0292 if (path.endsWith(QLatin1String(".json"))) { 0293 return KPluginMetaData::fromJsonFile(path); 0294 } else { 0295 return KPluginMetaData(path); 0296 } 0297 }; 0298 0299 KPluginMetaData mdAbsolute = createMetaData(inputAbsolute); 0300 QVERIFY(mdAbsolute.isValid()); 0301 QCOMPARE(mdAbsolute.fileName(), pluginPath); 0302 0303 // All files that have been opened should be stored as absolute paths. 0304 QString inputRelative; 0305 if (QLibrary::isLibrary(inputAbsolute)) { 0306 // We have a plugin without namespace, with the code path below we would end up with 0307 // a path relative to the PWD, but we want to check a path relative to the plugin dir. 0308 // Because of that we simply use the baseName of the file. 0309 inputRelative = QStringLiteral("namespace/") + QFileInfo(inputAbsolute).baseName(); 0310 } else { 0311 inputRelative = QDir::current().relativeFilePath(inputAbsolute); 0312 } 0313 QVERIFY2(QDir::isRelativePath(inputRelative), qPrintable(inputRelative)); 0314 KPluginMetaData mdRelative = createMetaData(inputRelative); 0315 QVERIFY(mdRelative.isValid()); 0316 QCOMPARE(mdRelative.fileName(), pluginPath); 0317 0318 // Check that creating it with the parsed JSON object and a path keeps the path unchanged 0319 const QJsonObject json = mdAbsolute.rawData(); 0320 QString pluginRelative = QDir::current().relativeFilePath(pluginPath); 0321 QVERIFY2(QDir::isRelativePath(pluginRelative), qPrintable(pluginRelative)); 0322 0323 // We should not be normalizing files that have not been openened, so both arguments should be unchanged. 0324 KPluginMetaData mdFromJson1(json, pluginRelative); 0325 KPluginMetaData mdFromJson2(json, inputRelative); 0326 QCOMPARE(mdFromJson1.fileName(), pluginRelative); 0327 QCOMPARE(mdFromJson2.fileName(), inputRelative); 0328 } 0329 0330 void testFindPlugins() 0331 { 0332 auto sortPlugins = [](const KPluginMetaData &a, const KPluginMetaData &b) { 0333 return a.pluginId() < b.pluginId(); 0334 }; 0335 // it should find jsonplugin and jsonplugin2 since unversionedplugin does not have any meta data 0336 auto plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace")); 0337 std::sort(plugins.begin(), plugins.end(), sortPlugins); 0338 QCOMPARE(plugins.size(), 2); 0339 QCOMPARE(plugins[0].pluginId(), QStringLiteral("jsonplugin_cmake_macro")); // ID is not the filename, it is set in the JSON metadata 0340 QCOMPARE(plugins[0].description(), QStringLiteral("This is a plugin")); 0341 QCOMPARE(plugins[1].pluginId(), QStringLiteral("qtplugin")); // ID is not the filename, it is set in the JSON metadata 0342 0343 // filter accepts none 0344 plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), [](const KPluginMetaData &) { 0345 return false; 0346 }); 0347 std::sort(plugins.begin(), plugins.end(), sortPlugins); 0348 QCOMPARE(plugins.size(), 0); 0349 0350 // filter accepts all 0351 plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), [](const KPluginMetaData &) { 0352 return true; 0353 }); 0354 std::sort(plugins.begin(), plugins.end(), sortPlugins); 0355 QCOMPARE(plugins.size(), 2); 0356 0357 // mimetype filter. Only one match, jsonplugin2 is specific to text/html. 0358 auto supportTextPlain = [](const KPluginMetaData &metaData) { 0359 return metaData.supportsMimeType(QStringLiteral("text/plain")); 0360 }; 0361 plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), supportTextPlain); 0362 QCOMPARE(plugins.size(), 1); 0363 QCOMPARE(plugins[0].pluginId(), QStringLiteral("jsonplugin_cmake_macro")); 0364 0365 // mimetype filter. Two matches, both support text/html, via inheritance. 0366 auto supportTextHtml = [](const KPluginMetaData &metaData) { 0367 return metaData.supportsMimeType(QStringLiteral("text/html")); 0368 }; 0369 plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), supportTextHtml); 0370 std::sort(plugins.begin(), plugins.end(), sortPlugins); 0371 QCOMPARE(plugins.size(), 2); 0372 QCOMPARE(plugins[0].pluginId(), QStringLiteral("jsonplugin_cmake_macro")); 0373 QCOMPARE(plugins[1].pluginId(), QStringLiteral("qtplugin")); 0374 0375 // mimetype filter with invalid mimetype 0376 auto supportDoesNotExist = [](const KPluginMetaData &metaData) { 0377 return metaData.supportsMimeType(QStringLiteral("does/not/exist")); 0378 }; 0379 plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), supportDoesNotExist); 0380 QCOMPARE(plugins.size(), 0); 0381 0382 // by plugin invalid id 0383 KPluginMetaData plugin = KPluginMetaData::findPluginById(QStringLiteral("namespace"), QStringLiteral("invalidid")); 0384 QVERIFY(!plugin.isValid()); 0385 0386 // absolute path, no filter 0387 plugins = KPluginMetaData::findPlugins(QCoreApplication::applicationDirPath() + QStringLiteral("/namespace")); 0388 std::sort(plugins.begin(), plugins.end(), sortPlugins); 0389 QCOMPARE(plugins.size(), 2); 0390 QCOMPARE(plugins[0].pluginId(), QStringLiteral("jsonplugin_cmake_macro")); 0391 QCOMPARE(plugins[1].pluginId(), QStringLiteral("qtplugin")); 0392 0393 // This plugin has no explicit pluginId and will fall back to basename of file 0394 const KPluginMetaData validPlugin = KPluginMetaData::findPluginById(QStringLiteral("namespace"), QStringLiteral("jsonplugin_cmake_macro")); 0395 QVERIFY(validPlugin.isValid()); 0396 QCOMPARE(plugins[0].pluginId(), QStringLiteral("jsonplugin_cmake_macro")); 0397 } 0398 0399 void testStaticPlugins() 0400 { 0401 QCOMPARE(QPluginLoader::staticPlugins().count(), 0); 0402 0403 const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("staticnamespace")); 0404 QCOMPARE(plugins.count(), 1); 0405 0406 QCOMPARE(plugins.first().description(), QStringLiteral("This is a plugin")); 0407 QCOMPARE(plugins.first().fileName(), QStringLiteral("staticnamespace/static_jsonplugin_cmake_macro")); 0408 } 0409 0410 void testPluginsWithoutMetaData() 0411 { 0412 KPluginMetaData emptyMetaData(QStringLiteral("namespace/pluginwithoutmetadata"), KPluginMetaData::AllowEmptyMetaData); 0413 QVERIFY(emptyMetaData.isValid()); 0414 QCOMPARE(emptyMetaData.pluginId(), QStringLiteral("pluginwithoutmetadata")); 0415 0416 const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), {}, KPluginMetaData::AllowEmptyMetaData); 0417 QCOMPARE(plugins.count(), 3); 0418 for (auto plugin : plugins) { 0419 QVERIFY(plugin.isValid()); 0420 if (plugin.pluginId() == QLatin1String("pluginwithoutmetadata")) { 0421 QVERIFY(plugin.rawData().isEmpty()); 0422 } else if (plugin.pluginId() == QLatin1String("jsonplugin_cmake_macro") || plugin.pluginId() == QLatin1String("qtplugin")) { 0423 QVERIFY(!plugin.rawData().isEmpty()); 0424 } else { 0425 QVERIFY2(false, "should not be reachable"); 0426 } 0427 } 0428 const auto pluginInvalid = KPluginMetaData::findPluginById(QStringLiteral("namespace"), QStringLiteral("pluginwithoutmetadata")); 0429 const auto pluginValid = KPluginMetaData::findPluginById(QStringLiteral("namespace"), // 0430 QStringLiteral("pluginwithoutmetadata"), 0431 KPluginMetaData::AllowEmptyMetaData); 0432 QVERIFY(!pluginInvalid.isValid()); 0433 QVERIFY(pluginValid.isValid()); 0434 } 0435 0436 void testStaticPluginsWithoutMetadata() 0437 { 0438 QVERIFY(KPluginMetaData::findPlugins(QStringLiteral("staticnamespace3")).isEmpty()); 0439 const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("staticnamespace3"), {}, KPluginMetaData::AllowEmptyMetaData); 0440 QCOMPARE(plugins.count(), 1); 0441 QVERIFY(plugins.first().isValid()); 0442 QCOMPARE(plugins.first().pluginId(), QStringLiteral("static_plugin_without_metadata")); 0443 } 0444 0445 void testReverseDomainNotationPluginId() 0446 { 0447 KPluginMetaData data(QStringLiteral("org.kde.test")); 0448 QVERIFY(data.isValid()); 0449 QCOMPARE(data.pluginId(), QStringLiteral("org.kde.test")); 0450 } 0451 0452 // We can't use the plain pluginId as a classname, thus check if the replacement compiles and the original identifies it used for lookup 0453 void testReverseDomanNotationStaticPlugin() 0454 { 0455 KPluginMetaData data = KPluginMetaData::findPluginById(QStringLiteral("rdnstatic"), QStringLiteral("org.kde.test-staticplugin")); 0456 QVERIFY(data.isValid()); 0457 QCOMPARE(data.pluginId(), QStringLiteral("org.kde.test-staticplugin")); 0458 } 0459 0460 void testFindingPluginInAppDirFirst() 0461 { 0462 const QString originalPluginPath = QPluginLoader(QStringLiteral("namespace/jsonplugin_cmake_macro")).fileName(); 0463 const QString pluginFileName = QFileInfo(originalPluginPath).fileName(); 0464 const QString pluginNamespace = QStringLiteral("somepluginnamespace"); 0465 const QString pluginAppDir = QCoreApplication::applicationDirPath() + QLatin1Char('/') + pluginNamespace; 0466 QDir(pluginAppDir).mkpath(QStringLiteral(".")); 0467 const QString pluginAppPath = pluginAppDir + QLatin1Char('/') + pluginFileName; 0468 QFile::remove(pluginAppPath); 0469 0470 QVERIFY(QFile::copy(originalPluginPath, pluginAppPath)); 0471 0472 QTemporaryDir temp; 0473 QVERIFY(temp.isValid()); 0474 QDir dir(temp.path()); 0475 QVERIFY(dir.mkdir(pluginNamespace)); 0476 QVERIFY(dir.cd(pluginNamespace)); 0477 0478 const QString pluginInNamespacePath = dir.absoluteFilePath(pluginFileName); 0479 QVERIFY(QFile::copy(originalPluginPath, pluginInNamespacePath)); 0480 0481 LibraryPathRestorer restorer(QCoreApplication::libraryPaths()); 0482 QCoreApplication::setLibraryPaths(QStringList() << temp.path()); 0483 0484 // Our plugin in the applicationDirPath should come first 0485 const QString relativePathWithNamespace = QStringLiteral("somepluginnamespace/jsonplugin_cmake_macro"); 0486 KPluginMetaData data(relativePathWithNamespace); 0487 QVERIFY(data.isValid()); 0488 QCOMPARE(data.fileName(), pluginAppPath); 0489 0490 // The other one must be valid 0491 QVERIFY(KPluginMetaData(pluginInNamespacePath).isValid()); 0492 // And after removing the plugin in the applicationDirPath, it should be found 0493 QVERIFY(QFile::remove(pluginAppPath)); 0494 QCOMPARE(KPluginMetaData(relativePathWithNamespace).fileName(), pluginInNamespacePath); 0495 } 0496 0497 void testMetaDataQDebugOperator() 0498 { 0499 const auto list = KPluginMetaData::findPlugins(QStringLiteral("namespace")); 0500 qDebug() << list.first(); 0501 qDebug() << list; 0502 qDebug() << (QList<KPluginMetaData>() << list << list << list); 0503 } 0504 void benchmarkFindPlugins() 0505 { 0506 QSKIP("Skipped by default"); 0507 int loopIterations = 10; 0508 QBENCHMARK { 0509 for (int i = 0; i < loopIterations; ++i) { 0510 const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), {}, KPluginMetaData::AllowEmptyMetaData); 0511 Q_UNUSED(plugins) 0512 } 0513 } 0514 QList<KPluginMetaData> plugins = 0515 KPluginMetaData::findPlugins(QStringLiteral("namespace"), {}, KPluginMetaData::AllowEmptyMetaData | KPluginMetaData::CacheMetaData); 0516 QBENCHMARK { 0517 for (int i = 0; i < loopIterations; ++i) { 0518 plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), {}, KPluginMetaData::AllowEmptyMetaData | KPluginMetaData::CacheMetaData); 0519 } 0520 } 0521 } 0522 }; 0523 0524 QTEST_MAIN(KPluginMetaDataTest) 0525 0526 #include "kpluginmetadatatest.moc"