File indexing completed on 2024-09-15 11:55:36
0001 /* 0002 SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include <QFileInfo> 0008 #include <QTest> 0009 0010 #include "kcoreaddons_debug.h" 0011 #include <kpluginloader.h> 0012 #include <kpluginmetadata.h> 0013 0014 class LibraryPathRestorer 0015 { 0016 public: 0017 explicit LibraryPathRestorer(const QStringList &paths) 0018 : mPaths(paths) 0019 { 0020 } 0021 ~LibraryPathRestorer() 0022 { 0023 QCoreApplication::setLibraryPaths(mPaths); 0024 } 0025 0026 private: 0027 QStringList mPaths; 0028 }; 0029 0030 class KPluginLoaderTest : public QObject 0031 { 0032 Q_OBJECT 0033 0034 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 86) 0035 private Q_SLOTS: 0036 void testFindPlugin_missing() 0037 { 0038 const QString location = KPluginLoader::findPlugin(QStringLiteral("idonotexist")); 0039 QVERIFY2(location.isEmpty(), qPrintable(location)); 0040 } 0041 0042 void testFindPlugin() 0043 { 0044 const QString location = KPluginLoader::findPlugin(QStringLiteral("jsonplugin")); 0045 QVERIFY2(!location.isEmpty(), qPrintable(location)); 0046 } 0047 0048 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 84) 0049 void testPluginVersion() 0050 { 0051 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 0052 QCOMPARE(vplugin.pluginVersion(), quint32(5)); 0053 0054 KPluginLoader vplugin2(QStringLiteral("versionedplugin")); 0055 QCOMPARE(vplugin2.pluginVersion(), quint32(5)); 0056 0057 KPluginLoader uplugin(QStringLiteral("unversionedplugin")); 0058 QCOMPARE(uplugin.pluginVersion(), quint32(-1)); 0059 0060 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 0061 QCOMPARE(jplugin.pluginVersion(), quint32(-1)); 0062 0063 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 0064 QCOMPARE(eplugin.pluginVersion(), quint32(-1)); 0065 0066 KPluginLoader noplugin(QStringLiteral("idonotexist")); 0067 QCOMPARE(noplugin.pluginVersion(), quint32(-1)); 0068 } 0069 #endif 0070 0071 void testPluginName() 0072 { 0073 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 0074 QCOMPARE(vplugin.pluginName(), QString::fromLatin1("versionedplugin")); 0075 0076 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 0077 QCOMPARE(jplugin.pluginName(), QString::fromLatin1("jsonplugin")); 0078 0079 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 0080 QVERIFY2(eplugin.pluginName().isEmpty(), qPrintable(eplugin.pluginName())); 0081 0082 KPluginLoader noplugin(QStringLiteral("idonotexist")); 0083 QCOMPARE(noplugin.pluginName(), QString::fromLatin1("idonotexist")); 0084 } 0085 0086 void testFactory() 0087 { 0088 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 0089 QVERIFY(vplugin.factory()); 0090 0091 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 0092 QVERIFY(jplugin.factory()); 0093 0094 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 0095 QVERIFY(!eplugin.factory()); 0096 0097 KPluginLoader noplugin(QStringLiteral("idonotexist")); 0098 QVERIFY(!noplugin.factory()); 0099 } 0100 0101 void testErrorString() 0102 { 0103 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 0104 QCOMPARE(eplugin.errorString(), QString::fromLatin1("there was an error")); 0105 } 0106 0107 void testFileName() 0108 { 0109 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 0110 QCOMPARE(QFileInfo(vplugin.fileName()).canonicalFilePath(), QFileInfo(QStringLiteral(VERSIONEDPLUGIN_FILE)).canonicalFilePath()); 0111 0112 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 0113 QCOMPARE(QFileInfo(jplugin.fileName()).canonicalFilePath(), QFileInfo(QStringLiteral(JSONPLUGIN_FILE)).canonicalFilePath()); 0114 0115 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 0116 QVERIFY2(eplugin.fileName().isEmpty(), qPrintable(eplugin.fileName())); 0117 0118 KPluginLoader noplugin(QStringLiteral("idonotexist")); 0119 QVERIFY2(noplugin.fileName().isEmpty(), qPrintable(noplugin.fileName())); 0120 } 0121 0122 void testInstance() 0123 { 0124 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 0125 QVERIFY(vplugin.instance()); 0126 0127 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 0128 QVERIFY(jplugin.instance()); 0129 0130 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 0131 QVERIFY(!eplugin.instance()); 0132 0133 KPluginLoader noplugin(QStringLiteral("idonotexist")); 0134 QVERIFY(!noplugin.instance()); 0135 } 0136 0137 void testIsLoaded() 0138 { 0139 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 0140 QVERIFY(!vplugin.isLoaded()); 0141 QVERIFY(vplugin.load()); 0142 QVERIFY(vplugin.isLoaded()); 0143 0144 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 0145 QVERIFY(!jplugin.isLoaded()); 0146 QVERIFY(jplugin.load()); 0147 QVERIFY(jplugin.isLoaded()); 0148 0149 KPluginLoader aplugin(QStringLiteral("alwaysunloadplugin")); 0150 QVERIFY(!aplugin.isLoaded()); 0151 QVERIFY(aplugin.load()); 0152 QVERIFY(aplugin.isLoaded()); 0153 if (aplugin.unload()) { 0154 QVERIFY(!aplugin.isLoaded()); 0155 } else { 0156 qCDebug(KCOREADDONS_DEBUG) << "Could not unload alwaysunloadplugin:" << aplugin.errorString(); 0157 } 0158 0159 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 0160 QVERIFY(!eplugin.isLoaded()); 0161 QVERIFY(!eplugin.load()); 0162 QVERIFY(!eplugin.isLoaded()); 0163 0164 KPluginLoader noplugin(QStringLiteral("idonotexist")); 0165 QVERIFY(!noplugin.isLoaded()); 0166 QVERIFY(!noplugin.load()); 0167 QVERIFY(!noplugin.isLoaded()); 0168 } 0169 0170 void testLoad() 0171 { 0172 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 0173 QVERIFY(vplugin.load()); 0174 0175 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 0176 QVERIFY(jplugin.load()); 0177 0178 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 0179 QVERIFY(!eplugin.load()); 0180 0181 KPluginLoader noplugin(QStringLiteral("idonotexist")); 0182 QVERIFY(!noplugin.load()); 0183 } 0184 0185 void testLoadHints() 0186 { 0187 KPluginLoader aplugin(QStringLiteral("alwaysunloadplugin")); 0188 QCOMPARE(aplugin.loadHints(), QLibrary::PreventUnloadHint); 0189 aplugin.setLoadHints(QLibrary::ResolveAllSymbolsHint); 0190 // setLoadHints merges in this scenario in the patch collection [1] but not in raw Qt5 0191 // [1] https://invent.kde.org/qt/qt/qtbase/-/merge_requests/285 0192 QVERIFY(aplugin.loadHints() == (QLibrary::ResolveAllSymbolsHint | QLibrary::PreventUnloadHint) 0193 || aplugin.loadHints() == QLibrary::ResolveAllSymbolsHint); 0194 } 0195 0196 void testMetaData() 0197 { 0198 KPluginLoader aplugin(QStringLiteral("alwaysunloadplugin")); 0199 QJsonObject ametadata = aplugin.metaData(); 0200 QVERIFY(!ametadata.isEmpty()); 0201 QVERIFY(ametadata.keys().contains(QLatin1String("IID"))); 0202 QJsonValue ametadata_metadata = ametadata.value(QStringLiteral("MetaData")); 0203 QVERIFY(ametadata_metadata.toObject().isEmpty()); 0204 QVERIFY(!aplugin.isLoaded()); // didn't load anything 0205 0206 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 0207 QJsonObject jmetadata = jplugin.metaData(); 0208 QVERIFY(!jmetadata.isEmpty()); 0209 QJsonValue jmetadata_metadata = jmetadata.value(QStringLiteral("MetaData")); 0210 QVERIFY(jmetadata_metadata.isObject()); 0211 QJsonObject jmetadata_obj = jmetadata_metadata.toObject(); 0212 QVERIFY(!jmetadata_obj.isEmpty()); 0213 QJsonValue comment = jmetadata_obj.value(QStringLiteral("KPlugin")).toObject().value(QStringLiteral("Description")); 0214 QVERIFY(comment.isString()); 0215 QCOMPARE(comment.toString(), QString::fromLatin1("This is a plugin")); 0216 0217 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 0218 QVERIFY(eplugin.metaData().isEmpty()); 0219 0220 KPluginLoader noplugin(QStringLiteral("idonotexist")); 0221 QVERIFY(noplugin.metaData().isEmpty()); 0222 } 0223 0224 void testUnload() 0225 { 0226 KPluginLoader aplugin(QStringLiteral("alwaysunloadplugin")); 0227 QVERIFY(aplugin.load()); 0228 // may need QEXPECT_FAIL on some platforms... 0229 QVERIFY(aplugin.unload()); 0230 } 0231 0232 void testInstantiatePlugins() 0233 { 0234 const QString plugin1Path = KPluginLoader::findPlugin(QStringLiteral("jsonplugin")); 0235 QVERIFY2(!plugin1Path.isEmpty(), qPrintable(plugin1Path)); 0236 const QString plugin2Path = KPluginLoader::findPlugin(QStringLiteral("unversionedplugin")); 0237 QVERIFY2(!plugin2Path.isEmpty(), qPrintable(plugin2Path)); 0238 const QString plugin3Path = KPluginLoader::findPlugin(QStringLiteral("jsonplugin2")); 0239 QVERIFY2(!plugin3Path.isEmpty(), qPrintable(plugin3Path)); 0240 0241 QTemporaryDir temp; 0242 QVERIFY(temp.isValid()); 0243 QDir dir(temp.path()); 0244 QVERIFY2(QFile::copy(plugin1Path, dir.absoluteFilePath(QFileInfo(plugin1Path).fileName())), 0245 qPrintable(dir.absoluteFilePath(QFileInfo(plugin1Path).fileName()))); 0246 QVERIFY2(QFile::copy(plugin2Path, dir.absoluteFilePath(QFileInfo(plugin2Path).fileName())), 0247 qPrintable(dir.absoluteFilePath(QFileInfo(plugin2Path).fileName()))); 0248 QVERIFY2(QFile::copy(plugin3Path, dir.absoluteFilePath(QFileInfo(plugin3Path).fileName())), 0249 qPrintable(dir.absoluteFilePath(QFileInfo(plugin3Path).fileName()))); 0250 0251 // only jsonplugin, since unversionedplugin has no json metadata 0252 QList<QObject *> plugins = KPluginLoader::instantiatePlugins(temp.path()); 0253 QCOMPARE(plugins.size(), 2); 0254 QStringList classNames = QStringList() << QString::fromLatin1(plugins[0]->metaObject()->className()) 0255 << QString::fromLatin1(plugins[1]->metaObject()->className()); 0256 classNames.sort(); 0257 QCOMPARE(classNames[0], QStringLiteral("jsonplugin2")); 0258 QCOMPARE(classNames[1], QStringLiteral("jsonpluginfa")); 0259 qDeleteAll(plugins); 0260 0261 // try filter 0262 plugins = KPluginLoader::instantiatePlugins(temp.path(), [](const KPluginMetaData &md) { 0263 return md.pluginId() == QLatin1String("jsonplugin"); 0264 }); 0265 QCOMPARE(plugins.size(), 1); 0266 QCOMPARE(plugins[0]->metaObject()->className(), "jsonpluginfa"); 0267 qDeleteAll(plugins); 0268 0269 plugins = KPluginLoader::instantiatePlugins(temp.path(), [](const KPluginMetaData &md) { 0270 return md.pluginId() == QLatin1String("unversionedplugin"); 0271 }); 0272 QCOMPARE(plugins.size(), 0); 0273 0274 plugins = KPluginLoader::instantiatePlugins(temp.path(), [](const KPluginMetaData &md) { 0275 return md.pluginId() == QLatin1String("foobar"); // ID does not match file name, is set in JSON 0276 }); 0277 QCOMPARE(plugins.size(), 1); 0278 QCOMPARE(plugins[0]->metaObject()->className(), "jsonplugin2"); 0279 qDeleteAll(plugins); 0280 0281 // check that parent gets set 0282 plugins = KPluginLoader::instantiatePlugins( 0283 temp.path(), 0284 [](const KPluginMetaData &) { 0285 return true; 0286 }, 0287 this); 0288 QCOMPARE(plugins.size(), 2); 0289 QCOMPARE(plugins[0]->parent(), this); 0290 QCOMPARE(plugins[1]->parent(), this); 0291 qDeleteAll(plugins); 0292 0293 const QString subDirName = dir.dirName(); 0294 QVERIFY(dir.cdUp()); // should now point to /tmp on Linux 0295 LibraryPathRestorer restorer(QCoreApplication::libraryPaths()); 0296 // instantiate using relative path 0297 // make sure library path is set up correctly 0298 QCoreApplication::setLibraryPaths(QStringList() << dir.absolutePath()); 0299 QVERIFY(!QDir::isAbsolutePath(subDirName)); 0300 plugins = KPluginLoader::instantiatePlugins(subDirName); 0301 QCOMPARE(plugins.size(), 2); 0302 classNames = QStringList() << QString::fromLatin1(plugins[0]->metaObject()->className()) << QString::fromLatin1(plugins[1]->metaObject()->className()); 0303 classNames.sort(); 0304 QCOMPARE(classNames[0], QStringLiteral("jsonplugin2")); 0305 QCOMPARE(classNames[1], QStringLiteral("jsonpluginfa")); 0306 qDeleteAll(plugins); 0307 } 0308 0309 void testForEachPlugin() 0310 { 0311 const QString jsonPluginSrc = KPluginLoader::findPlugin(QStringLiteral("jsonplugin")); 0312 QVERIFY2(!jsonPluginSrc.isEmpty(), qPrintable(jsonPluginSrc)); 0313 const QString unversionedPluginSrc = KPluginLoader::findPlugin(QStringLiteral("unversionedplugin")); 0314 QVERIFY2(!unversionedPluginSrc.isEmpty(), qPrintable(unversionedPluginSrc)); 0315 const QString jsonPlugin2Src = KPluginLoader::findPlugin(QStringLiteral("jsonplugin2")); 0316 QVERIFY2(!jsonPlugin2Src.isEmpty(), qPrintable(jsonPlugin2Src)); 0317 0318 QTemporaryDir temp; 0319 QVERIFY(temp.isValid()); 0320 QDir dir(temp.path()); 0321 QVERIFY(dir.mkdir(QStringLiteral("for-each-plugin"))); 0322 QVERIFY(dir.cd(QStringLiteral("for-each-plugin"))); 0323 const QString jsonPluginDest = dir.absoluteFilePath(QFileInfo(jsonPluginSrc).fileName()); 0324 QVERIFY2(QFile::copy(jsonPluginSrc, jsonPluginDest), qPrintable(jsonPluginDest)); 0325 const QString unversionedPluginDest = dir.absoluteFilePath(QFileInfo(unversionedPluginSrc).fileName()); 0326 QVERIFY2(QFile::copy(unversionedPluginSrc, unversionedPluginDest), qPrintable(unversionedPluginDest)); 0327 // copy jsonplugin2 to a "for-each-plugin" subdirectory in a different directory 0328 QTemporaryDir temp2; 0329 QVERIFY(temp2.isValid()); 0330 QDir dir2(temp2.path()); 0331 QVERIFY(dir2.mkdir(QStringLiteral("for-each-plugin"))); 0332 QVERIFY(dir2.cd(QStringLiteral("for-each-plugin"))); 0333 const QString jsonPlugin2Dest = dir2.absoluteFilePath(QFileInfo(jsonPlugin2Src).fileName()); 0334 QVERIFY2(QFile::copy(jsonPlugin2Src, jsonPlugin2Dest), qPrintable(jsonPlugin2Dest)); 0335 0336 QStringList foundPlugins; 0337 QStringList expectedPlugins; 0338 const auto addToFoundPlugins = [&](const QString &path) { 0339 QVERIFY(!path.isEmpty()); 0340 foundPlugins.append(path); 0341 }; 0342 0343 // test finding with absolute path 0344 expectedPlugins = QStringList() << jsonPluginDest << unversionedPluginDest; 0345 expectedPlugins.sort(); 0346 KPluginLoader::forEachPlugin(dir.path(), addToFoundPlugins); 0347 foundPlugins.sort(); 0348 QCOMPARE(foundPlugins, expectedPlugins); 0349 0350 expectedPlugins = QStringList() << jsonPlugin2Dest; 0351 expectedPlugins.sort(); 0352 foundPlugins.clear(); 0353 KPluginLoader::forEachPlugin(dir2.path(), addToFoundPlugins); 0354 foundPlugins.sort(); 0355 QCOMPARE(foundPlugins, expectedPlugins); 0356 0357 // now test relative paths 0358 0359 LibraryPathRestorer restorer(QCoreApplication::libraryPaths()); 0360 QCoreApplication::setLibraryPaths(QStringList() << temp.path()); 0361 expectedPlugins = QStringList() << jsonPluginDest << unversionedPluginDest; 0362 expectedPlugins.sort(); 0363 foundPlugins.clear(); 0364 KPluginLoader::forEachPlugin(QStringLiteral("for-each-plugin"), addToFoundPlugins); 0365 foundPlugins.sort(); 0366 QCOMPARE(foundPlugins, expectedPlugins); 0367 0368 QCoreApplication::setLibraryPaths(QStringList() << temp2.path()); 0369 expectedPlugins = QStringList() << jsonPlugin2Dest; 0370 expectedPlugins.sort(); 0371 foundPlugins.clear(); 0372 KPluginLoader::forEachPlugin(QStringLiteral("for-each-plugin"), addToFoundPlugins); 0373 foundPlugins.sort(); 0374 QCOMPARE(foundPlugins, expectedPlugins); 0375 0376 QCoreApplication::setLibraryPaths(QStringList() << temp.path() << temp2.path()); 0377 expectedPlugins = QStringList() << jsonPluginDest << unversionedPluginDest << jsonPlugin2Dest; 0378 expectedPlugins.sort(); 0379 foundPlugins.clear(); 0380 KPluginLoader::forEachPlugin(QStringLiteral("for-each-plugin"), addToFoundPlugins); 0381 foundPlugins.sort(); 0382 QCOMPARE(foundPlugins, expectedPlugins); 0383 } 0384 #endif 0385 }; 0386 0387 QTEST_MAIN(KPluginLoaderTest) 0388 0389 #include "kpluginloadertest.moc"