diff options
Diffstat (limited to 'tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp')
-rw-r--r-- | tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp | 170 |
1 files changed, 108 insertions, 62 deletions
diff --git a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp index fcce21433e..f4ecf5bfb3 100644 --- a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp +++ b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp @@ -1,31 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Copyright (C) 2021 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2021 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> @@ -41,6 +16,8 @@ # include <QtCore/private/qmachparser_p.h> #endif +using namespace Qt::StringLiterals; + // Helper macros to let us know if some suffixes are valid #define bundle_VALID false #define dylib_VALID false @@ -153,7 +130,7 @@ static std::unique_ptr<QTemporaryFile> patchElf(const QString &source, ElfPatche const char *basename = QTest::currentDataTag(); if (!basename) basename = QTest::currentTestFunction(); - tmplib.reset(new QTemporaryFile(basename + QString(".XXXXXX" SUFFIX))); + tmplib.reset(new QTemporaryFile(QDir::currentPath() + u'/' + basename + u".XXXXXX" SUFFIX ""_s)); QVERIFY2(tmplib->open(), qPrintable(tmplib->errorString())); // sanity-check @@ -185,12 +162,22 @@ static std::unique_ptr<QTemporaryFile> patchElf(const QString &source, ElfPatche static QString sys_qualifiedLibraryName(const QString &fileName) { +#ifdef Q_OS_ANDROID + // On Android all the libraries must be located in the APK's libs subdir + const QStringList paths = QCoreApplication::libraryPaths(); + if (!paths.isEmpty()) { + return QLatin1String("%1/%2%3_%4%5").arg(paths.first(), PREFIX, fileName, + ANDROID_ARCH, SUFFIX); + } + return fileName; +#else QString name = QLatin1String("bin/") + QLatin1String(PREFIX) + fileName + QLatin1String(SUFFIX); const QString libname = QFINDTESTDATA(name); QFileInfo fi(libname); if (fi.exists()) return fi.canonicalFilePath(); return libname; +#endif } QT_FORWARD_DECLARE_CLASS(QPluginLoader) @@ -221,6 +208,7 @@ private slots: void preloadedPlugin_data(); void preloadedPlugin(); void staticPlugins(); + void reregisteredStaticPlugins(); }; Q_IMPORT_PLUGIN(StaticPlugin) @@ -292,7 +280,9 @@ void tst_QPluginLoader::errorString() QVERIFY(!unloaded); } -#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_HPUX) +// A bug in QNX causes the test to crash on exit after attempting to load +// a shared library with undefined symbols (tracked as QTBUG-114682). +#if !defined(Q_OS_WIN) && !defined(Q_OS_DARWIN) && !defined(Q_OS_HPUX) && !defined(Q_OS_QNX) { QPluginLoader loader( sys_qualifiedLibraryName("almostplugin")); //a plugin with unresolved symbols loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); @@ -349,10 +339,37 @@ void tst_QPluginLoader::loadHints() QSKIP("This test requires Qt to create shared libraries."); #endif QPluginLoader loader; - QCOMPARE(loader.loadHints(), QLibrary::LoadHints{}); //Do not crash + QCOMPARE(loader.loadHints(), QLibrary::PreventUnloadHint); //Do not crash loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); + QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); + // We can clear load hints when file name is not set. + loader.setLoadHints(QLibrary::LoadHints{}); + QCOMPARE(loader.loadHints(), QLibrary::LoadHints{}); + // Set the hints again + loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); + QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); loader.setFileName( sys_qualifiedLibraryName("theplugin")); //a plugin QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); + + QPluginLoader loader4; + QCOMPARE(loader4.loadHints(), QLibrary::PreventUnloadHint); + loader4.setLoadHints(QLibrary::LoadHints{}); + QCOMPARE(loader4.loadHints(), QLibrary::LoadHints{}); + loader4.setFileName(sys_qualifiedLibraryName("theplugin")); + // Hints are merged with hints from the previous loader. + QCOMPARE(loader4.loadHints(), QLibrary::ResolveAllSymbolsHint); + // We cannot clear load hints after associating the loader with a file. + loader.setLoadHints(QLibrary::LoadHints{}); + QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); + + QPluginLoader loader2; + QCOMPARE(loader2.loadHints(), QLibrary::PreventUnloadHint); + loader2.setFileName(sys_qualifiedLibraryName("theplugin")); + // Hints are merged with hints from previous loaders. + QCOMPARE(loader2.loadHints(), QLibrary::PreventUnloadHint | QLibrary::ResolveAllSymbolsHint); + + QPluginLoader loader3(sys_qualifiedLibraryName("theplugin")); + QCOMPARE(loader3.loadHints(), QLibrary::PreventUnloadHint | QLibrary::ResolveAllSymbolsHint); } void tst_QPluginLoader::deleteinstanceOnUnload() @@ -382,14 +399,14 @@ void tst_QPluginLoader::deleteinstanceOnUnload() QVERIFY(spy2.isValid()); if (pass == 0) { QCOMPARE(loader2.unload(), false); // refcount not reached 0, not really unloaded - QCOMPARE(spy1.count(), 0); - QCOMPARE(spy2.count(), 0); + QCOMPARE(spy1.size(), 0); + QCOMPARE(spy2.size(), 0); } QCOMPARE(instance1->pluginName(), QLatin1String("Plugin ok")); QCOMPARE(instance2->pluginName(), QLatin1String("Plugin ok")); QVERIFY(loader1.unload()); // refcount reached 0, did really unload - QCOMPARE(spy1.count(), 1); - QCOMPARE(spy2.count(), 1); + QCOMPARE(spy1.size(), 1); + QCOMPARE(spy2.size(), 1); } } @@ -457,7 +474,7 @@ static void loadCorruptElfCommonRows() memcpy(h, &o, sizeof(o)); }); newRow("invalid-word-size", "file is for a different word size", [](H h) { - h->e_ident[EI_CLASS] = ELFCLASSNONE;; + h->e_ident[EI_CLASS] = ELFCLASSNONE; }); newRow("unknown-word-size", "file is for a different word size", [](H h) { h->e_ident[EI_CLASS] |= 0x40; @@ -843,26 +860,23 @@ void tst_QPluginLoader::loadMachO_data() # ifdef Q_PROCESSOR_X86_64 QTest::newRow("machtest/good.x86_64.dylib") << true; - QTest::newRow("machtest/good.i386.dylib") << false; + QTest::newRow("machtest/good.arm64.dylib") << false; QTest::newRow("machtest/good.fat.no-x86_64.dylib") << false; - QTest::newRow("machtest/good.fat.no-i386.dylib") << true; -# elif defined(Q_PROCESSOR_X86_32) - QTest::newRow("machtest/good.i386.dylib") << true; + QTest::newRow("machtest/good.fat.no-arm64.dylib") << true; +# elif defined(Q_PROCESSOR_ARM) + QTest::newRow("machtest/good.arm64.dylib") << true; QTest::newRow("machtest/good.x86_64.dylib") << false; - QTest::newRow("machtest/good.fat.no-i386.dylib") << false; + QTest::newRow("machtest/good.fat.no-arm64.dylib") << false; QTest::newRow("machtest/good.fat.no-x86_64.dylib") << true; # endif -# ifndef Q_PROCESSOR_POWER_64 - QTest::newRow("machtest/good.ppc64.dylib") << false; -# endif QTest::newRow("machtest/good.fat.all.dylib") << true; QTest::newRow("machtest/good.fat.stub-x86_64.dylib") << false; - QTest::newRow("machtest/good.fat.stub-i386.dylib") << false; + QTest::newRow("machtest/good.fat.stub-arm64.dylib") << false; QDir d(QFINDTESTDATA("machtest")); - QStringList badlist = d.entryList(QStringList() << "bad*.dylib"); - foreach (const QString &bad, badlist) + const QStringList badlist = d.entryList(QStringList() << "bad*.dylib"); + for (const QString &bad : badlist) QTest::newRow(qPrintable("machtest/" + bad)) << false; #endif } @@ -886,12 +900,7 @@ void tst_QPluginLoader::loadMachO() } QVERIFY(r.pos > 0); - QVERIFY(size_t(r.length) >= sizeof(void*)); QVERIFY(r.pos + r.length < data.size()); - QCOMPARE(r.pos & (sizeof(void*) - 1), 0UL); - - void *value = *(void**)(data.constData() + r.pos); - QCOMPARE(value, sizeof(void*) > 4 ? (void*)(0xc0ffeec0ffeeL) : (void*)0xc0ffee); // now that we know it's valid, let's try to make it invalid ulong offeredlen = r.pos; @@ -909,11 +918,19 @@ void tst_QPluginLoader::relativePath() #if !defined(QT_SHARED) QSKIP("This test requires Qt to create shared libraries."); #endif +#ifdef Q_OS_ANDROID + // On Android we do not need to explicitly set library paths, as they are + // already set. + // But we need to use ARCH suffix in pulgin name + const QString pluginName("theplugin_" ANDROID_ARCH SUFFIX); +#else // Windows binaries run from release and debug subdirs, so we can't rely on the current dir. const QString binDir = QFINDTESTDATA("bin"); QVERIFY(!binDir.isEmpty()); QCoreApplication::addLibraryPath(binDir); - QPluginLoader loader("theplugin" SUFFIX); + const QString pluginName("theplugin" SUFFIX); +#endif + QPluginLoader loader(pluginName); loader.load(); // not recommended, instance() should do the job. PluginInterface *instance = qobject_cast<PluginInterface*>(loader.instance()); QVERIFY(instance); @@ -926,13 +943,27 @@ void tst_QPluginLoader::absolutePath() #if !defined(QT_SHARED) QSKIP("This test requires Qt to create shared libraries."); #endif +#ifdef Q_OS_ANDROID + // On Android we need to clear library paths to make sure that the absolute + // path works + const QStringList libraryPaths = QCoreApplication::libraryPaths(); + QVERIFY(!libraryPaths.isEmpty()); + QCoreApplication::setLibraryPaths(QStringList()); + const QString pluginPath(libraryPaths.first() + "/" PREFIX "theplugin_" ANDROID_ARCH SUFFIX); +#else // Windows binaries run from release and debug subdirs, so we can't rely on the current dir. const QString binDir = QFINDTESTDATA("bin"); QVERIFY(!binDir.isEmpty()); QVERIFY(QDir::isAbsolutePath(binDir)); - QPluginLoader loader(binDir + "/" PREFIX "theplugin" SUFFIX); + const QString pluginPath(binDir + "/" PREFIX "theplugin" SUFFIX); +#endif + QPluginLoader loader(pluginPath); loader.load(); // not recommended, instance() should do the job. PluginInterface *instance = qobject_cast<PluginInterface*>(loader.instance()); +#ifdef Q_OS_ANDROID + // Restore library paths + QCoreApplication::setLibraryPaths(libraryPaths); +#endif QVERIFY(instance); QCOMPARE(instance->pluginName(), QLatin1String("Plugin ok")); QVERIFY(loader.unload()); @@ -953,7 +984,7 @@ void tst_QPluginLoader::reloadPlugin() QSignalSpy spy(loader.instance(), &QObject::destroyed); QVERIFY(spy.isValid()); QVERIFY(loader.unload()); // refcount reached 0, did really unload - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); // reload plugin QVERIFY(loader.load()); @@ -968,6 +999,10 @@ void tst_QPluginLoader::reloadPlugin() void tst_QPluginLoader::loadSectionTableStrippedElf() { +#ifdef Q_OS_ANDROID + if (QNativeInterface::QAndroidApplication::sdkVersion() >= 24) + QSKIP("Android 7+ (API 24+) linker doesn't allow missing or bad section header"); +#endif #if !defined(QT_SHARED) QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds"); #elif !defined(Q_OF_ELF) @@ -1060,19 +1095,18 @@ void tst_QPluginLoader::staticPlugins() const QObjectList instances = QPluginLoader::staticInstances(); QVERIFY(instances.size()); - bool found = false; - for (QObject *obj : instances) { - found = obj->metaObject()->className() == QLatin1String("StaticPlugin"); - if (found) - break; - } - QVERIFY(found); + // ensure the our plugin only shows up once + int foundCount = std::count_if(instances.begin(), instances.end(), [](QObject *obj) { + return obj->metaObject()->className() == QLatin1String("StaticPlugin"); + }); + QCOMPARE(foundCount, 1); const auto plugins = QPluginLoader::staticPlugins(); QCOMPARE(plugins.size(), instances.size()); // find the metadata QJsonObject metaData; + bool found = false; for (const auto &p : plugins) { metaData = p.metaData(); found = metaData.value("className").toString() == QLatin1String("StaticPlugin"); @@ -1088,6 +1122,18 @@ void tst_QPluginLoader::staticPlugins() QCOMPARE(metaData.value("URI").toString(), "qt.test.pluginloader.staticplugin"); } +void tst_QPluginLoader::reregisteredStaticPlugins() +{ + // the Q_IMPORT_PLUGIN macro will have already done this + qRegisterStaticPluginFunction(qt_static_plugin_StaticPlugin()); + staticPlugins(); + if (QTest::currentTestFailed()) + return; + + qRegisterStaticPluginFunction(qt_static_plugin_StaticPlugin()); + staticPlugins(); +} + QTEST_MAIN(tst_QPluginLoader) #include "tst_qpluginloader.moc" |