diff --git a/autotests/KDbTestUtils.cpp b/autotests/KDbTestUtils.cpp index 51fcc5a8..494670a5 100644 --- a/autotests/KDbTestUtils.cpp +++ b/autotests/KDbTestUtils.cpp @@ -1,388 +1,421 @@ /* This file is part of the KDE project - Copyright (C) 2015-2018 Jarosław Staniek + Copyright (C) 2015-2019 Jarosław Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KDbTestUtils.h" #include "KDbUtils_p.h" #include #include #include #include #include #include #include #include #include #include #include #include "../tests/features/tables_test_p.h" namespace QTest { KDBTESTUTILS_EXPORT bool qCompare(const KDbEscapedString &val1, const KDbEscapedString &val2, const char *actual, const char *expected, const char *file, int line) { return val1 == val2 ? compare_helper(true, "COMPARE()", toString(qPrintable(val1.toString())), toString(qPrintable(val2.toString())), actual, expected, file, line) : compare_helper(false, "Compared values are not the same", toString(qPrintable(val1.toString())), toString(qPrintable(val2.toString())), actual, expected, file, line); } KDBTESTUTILS_EXPORT bool qCompare(const KDbEscapedString &val1, const char *val2, const char *actual, const char *expected, const char *file, int line) { return val1 == val2 ? compare_helper(true, "COMPARE()", toString(qPrintable(val1.toString())), toString(val2), actual, expected, file, line) : compare_helper(false, "Compared values are not the same", toString(qPrintable(val1.toString())), toString(val2), actual, expected, file, line); } KDBTESTUTILS_EXPORT bool qCompare(const char *val1, const KDbEscapedString &val2, const char *actual, const char *expected, const char *file, int line) { return val1 == val2 ? compare_helper(true, "COMPARE()", toString(val1), toString(qPrintable(val2.toString())), actual, expected, file, line) : compare_helper(false, "Compared values are not the same", toString(val1), toString(qPrintable(val2.toString())), actual, expected, file, line); } KDBTESTUTILS_EXPORT bool qCompare(const KDbEscapedString &val1, const QString &val2, const char *actual, const char *expected, const char *file, int line) { return val1 == KDbEscapedString(val2) ? compare_helper(true, "COMPARE()", toString(qPrintable(val1.toString())), toString(val2), actual, expected, file, line) : compare_helper(false, "Compared values are not the same", toString(qPrintable(val1.toString())), toString(val2), actual, expected, file, line); } KDBTESTUTILS_EXPORT bool qCompare(const QString &val1, const KDbEscapedString &val2, const char *actual, const char *expected, const char *file, int line) { return KDbEscapedString(val1) == val2 ? compare_helper(true, "COMPARE()", toString(val1), toString(qPrintable(val2.toString())), actual, expected, file, line) : compare_helper(false, "Compared values are not the same", toString(val1), toString(qPrintable(val2.toString())), actual, expected, file, line); } + +static char *toString(const QStringList &list) +{ + return toString(qPrintable(QStringLiteral("QStringList(%1)").arg(list.join(", ")))); +} + +KDBTESTUTILS_EXPORT bool qCompare(const QStringList &val1, const QStringList &val2, + const char *actual, const char *expected, const char *file, + int line) +{ + return val1 == val2 ? compare_helper(true, "COMPARE()", toString(val1), toString(val2), actual, + expected, file, line) + : compare_helper(false, "Compared values are not the same", toString(val1), + toString(val2), actual, expected, file, line); +} } class KDbTestUtils::Private { public: Private() {} QScopedPointer kdbBuilder; QScopedPointer driverBuilder; KDbConnection *connection() { return m_connection.data(); } void setConnection(KDbConnection *conn) { kdbBuilder.reset(); // dependency will be removed m_connection.reset(conn); } KDbConnection* takeConnection() { if (!m_connection) { return nullptr; } kdbBuilder.reset(); // dependency may be removed return m_connection.take(); } private: QScopedPointer m_connection; }; KDbTestUtils::KDbTestUtils() : d(new Private) { QCoreApplication::addLibraryPath(KDB_LOCAL_PLUGINS_DIR); // make plugins work without installing them } KDbTestUtils::~KDbTestUtils() { delete d; } KDbConnection* KDbTestUtils::connection() { return d->connection(); } KDbNativeStatementBuilder* KDbTestUtils::kdbBuilder() { Q_ASSERT(connection()); if (connection() && !d->kdbBuilder) { d->kdbBuilder.reset(new KDbNativeStatementBuilder(connection(), KDb::KDbEscaping)); } return d->kdbBuilder.data(); } KDbNativeStatementBuilder* KDbTestUtils::driverBuilder() { Q_ASSERT(connection()); if (connection() && !d->driverBuilder) { d->driverBuilder.reset(new KDbNativeStatementBuilder(connection(), KDb::DriverEscaping)); } return d->driverBuilder.data(); } void KDbTestUtils::testDriverManagerInternal(bool forceEmpty) { DriverManagerInternal::self()->forceEmpty = forceEmpty; QStringList ids = manager.driverIds(); //qDebug() << "DRIVERS:" << ids; QVERIFY2(forceEmpty == manager.result().isError(), "Error in driver manager"); //qDebug() << manager.result().message(); QVERIFY2(forceEmpty == ids.isEmpty(), "No db drivers found"); if (forceEmpty) { // no drivers, so try to find one and expect failure ids << "org.kde.kdb.sqlite"; } for (const QString &id : qAsConst(ids)) { const KDbDriverMetaData* driverMetaData; if (forceEmpty) { KDB_EXPECT_FAIL(manager.resultable(), driverMetaData = manager.driverMetaData(id), ERR_DRIVERMANAGER, "Driver metadata not found"); // find driver for the metadata KDB_EXPECT_FAIL(manager.resultable(), driver = manager.driver(id), ERR_DRIVERMANAGER, "Driver not found"); } else { KDB_VERIFY(manager.resultable(), driverMetaData = manager.driverMetaData(id), "Driver metadata not found"); QCOMPARE(driverMetaData->id(), id); // find driver for the metadata KDB_VERIFY(manager.resultable(), driver = manager.driver(id), "Driver not found"); } } DriverManagerInternal::self()->forceEmpty = false; // default state } void KDbTestUtils::testDriverManagerInternal() { testDriverManagerInternal(true); testDriverManagerInternal(false); } -void KDbTestUtils::testDriver(const QString &driverId, bool fileBased, const QStringList &mimeTypes) +void KDbTestUtils::testDriver(const QString &driverId, bool fileBased, + const QStringList &expectedMimeTypes, + const QStringList &possiblyInvalidMimeTypes) { // find the metadata const KDbDriverMetaData* driverMetaData; - KDB_VERIFY(manager.resultable(), driverMetaData = manager.driverMetaData(driverId), "Driver metadata not found"); + KDB_VERIFY(manager.resultable(), driverMetaData = manager.driverMetaData(driverId), + qPrintable(QStringLiteral("Driver metadata not found for id=%1").arg(driverId))); QCOMPARE(driverMetaData->id(), driverId); QCOMPARE(driverMetaData->isFileBased(), fileBased); // test the mimetypes - QStringList foundMimeTypes(driverMetaData->mimeTypes()); - foundMimeTypes.sort(); - QStringList expectedMimeTypes(mimeTypes); - expectedMimeTypes.sort(); - //qDebug() << "mimeTypes:" << mimeTypes; - QCOMPARE(foundMimeTypes, expectedMimeTypes); - QVERIFY(!KDb::defaultFileBasedDriverMimeType().isEmpty()); + const QStringList foundMimeTypes(driverMetaData->mimeTypes()); + QVERIFY2(!KDb::defaultFileBasedDriverMimeType().isEmpty(), + qPrintable(QStringLiteral("id=%1").arg(driverId))); QMimeDatabase mimeDb; - foreach(const QString &mimeName, expectedMimeTypes) { - QVERIFY2(mimeDb.mimeTypeForName(mimeName).isValid(), - qPrintable(QString("%1 MIME type not found in the MIME database").arg(mimeName))); + for (const QString &mimeName : expectedMimeTypes) { + if (!mimeDb.mimeTypeForName(mimeName).isValid()) { + const QString msg = QStringLiteral("MIME type %1 not found in the MIME database").arg(mimeName); + if (possiblyInvalidMimeTypes.contains(mimeName)) { + qInfo() << qPrintable(msg); + continue; // ignore + } else { + QVERIFY2(mimeDb.mimeTypeForName(mimeName).isValid(), qPrintable(msg)); + } + } + const QStringList ids = manager.driverIdsForMimeType(mimeName); + QVERIFY2(!ids.isEmpty(), + qPrintable(QStringLiteral("No drivers found for MIME type=%1").arg(mimeName))); + QVERIFY2(ids.contains(driverId), + qPrintable(QStringLiteral("No driver with id=%1 found for MIME type=%2") + .arg(driverId, mimeName))); + } + // each found mime type expected? + for (const QString &mimeName : foundMimeTypes) { + QVERIFY2(expectedMimeTypes.contains(mimeName), + qPrintable(QStringLiteral("Unexpected MIME type=%1 found for driver with id=%2") + .arg(mimeName, driverId))); } // find driver for the metadata KDB_VERIFY(manager.resultable(), driver = manager.driver(driverId), "Driver not found"); } void KDbTestUtils::testSqliteDriverInternal() { - QStringList mimeTypes; - mimeTypes << "application/x-kexiproject-sqlite3" << "application/x-sqlite3"; - testDriver("org.kde.kdb.sqlite", - true, // file-based - mimeTypes); - QVERIFY2(mimeTypes.contains(KDb::defaultFileBasedDriverMimeType()), "SQLite's MIME types should include the default file based one"); + const QStringList mimeTypes { "application/x-kexiproject-sqlite3", "application/x-sqlite3", + "application/x-vnd.kde.kexi", "application/vnd.sqlite3" }; + const QStringList possiblyInvalidMimeTypes { "application/vnd.sqlite3" }; + testDriver("org.kde.kdb.sqlite", true /* file-based */, mimeTypes, possiblyInvalidMimeTypes); + QVERIFY2(mimeTypes.contains(KDb::defaultFileBasedDriverMimeType()), + "SQLite's MIME types should include the default file based one"); } void KDbTestUtils::testConnectInternal(const KDbConnectionData &cdata, const KDbConnectionOptions &options) { //qDebug() << cdata; if (!driver) { //! @todo don't hardcode SQLite here KDB_VERIFY(manager.resultable(), driver = manager.driver("org.kde.kdb.sqlite"), "Driver not found"); } KDbConnectionOptions connOptionsOverride(options); QStringList extraSqliteExtensionPaths; extraSqliteExtensionPaths << SQLITE_LOCAL_ICU_EXTENSION_PATH; connOptionsOverride.insert("extraSqliteExtensionPaths", extraSqliteExtensionPaths); d->setConnection(nullptr); // remove previous connection if present const int connCount = driver->connections().count(); d->setConnection(driver->createConnection(cdata, connOptionsOverride)); KDB_VERIFY(driver, connection(), "Failed to create connection"); QVERIFY2(cdata.driverId().isEmpty(), "Connection data has filled driver ID"); QCOMPARE(connection()->data().driverId(), driver->metaData()->id()); QVERIFY2(driver->connections().contains(connection()), "Driver does not list created connection"); QCOMPARE(driver->connections().count(), connCount + 1); // one more const KDbUtils::Property extraSqliteExtensionPathsProperty = connection()->options()->property("extraSqliteExtensionPaths"); QVERIFY2(!extraSqliteExtensionPathsProperty.isNull(), "extraSqliteExtensionPaths property not found"); QCOMPARE(extraSqliteExtensionPathsProperty.value().type(), QVariant::StringList); QCOMPARE(extraSqliteExtensionPathsProperty.value().toStringList(), extraSqliteExtensionPaths); const KDbUtils::Property readOnlyProperty = connection()->options()->property("readOnly"); QVERIFY2(!readOnlyProperty.isNull(), "readOnly property not found"); QCOMPARE(readOnlyProperty.value().toBool(), connection()->options()->isReadOnly()); //! @todo Add extensive test for a read-only connection KDB_VERIFY(connection(), connection()->connect(), "Failed to connect"); KDB_VERIFY(connection(), connection()->isConnected(), "Database not connected after call to connect()"); } void KDbTestUtils::testUseInternal() { KDB_VERIFY(connection(), connection()->databaseExists(connection()->data().databaseName()), "Database does not exist"); KDB_VERIFY(connection(), connection()->useDatabase(), "Failed to use database"); KDB_VERIFY(connection(), connection()->isDatabaseUsed(), "Database not used after call to useDatabase()"); } void KDbTestUtils::testConnectAndUseInternal(const KDbConnectionData &cdata, const KDbConnectionOptions &options) { if (!testConnect(cdata, options) || !connection()) { qWarning() << driver->result(); QFAIL("testConnect"); } if (!testUse() || !connection()->isDatabaseUsed()) { qWarning() << connection()->result(); bool result = testDisconnect(); Q_UNUSED(result); QFAIL("testUse"); } } void KDbTestUtils::testConnectAndUseInternal(const QString &path, const KDbConnectionOptions &options) { KDbConnectionData cdata; cdata.setDatabaseName(path); testConnectAndUseInternal(cdata, options); } void KDbTestUtils::testCreateDbInternal(const QString &dbName) { //open connection KDbConnectionData cdata; //! @todo don't hardcode SQLite (.kexi) extension here QString fullDbName(QDir::fromNativeSeparators(QFile::decodeName(FILES_OUTPUT_DIR "/") + dbName + ".kexi")); cdata.setDatabaseName(fullDbName); QVERIFY(testConnect(cdata)); QVERIFY(connection()); //! @todo KDbDriver::metaData { QScopedPointer connGuard(d->takeConnection()); if (connGuard->databaseExists(dbName)) { KDB_VERIFY(connGuard, connGuard->dropDatabase(fullDbName), "Failed to drop database"); } KDB_VERIFY(connGuard, !connGuard->databaseExists(fullDbName), "Database exists"); KDB_VERIFY(connGuard, connGuard->createDatabase(fullDbName), "Failed to create db"); KDB_VERIFY(connGuard, connGuard->databaseExists(fullDbName), "Database does not exist after creation"); d->setConnection(connGuard.take()); } } void KDbTestUtils::testCreateDbWithTablesInternal(const QString &dbName) { QVERIFY(testCreateDb(dbName)); KDB_VERIFY(connection(), connection()->useDatabase(), "Failed to use database"); testCreateTablesInternal(); } void KDbTestUtils::testPropertiesInternal() { QStringList properties; properties << connection()->databaseProperties().names(); QVERIFY(properties.contains("kexidb_major_ver")); bool ok; QVERIFY(connection()->databaseProperties().value("kexidb_major_ver").toInt(&ok) >= 0); QVERIFY(ok); QVERIFY(properties.contains("kexidb_minor_ver")); QVERIFY(connection()->databaseProperties().value("kexidb_minor_ver").toInt(&ok) >= 0); QVERIFY(ok); } void KDbTestUtils::testCreateTablesInternal() { QVERIFY2(tablesTest_createTables(connection()) == 0, "Failed to create test data"); } void KDbTestUtils::testDisconnectPrivate() { if (!connection()) { return; } KDB_VERIFY(connection(), connection()->closeDatabase(), "Failed to close database"); KDB_VERIFY(connection(), !connection()->isDatabaseUsed(), "Database still used after closing"); KDB_VERIFY(connection(), connection()->closeDatabase(), "Second closeDatabase() call should not fail"); KDB_VERIFY(connection(), connection()->disconnect(), "Failed to disconnect database"); KDB_VERIFY(connection(), !connection()->isConnected(), "Database still connected after disconnecting"); KDB_VERIFY(connection(), connection()->disconnect(), "Second disconnect() call should not fail"); } void KDbTestUtils::testDisconnectInternal() { const int connCount = driver ? driver->connections().count() : 0; testDisconnectPrivate(); QVERIFY(!QTest::currentTestFailed()); d->setConnection(nullptr); QCOMPARE(driver ? driver->connections().count() : -1, connCount - 1); // one less } void KDbTestUtils::testDropDbInternal() { QVERIFY(connection()->dropDatabase(connection()->data().databaseName())); } void KDbTestUtils::testDisconnectAndDropDbInternal() { QString dbName(connection()->data().databaseName()); testDisconnectPrivate(); QVERIFY(!QTest::currentTestFailed()); KDB_VERIFY(connection(), connection()->dropDatabase(dbName), "Failed to drop database"); d->setConnection(nullptr); } diff --git a/autotests/KDbTestUtils.h b/autotests/KDbTestUtils.h index 0ac0b7cb..0462e95c 100644 --- a/autotests/KDbTestUtils.h +++ b/autotests/KDbTestUtils.h @@ -1,203 +1,208 @@ /* This file is part of the KDE project - Copyright (C) 2015-2018 Jarosław Staniek + Copyright (C) 2015-2019 Jarosław Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KDB_TESTUTILS_H #define KDB_TESTUTILS_H #include "kdbtestutils_export.h" #include #include #include #include #include #include class KDbNativeStatementBuilder; Q_DECLARE_METATYPE(KDbField::TypeGroup) Q_DECLARE_METATYPE(KDbField::Type) Q_DECLARE_METATYPE(KDb::Signedness) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(KDb::BLOBEscapingType) //! @internal for KDB_VERIFY template const T* KDB_POINTER_WRAPPER(const T &t) { return &t; } //! @internal for KDB_VERIFY template const T* KDB_POINTER_WRAPPER(const T *t) { return t; } //! @internal for KDB_VERIFY template T* KDB_POINTER_WRAPPER(T *t) { return t; } //! @internal for KDB_VERIFY template T* KDB_POINTER_WRAPPER(const QPointer &t) { return t.data(); } //! @internal for KDB_VERIFY template T* KDB_POINTER_WRAPPER(const QScopedPointer &t) { return t.data(); } namespace QTest { KDBTESTUTILS_EXPORT bool qCompare(const KDbEscapedString &val1, const KDbEscapedString &val2, const char *actual, const char *expected, const char *file, int line); KDBTESTUTILS_EXPORT bool qCompare(const KDbEscapedString &val1, const char *val2, const char *actual, const char *expected, const char *file, int line); KDBTESTUTILS_EXPORT bool qCompare(const char *val1, const KDbEscapedString &val2, const char *actual, const char *expected, const char *file, int line); KDBTESTUTILS_EXPORT bool qCompare(const KDbEscapedString &val1, const QString &val2, const char *actual, const char *expected, const char *file, int line); KDBTESTUTILS_EXPORT bool qCompare(const QString &val1, const KDbEscapedString &val2, const char *actual, const char *expected, const char *file, int line); + +KDBTESTUTILS_EXPORT bool qCompare(const QStringList &val1, const QStringList &val2, + const char *actual, const char *expected, const char *file, + int line); } //! Calls @a call and verifies status of @a resultable //! On error displays the status on debug and does the same as QVERIFY with @a errorMessage #define KDB_VERIFY(resultable, call, errorMessage) \ do { \ bool KDB_VERIFY_ok = (call); \ const KDbResultable *KDB_VERIFY_resultablePtr = KDB_POINTER_WRAPPER(resultable); \ if (KDB_VERIFY_resultablePtr->result().isError()) { \ qDebug() << KDB_VERIFY_resultablePtr->result(); \ } \ if (!QTest::qVerify(KDB_VERIFY_ok && !KDB_VERIFY_resultablePtr->result().isError(), # call, (errorMessage), __FILE__, __LINE__)) {\ return; \ } \ } \ while (false) //! Calls @a call and verifies status of @a resultable //! On error displays the status on debug and does the same as QVERIFY with @a errorMessage #define KDB_EXPECT_FAIL(resultable, call, expectedErrorCode, errorMessage) \ do { \ bool KDB_VERIFY_ok = (call); \ const KDbResultable *KDB_VERIFY_resultablePtr = KDB_POINTER_WRAPPER(resultable); \ if (KDB_VERIFY_resultablePtr->result().isError()) { \ qDebug() << KDB_VERIFY_resultablePtr->result(); \ } \ QVERIFY(KDB_VERIFY_resultablePtr->result().isError()); \ if (!QTest::qVerify(!KDB_VERIFY_ok, # call, (errorMessage), __FILE__, __LINE__)) {\ return; \ } \ if (!QTest::qCompare(KDB_VERIFY_resultablePtr->result().code(), expectedErrorCode, # call, # expectedErrorCode, __FILE__, __LINE__)) {\ return; \ } \ } \ while (false) //! Declares method @a name that returns false on test failure, it can be called as utility function. //! Also declared internal method name ## Internal which performs the actual test. //! This way users of this method can call QVERIFY(utils.()); #define KDBTEST_METHOD_DECL(name, argsDecl, args) \ public: \ bool name argsDecl Q_REQUIRED_RESULT { name ## Internal args ; return !QTest::currentTestFailed(); } \ private Q_SLOTS: \ void name ## Internal argsDecl //! Test utilities that provide basic database features class KDBTESTUTILS_EXPORT KDbTestUtils : public QObject { Q_OBJECT public: KDbTestUtils(); ~KDbTestUtils(); KDbDriverManager manager; QPointer driver; /** * Returns associated connection */ KDbConnection* connection(); /** * Returns builder for generating KDb SQL statements */ KDbNativeStatementBuilder* kdbBuilder(); /** * Returns builder for generating driver-native SQL statements */ KDbNativeStatementBuilder* driverBuilder(); KDBTEST_METHOD_DECL(testDriverManager, (), ()); KDBTEST_METHOD_DECL(testSqliteDriver, (), ()); //! Connects to a database //! @since 3.2 KDBTEST_METHOD_DECL(testConnect, (const KDbConnectionData &cdata, const KDbConnectionOptions &options = KDbConnectionOptions()), (cdata, options)); KDBTEST_METHOD_DECL(testUse, (), ()); //! Convenience method that performs testConnect and testUse in one go //! @since 3.2 KDBTEST_METHOD_DECL(testConnectAndUse, (const KDbConnectionData &cdata, const KDbConnectionOptions &options = KDbConnectionOptions()), (cdata, options)); //! Overload of testConnectAndUse for file-based databases //! @since 3.2 KDBTEST_METHOD_DECL(testConnectAndUse, (const QString &path, const KDbConnectionOptions &options = KDbConnectionOptions()), (path, options)); //! Creates database with name @a dbName //! Does not use the database. //! @todo don't hardcode SQLite here //! @note dbName should not include ".kexi" extension or path KDBTEST_METHOD_DECL(testCreateDb, (const QString &dbName), (dbName)); //! Creates database with name @a dbName, then uses it and creates test tables //! The database stays used. //! @note dbName should not include ".kexi" extension or path KDBTEST_METHOD_DECL(testCreateDbWithTables, (const QString &dbName), (dbName)); KDBTEST_METHOD_DECL(testProperties, (), ()); KDBTEST_METHOD_DECL(testCreateTables, (), ()); KDBTEST_METHOD_DECL(testDisconnect, (), ()); KDBTEST_METHOD_DECL(testDropDb, (), ()); KDBTEST_METHOD_DECL(testDisconnectAndDropDb, (), ()); protected: void testDisconnectPrivate(); - void testDriver(const QString &driverId, bool fileBased, const QStringList &mimeTypes); + void testDriver(const QString &driverId, bool fileBased, const QStringList &expectedMimeTypes, + const QStringList &possiblyInvalidMimeTypes); void testDriverManagerInternal(bool forceEmpty); private: Q_DISABLE_COPY(KDbTestUtils) class Private; Private * const d; }; #endif diff --git a/src/KDbDriverManager.cpp b/src/KDbDriverManager.cpp index 0d35729c..1b076bcf 100644 --- a/src/KDbDriverManager.cpp +++ b/src/KDbDriverManager.cpp @@ -1,288 +1,306 @@ /* This file is part of the KDE project Copyright (C) 2003 Daniel Molkentin Copyright (C) 2003 Joseph Wenninger Copyright (C) 2003-2015 Jarosław Staniek Copyright (C) 2012 Dimitrios T. Tanis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KDbDriverManager.h" #include "KDbDriverManager_p.h" #include "KDbJsonTrader_p.h" #include "KDbDriver.h" #include "KDbDriverMetaData.h" #include "kdb_debug.h" #include #include +#include #include #include Q_GLOBAL_STATIC(DriverManagerInternal, s_self) DriverManagerInternal::DriverManagerInternal() : m_lookupDriversNeeded(true) { qsrand(QTime::currentTime().msec()); // needed e.g. to create random table names } DriverManagerInternal::~DriverManagerInternal() { drivermanagerDebug(); clear(); drivermanagerDebug() << "ok"; } void DriverManagerInternal::clear() { drivermanagerDebug() << "Clearing drivers..."; qDeleteAll(m_drivers); m_drivers.clear(); qDeleteAll(m_driversMetaData); m_driversMetaData.clear(); } void DriverManagerInternal::slotAppQuits() { if (qApp && !qApp->topLevelWidgets().isEmpty() && qApp->topLevelWidgets().first()->isVisible()) { return; //what a hack! - we give up when app is still there } clear(); } //static DriverManagerInternal *DriverManagerInternal::self() { return s_self; } bool DriverManagerInternal::lookupDrivers() { if (!m_lookupDriversNeeded) return true; if (!forceEmpty) { lookupDriversInternal(); m_lookupDriversNeeded = false; } if (m_driversMetaData.isEmpty()) { m_result = KDbResult(ERR_DRIVERMANAGER, tr("Could not find any database drivers.")); return false; } return true; } void DriverManagerInternal::lookupDriversInternal() { if (qApp) { connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(slotAppQuits())); } clearResult(); //drivermanagerDebug() << "Load all plugins"; QList offers = KDbJsonTrader::self()->query(QLatin1String("KDb/Driver")); const QString expectedVersion = QString::fromLatin1("%1.%2") .arg(KDB_STABLE_VERSION_MAJOR).arg(KDB_STABLE_VERSION_MINOR); + QMimeDatabase mimedb; foreach(const QPluginLoader *loader, offers) { //QJsonObject json = loader->metaData(); //drivermanagerDebug() << json; QScopedPointer metaData(new KDbDriverMetaData(*loader)); //qDebug() << "VER:" << metaData->version(); if (metaData->version() != expectedVersion) { kdbWarning() << "Driver with ID" << metaData->id() << "(" << metaData->fileName() << ")" << "has version" << metaData->version() << "but expected version is" << expectedVersion << "-- skipping it"; continue; } if (m_driversMetaData.contains(metaData->id())) { if (qgetenv("KDB_NO_DUPLICATED_DRIVER_WARNINGS").isEmpty()) { kdbWarning() << "Driver with ID" << metaData->id() << "already found at" << m_driversMetaData.value(metaData->id())->fileName() << "-- skipping another at" << metaData->fileName(); } continue; } - foreach (const QString& mimeType, metaData->mimeTypes()) { + QSet resolvedMimeTypes; + for (const QString &mimeType : metaData->mimeTypes()) { + const QMimeType mime = mimedb.mimeTypeForName(mimeType); + if (!mime.isValid()) { + kdbWarning() << "Driver with ID" << metaData->id() + << "specifies the unknown MIME type" + << mimeType; + continue; + } + resolvedMimeTypes.insert(mime.name()); + } + for (const QString &mimeType : resolvedMimeTypes) { m_metadata_by_mimetype.insertMulti(mimeType, metaData.data()); } m_driversMetaData.insert(metaData->id(), metaData.data()); metaData.take(); } qDeleteAll(offers); offers.clear(); } QStringList DriverManagerInternal::driverIds() { if (!lookupDrivers()) { return QStringList(); } if (m_driversMetaData.isEmpty() && result().isError()) { return QStringList(); } return m_driversMetaData.keys(); } const KDbDriverMetaData* DriverManagerInternal::driverMetaData(const QString &id) { if (!lookupDrivers()) { return nullptr; } const KDbDriverMetaData *metaData = m_driversMetaData.value(id.toLower()); if (!metaData || m_result.isError()) { m_result = KDbResult(ERR_DRIVERMANAGER, tr("Could not find database driver \"%1\".").arg(id)); } return metaData; } QStringList DriverManagerInternal::driverIdsForMimeType(const QString &mimeType) { if (!lookupDrivers()) { return QStringList(); } - const QList metaDatas(m_metadata_by_mimetype.values(mimeType.toLower())); + QMimeDatabase mimedb; + const QMimeType mime = mimedb.mimeTypeForName(mimeType.toLower()); + if (!mime.isValid()) { + return QStringList(); + } + const QList metaDatas(m_metadata_by_mimetype.values(mime.name())); QStringList result; foreach (const KDbDriverMetaData* metaData, metaDatas) { result.append(metaData->id()); } return result; } QStringList DriverManagerInternal::possibleProblems() const { return m_possibleProblems; } KDbDriver* DriverManagerInternal::driver(const QString& id) { if (!lookupDrivers()) return nullptr; clearResult(); drivermanagerDebug() << "loading" << id; KDbDriver *driver = nullptr; if (!id.isEmpty()) { driver = m_drivers.value(id.toLower()); } if (driver) return driver; //cached if (!m_driversMetaData.contains(id.toLower())) { m_result = KDbResult(ERR_DRIVERMANAGER, tr("Could not find database driver \"%1\".").arg(id)); return nullptr; } const KDbDriverMetaData *metaData = m_driversMetaData.value(id.toLower()); KPluginFactory *factory = qobject_cast(metaData->instantiate()); if (!factory) { m_result = KDbResult(ERR_DRIVERMANAGER, tr("Could not load database driver's plugin file \"%1\".") .arg(metaData->fileName())); QPluginLoader loader(metaData->fileName()); // use this to get the message (void)loader.load(); m_result.setServerMessage(loader.errorString()); kdbWarning() << m_result.message() << m_result.serverMessage(); return nullptr; } driver = factory->create(); if (!driver) { m_result = KDbResult(ERR_DRIVERMANAGER, tr("Could not open database driver \"%1\" from plugin file \"%2\".") .arg(metaData->id(), metaData->fileName())); kdbWarning() << m_result.message(); return nullptr; } driver->setMetaData(metaData); m_drivers.insert(id.toLower(), driver); return driver; } // --------------------------- KDbDriverManager::KDbDriverManager() { } KDbDriverManager::~KDbDriverManager() { } KDbResult KDbDriverManager::result() const { return s_self->result(); } KDbResultable* KDbDriverManager::resultable() const { return s_self; } QStringList KDbDriverManager::driverIds() { return s_self->driverIds(); } const KDbDriverMetaData* KDbDriverManager::driverMetaData(const QString &id) { return s_self->driverMetaData(id); } QStringList KDbDriverManager::driverIdsForMimeType(const QString &mimeType) { return s_self->driverIdsForMimeType(mimeType); } KDbDriver* KDbDriverManager::driver(const QString& id) { return s_self->driver(id); } QString KDbDriverManager::possibleProblemsMessage() const { if (s_self->possibleProblems().isEmpty()) { return QString(); } QString str; str.reserve(1024); str = QLatin1String("
    "); foreach (const QString& problem, s_self->possibleProblems()) str += (QLatin1String("
  • ") + problem + QLatin1String("
  • ")); str += QLatin1String("
"); return str; } bool KDbDriverManager::hasDatabaseServerDrivers() { foreach(const QString& id, driverIds()) { const KDbDriverMetaData *metaData = s_self->driverMetaData(id); if (!metaData->isFileBased()) { return true; } } return false; } diff --git a/src/KDbDriverManager.h b/src/KDbDriverManager.h index 47710566..59fbbdc8 100644 --- a/src/KDbDriverManager.h +++ b/src/KDbDriverManager.h @@ -1,99 +1,107 @@ /* This file is part of the KDE project Copyright (C) 2003 Daniel Molkentin Copyright (C) 2003 Joseph Wenninger Copyright (C) 2003-2015 Jarosław Staniek Copyright (C) 2012 Dimitrios T. Tanis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KDB_DRIVER_MANAGER_H #define KDB_DRIVER_MANAGER_H #include #include #include "kdb_export.h" class KDbResult; class KDbResultable; class KDbDriver; class KDbDriverMetaData; //! A driver manager for finding and loading driver plugins. class KDB_EXPORT KDbDriverManager { Q_DECLARE_TR_FUNCTIONS(KDbDriverManager) public: KDbDriverManager(); virtual ~KDbDriverManager(); //! @return result of the recent operation. KDbResult result() const; //! @return KDbResultable object for the recent operation. //! It adds serverResultName() in addition to the result(). KDbResultable* resultable() const; /*! @return information (metadata) about driver with ID @a id. The lookup is case insensitive. The metadata object is owned by KDb internals and is not deleted after this KDbDriverManager object is deleted. @a nullptr is returned if the metadata has not been found. On error status can be obtained using result(). @see driver(const QString& id) for information about duplicated drivers. */ const KDbDriverMetaData* driverMetaData(const QString &id); /*! Tries to load db driver with ID @a id. The lookup is case insensitive. @return driver object or @a nullptr on error. On error status can be obtained using result(). The driver object is owned by KDb internals and is not deleted after this KDbDriverManager object is deleted. @note If more than one driver with the same ID found on the search path, first located driver is selected. All other drivers for this ID are skip with a warning "Driver with ID '...' already found at (path) -- skipping another at (path). The warning can be suppressed by setting a KDB_NO_DUPLICATED_DRIVER_WARNINGS environment variable. */ KDbDriver* driver(const QString& id); /*! returns list of available drivers IDs. That drivers can be loaded by first use of driver() method. */ QStringList driverIds(); - /*! @return list of driver IDs for @a mimeType mime type. - Empty list is returned if no driver has been found. - Works only with drivers of file-based databases such as SQLite. - The lookup is case insensitive. */ + /** + * Returns list of driver IDs for @a mimeType MIME type + * + * IDs of drivers for file-based databases are only returned. + * Empty list is returned if no driver has been found for the type or if the type is invalid. + * Driver supports the supplied MIME type if it is specified as supported in the driver's + * metadata. If a MIME type alias is supplied, proper type for this alias is resolved and driver + * IDs for that type are returned. Similarly, if proper MIME type is supplied, IDs are returned + * for drivers that support any alias for this type. + * + * The lookup is case insensitive. + */ QStringList driverIdsForMimeType(const QString& mimeType); /*! @return HTML-formatted message about possible problems encountered. It can be displayed in a 'details' section of a GUI message if an error encountered. Currently the message contains a list of incompatible db drivers. Can be used in code that finds driver depending on file format. */ //! @todo make it just QStringList QString possibleProblemsMessage() const; /*! @return true if there is at least one server-based database driver installed. */ bool hasDatabaseServerDrivers(); private: Q_DISABLE_COPY(KDbDriverManager) }; #endif