1 /* 2 SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org> 3 4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 5 */ 6 7 #include <QFileInfo> 8 #include <QTest> 9 10 #include "kcoreaddons_debug.h" 11 #include <kpluginloader.h> 12 #include <kpluginmetadata.h> 13 14 class LibraryPathRestorer 15 { 16 public: LibraryPathRestorer(const QStringList & paths)17 explicit LibraryPathRestorer(const QStringList &paths) 18 : mPaths(paths) 19 { 20 } ~LibraryPathRestorer()21 ~LibraryPathRestorer() 22 { 23 QCoreApplication::setLibraryPaths(mPaths); 24 } 25 26 private: 27 QStringList mPaths; 28 }; 29 30 class KPluginLoaderTest : public QObject 31 { 32 Q_OBJECT 33 34 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 86) 35 private Q_SLOTS: testFindPlugin_missing()36 void testFindPlugin_missing() 37 { 38 const QString location = KPluginLoader::findPlugin(QStringLiteral("idonotexist")); 39 QVERIFY2(location.isEmpty(), qPrintable(location)); 40 } 41 testFindPlugin()42 void testFindPlugin() 43 { 44 const QString location = KPluginLoader::findPlugin(QStringLiteral("jsonplugin")); 45 QVERIFY2(!location.isEmpty(), qPrintable(location)); 46 } 47 48 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 84) testPluginVersion()49 void testPluginVersion() 50 { 51 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 52 QCOMPARE(vplugin.pluginVersion(), quint32(5)); 53 54 KPluginLoader vplugin2(QStringLiteral("versionedplugin")); 55 QCOMPARE(vplugin2.pluginVersion(), quint32(5)); 56 57 KPluginLoader uplugin(QStringLiteral("unversionedplugin")); 58 QCOMPARE(uplugin.pluginVersion(), quint32(-1)); 59 60 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 61 QCOMPARE(jplugin.pluginVersion(), quint32(-1)); 62 63 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 64 QCOMPARE(eplugin.pluginVersion(), quint32(-1)); 65 66 KPluginLoader noplugin(QStringLiteral("idonotexist")); 67 QCOMPARE(noplugin.pluginVersion(), quint32(-1)); 68 } 69 #endif 70 testPluginName()71 void testPluginName() 72 { 73 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 74 QCOMPARE(vplugin.pluginName(), QString::fromLatin1("versionedplugin")); 75 76 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 77 QCOMPARE(jplugin.pluginName(), QString::fromLatin1("jsonplugin")); 78 79 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 80 QVERIFY2(eplugin.pluginName().isEmpty(), qPrintable(eplugin.pluginName())); 81 82 KPluginLoader noplugin(QStringLiteral("idonotexist")); 83 QCOMPARE(noplugin.pluginName(), QString::fromLatin1("idonotexist")); 84 } 85 testFactory()86 void testFactory() 87 { 88 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 89 QVERIFY(vplugin.factory()); 90 91 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 92 QVERIFY(jplugin.factory()); 93 94 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 95 QVERIFY(!eplugin.factory()); 96 97 KPluginLoader noplugin(QStringLiteral("idonotexist")); 98 QVERIFY(!noplugin.factory()); 99 } 100 testErrorString()101 void testErrorString() 102 { 103 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 104 QCOMPARE(eplugin.errorString(), QString::fromLatin1("there was an error")); 105 } 106 testFileName()107 void testFileName() 108 { 109 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 110 QCOMPARE(QFileInfo(vplugin.fileName()).canonicalFilePath(), QFileInfo(QStringLiteral(VERSIONEDPLUGIN_FILE)).canonicalFilePath()); 111 112 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 113 QCOMPARE(QFileInfo(jplugin.fileName()).canonicalFilePath(), QFileInfo(QStringLiteral(JSONPLUGIN_FILE)).canonicalFilePath()); 114 115 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 116 QVERIFY2(eplugin.fileName().isEmpty(), qPrintable(eplugin.fileName())); 117 118 KPluginLoader noplugin(QStringLiteral("idonotexist")); 119 QVERIFY2(noplugin.fileName().isEmpty(), qPrintable(noplugin.fileName())); 120 } 121 testInstance()122 void testInstance() 123 { 124 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 125 QVERIFY(vplugin.instance()); 126 127 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 128 QVERIFY(jplugin.instance()); 129 130 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 131 QVERIFY(!eplugin.instance()); 132 133 KPluginLoader noplugin(QStringLiteral("idonotexist")); 134 QVERIFY(!noplugin.instance()); 135 } 136 testIsLoaded()137 void testIsLoaded() 138 { 139 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 140 QVERIFY(!vplugin.isLoaded()); 141 QVERIFY(vplugin.load()); 142 QVERIFY(vplugin.isLoaded()); 143 144 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 145 QVERIFY(!jplugin.isLoaded()); 146 QVERIFY(jplugin.load()); 147 QVERIFY(jplugin.isLoaded()); 148 149 KPluginLoader aplugin(QStringLiteral("alwaysunloadplugin")); 150 QVERIFY(!aplugin.isLoaded()); 151 QVERIFY(aplugin.load()); 152 QVERIFY(aplugin.isLoaded()); 153 if (aplugin.unload()) { 154 QVERIFY(!aplugin.isLoaded()); 155 } else { 156 qCDebug(KCOREADDONS_DEBUG) << "Could not unload alwaysunloadplugin:" << aplugin.errorString(); 157 } 158 159 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 160 QVERIFY(!eplugin.isLoaded()); 161 QVERIFY(!eplugin.load()); 162 QVERIFY(!eplugin.isLoaded()); 163 164 KPluginLoader noplugin(QStringLiteral("idonotexist")); 165 QVERIFY(!noplugin.isLoaded()); 166 QVERIFY(!noplugin.load()); 167 QVERIFY(!noplugin.isLoaded()); 168 } 169 testLoad()170 void testLoad() 171 { 172 KPluginLoader vplugin(QStringLiteral("versionedplugin")); 173 QVERIFY(vplugin.load()); 174 175 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 176 QVERIFY(jplugin.load()); 177 178 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 179 QVERIFY(!eplugin.load()); 180 181 KPluginLoader noplugin(QStringLiteral("idonotexist")); 182 QVERIFY(!noplugin.load()); 183 } 184 testLoadHints()185 void testLoadHints() 186 { 187 KPluginLoader aplugin(QStringLiteral("alwaysunloadplugin")); 188 aplugin.setLoadHints(QLibrary::ResolveAllSymbolsHint); 189 QCOMPARE(aplugin.loadHints(), QLibrary::ResolveAllSymbolsHint); 190 } 191 testMetaData()192 void testMetaData() 193 { 194 KPluginLoader aplugin(QStringLiteral("alwaysunloadplugin")); 195 QJsonObject ametadata = aplugin.metaData(); 196 QVERIFY(!ametadata.isEmpty()); 197 QVERIFY(ametadata.keys().contains(QLatin1String("IID"))); 198 QJsonValue ametadata_metadata = ametadata.value(QStringLiteral("MetaData")); 199 QVERIFY(ametadata_metadata.toObject().isEmpty()); 200 QVERIFY(!aplugin.isLoaded()); // didn't load anything 201 202 KPluginLoader jplugin(KPluginName(QStringLiteral("jsonplugin"))); 203 QJsonObject jmetadata = jplugin.metaData(); 204 QVERIFY(!jmetadata.isEmpty()); 205 QJsonValue jmetadata_metadata = jmetadata.value(QStringLiteral("MetaData")); 206 QVERIFY(jmetadata_metadata.isObject()); 207 QJsonObject jmetadata_obj = jmetadata_metadata.toObject(); 208 QVERIFY(!jmetadata_obj.isEmpty()); 209 QJsonValue comment = jmetadata_obj.value(QStringLiteral("KPlugin")).toObject().value(QStringLiteral("Description")); 210 QVERIFY(comment.isString()); 211 QCOMPARE(comment.toString(), QString::fromLatin1("This is a plugin")); 212 213 KPluginLoader eplugin(KPluginName::fromErrorString(QStringLiteral("there was an error"))); 214 QVERIFY(eplugin.metaData().isEmpty()); 215 216 KPluginLoader noplugin(QStringLiteral("idonotexist")); 217 QVERIFY(noplugin.metaData().isEmpty()); 218 } 219 testUnload()220 void testUnload() 221 { 222 KPluginLoader aplugin(QStringLiteral("alwaysunloadplugin")); 223 QVERIFY(aplugin.load()); 224 // may need QEXPECT_FAIL on some platforms... 225 QVERIFY(aplugin.unload()); 226 } 227 testInstantiatePlugins()228 void testInstantiatePlugins() 229 { 230 const QString plugin1Path = KPluginLoader::findPlugin(QStringLiteral("jsonplugin")); 231 QVERIFY2(!plugin1Path.isEmpty(), qPrintable(plugin1Path)); 232 const QString plugin2Path = KPluginLoader::findPlugin(QStringLiteral("unversionedplugin")); 233 QVERIFY2(!plugin2Path.isEmpty(), qPrintable(plugin2Path)); 234 const QString plugin3Path = KPluginLoader::findPlugin(QStringLiteral("jsonplugin2")); 235 QVERIFY2(!plugin3Path.isEmpty(), qPrintable(plugin3Path)); 236 237 QTemporaryDir temp; 238 QVERIFY(temp.isValid()); 239 QDir dir(temp.path()); 240 QVERIFY2(QFile::copy(plugin1Path, dir.absoluteFilePath(QFileInfo(plugin1Path).fileName())), 241 qPrintable(dir.absoluteFilePath(QFileInfo(plugin1Path).fileName()))); 242 QVERIFY2(QFile::copy(plugin2Path, dir.absoluteFilePath(QFileInfo(plugin2Path).fileName())), 243 qPrintable(dir.absoluteFilePath(QFileInfo(plugin2Path).fileName()))); 244 QVERIFY2(QFile::copy(plugin3Path, dir.absoluteFilePath(QFileInfo(plugin3Path).fileName())), 245 qPrintable(dir.absoluteFilePath(QFileInfo(plugin3Path).fileName()))); 246 247 // only jsonplugin, since unversionedplugin has no json metadata 248 QList<QObject *> plugins = KPluginLoader::instantiatePlugins(temp.path()); 249 QCOMPARE(plugins.size(), 2); 250 QStringList classNames = QStringList() << QString::fromLatin1(plugins[0]->metaObject()->className()) 251 << QString::fromLatin1(plugins[1]->metaObject()->className()); 252 classNames.sort(); 253 QCOMPARE(classNames[0], QStringLiteral("jsonplugin2")); 254 QCOMPARE(classNames[1], QStringLiteral("jsonpluginfa")); 255 qDeleteAll(plugins); 256 257 // try filter 258 plugins = KPluginLoader::instantiatePlugins(temp.path(), [](const KPluginMetaData &md) { 259 return md.pluginId() == QLatin1String("jsonplugin"); 260 }); 261 QCOMPARE(plugins.size(), 1); 262 QCOMPARE(plugins[0]->metaObject()->className(), "jsonpluginfa"); 263 qDeleteAll(plugins); 264 265 plugins = KPluginLoader::instantiatePlugins(temp.path(), [](const KPluginMetaData &md) { 266 return md.pluginId() == QLatin1String("unversionedplugin"); 267 }); 268 QCOMPARE(plugins.size(), 0); 269 270 plugins = KPluginLoader::instantiatePlugins(temp.path(), [](const KPluginMetaData &md) { 271 return md.pluginId() == QLatin1String("foobar"); // ID does not match file name, is set in JSON 272 }); 273 QCOMPARE(plugins.size(), 1); 274 QCOMPARE(plugins[0]->metaObject()->className(), "jsonplugin2"); 275 qDeleteAll(plugins); 276 277 // check that parent gets set 278 plugins = KPluginLoader::instantiatePlugins( 279 temp.path(), 280 [](const KPluginMetaData &) { 281 return true; 282 }, 283 this); 284 QCOMPARE(plugins.size(), 2); 285 QCOMPARE(plugins[0]->parent(), this); 286 QCOMPARE(plugins[1]->parent(), this); 287 qDeleteAll(plugins); 288 289 const QString subDirName = dir.dirName(); 290 QVERIFY(dir.cdUp()); // should now point to /tmp on Linux 291 LibraryPathRestorer restorer(QCoreApplication::libraryPaths()); 292 // instantiate using relative path 293 // make sure library path is set up correctly 294 QCoreApplication::setLibraryPaths(QStringList() << dir.absolutePath()); 295 QVERIFY(!QDir::isAbsolutePath(subDirName)); 296 plugins = KPluginLoader::instantiatePlugins(subDirName); 297 QCOMPARE(plugins.size(), 2); 298 classNames = QStringList() << QString::fromLatin1(plugins[0]->metaObject()->className()) << QString::fromLatin1(plugins[1]->metaObject()->className()); 299 classNames.sort(); 300 QCOMPARE(classNames[0], QStringLiteral("jsonplugin2")); 301 QCOMPARE(classNames[1], QStringLiteral("jsonpluginfa")); 302 qDeleteAll(plugins); 303 } 304 testForEachPlugin()305 void testForEachPlugin() 306 { 307 const QString jsonPluginSrc = KPluginLoader::findPlugin(QStringLiteral("jsonplugin")); 308 QVERIFY2(!jsonPluginSrc.isEmpty(), qPrintable(jsonPluginSrc)); 309 const QString unversionedPluginSrc = KPluginLoader::findPlugin(QStringLiteral("unversionedplugin")); 310 QVERIFY2(!unversionedPluginSrc.isEmpty(), qPrintable(unversionedPluginSrc)); 311 const QString jsonPlugin2Src = KPluginLoader::findPlugin(QStringLiteral("jsonplugin2")); 312 QVERIFY2(!jsonPlugin2Src.isEmpty(), qPrintable(jsonPlugin2Src)); 313 314 QTemporaryDir temp; 315 QVERIFY(temp.isValid()); 316 QDir dir(temp.path()); 317 QVERIFY(dir.mkdir(QStringLiteral("for-each-plugin"))); 318 QVERIFY(dir.cd(QStringLiteral("for-each-plugin"))); 319 const QString jsonPluginDest = dir.absoluteFilePath(QFileInfo(jsonPluginSrc).fileName()); 320 QVERIFY2(QFile::copy(jsonPluginSrc, jsonPluginDest), qPrintable(jsonPluginDest)); 321 const QString unversionedPluginDest = dir.absoluteFilePath(QFileInfo(unversionedPluginSrc).fileName()); 322 QVERIFY2(QFile::copy(unversionedPluginSrc, unversionedPluginDest), qPrintable(unversionedPluginDest)); 323 // copy jsonplugin2 to a "for-each-plugin" subdirectory in a different directory 324 QTemporaryDir temp2; 325 QVERIFY(temp2.isValid()); 326 QDir dir2(temp2.path()); 327 QVERIFY(dir2.mkdir(QStringLiteral("for-each-plugin"))); 328 QVERIFY(dir2.cd(QStringLiteral("for-each-plugin"))); 329 const QString jsonPlugin2Dest = dir2.absoluteFilePath(QFileInfo(jsonPlugin2Src).fileName()); 330 QVERIFY2(QFile::copy(jsonPlugin2Src, jsonPlugin2Dest), qPrintable(jsonPlugin2Dest)); 331 332 QStringList foundPlugins; 333 QStringList expectedPlugins; 334 const auto addToFoundPlugins = [&](const QString &path) { 335 QVERIFY(!path.isEmpty()); 336 foundPlugins.append(path); 337 }; 338 339 // test finding with absolute path 340 expectedPlugins = QStringList() << jsonPluginDest << unversionedPluginDest; 341 expectedPlugins.sort(); 342 KPluginLoader::forEachPlugin(dir.path(), addToFoundPlugins); 343 foundPlugins.sort(); 344 QCOMPARE(foundPlugins, expectedPlugins); 345 346 expectedPlugins = QStringList() << jsonPlugin2Dest; 347 expectedPlugins.sort(); 348 foundPlugins.clear(); 349 KPluginLoader::forEachPlugin(dir2.path(), addToFoundPlugins); 350 foundPlugins.sort(); 351 QCOMPARE(foundPlugins, expectedPlugins); 352 353 // now test relative paths 354 355 LibraryPathRestorer restorer(QCoreApplication::libraryPaths()); 356 QCoreApplication::setLibraryPaths(QStringList() << temp.path()); 357 expectedPlugins = QStringList() << jsonPluginDest << unversionedPluginDest; 358 expectedPlugins.sort(); 359 foundPlugins.clear(); 360 KPluginLoader::forEachPlugin(QStringLiteral("for-each-plugin"), addToFoundPlugins); 361 foundPlugins.sort(); 362 QCOMPARE(foundPlugins, expectedPlugins); 363 364 QCoreApplication::setLibraryPaths(QStringList() << temp2.path()); 365 expectedPlugins = QStringList() << jsonPlugin2Dest; 366 expectedPlugins.sort(); 367 foundPlugins.clear(); 368 KPluginLoader::forEachPlugin(QStringLiteral("for-each-plugin"), addToFoundPlugins); 369 foundPlugins.sort(); 370 QCOMPARE(foundPlugins, expectedPlugins); 371 372 QCoreApplication::setLibraryPaths(QStringList() << temp.path() << temp2.path()); 373 expectedPlugins = QStringList() << jsonPluginDest << unversionedPluginDest << jsonPlugin2Dest; 374 expectedPlugins.sort(); 375 foundPlugins.clear(); 376 KPluginLoader::forEachPlugin(QStringLiteral("for-each-plugin"), addToFoundPlugins); 377 foundPlugins.sort(); 378 QCOMPARE(foundPlugins, expectedPlugins); 379 } 380 #endif 381 }; 382 383 QTEST_MAIN(KPluginLoaderTest) 384 385 #include "kpluginloadertest.moc" 386