File indexing completed on 2024-04-21 04:40:07

0001 /* This file is part of the KDE project
0002    Copyright (C) 2015-2019 Jarosław Staniek <staniek@kde.org>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This program is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this program; see the file COPYING.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KDbTestUtils.h"
0021 #include "KDbUtils_p.h"
0022 #include <KDbConnection>
0023 #include <KDbConnectionData>
0024 #include <KDbConnectionOptions>
0025 #include <KDbDriverManager>
0026 #include <KDbDriverManager_p.h>
0027 #include <KDbDriverMetaData>
0028 #include <KDbNativeStatementBuilder>
0029 #include <KDbProperties>
0030 
0031 #include <QFile>
0032 #include <QTest>
0033 #include <QMimeDatabase>
0034 
0035 #include "../tests/features/tables_test_p.h"
0036 
0037 namespace QTest
0038 {
0039 KDBTESTUTILS_EXPORT bool qCompare(const KDbEscapedString &val1, const KDbEscapedString &val2,
0040                                   const char *actual, const char *expected, const char *file,
0041                                   int line)
0042 {
0043     return val1 == val2
0044         ? compare_helper(true, "COMPARE()", toString(qPrintable(val1.toString())),
0045                          toString(qPrintable(val2.toString())), actual, expected, file, line)
0046         : compare_helper(false, "Compared values are not the same",
0047                          toString(qPrintable(val1.toString())),
0048                          toString(qPrintable(val2.toString())), actual, expected, file, line);
0049 }
0050 
0051 KDBTESTUTILS_EXPORT bool qCompare(const KDbEscapedString &val1, const char *val2,
0052                                   const char *actual, const char *expected, const char *file,
0053                                   int line)
0054 {
0055     return val1 == val2
0056         ? compare_helper(true, "COMPARE()", toString(qPrintable(val1.toString())),
0057                          toString(val2), actual, expected, file, line)
0058         : compare_helper(false, "Compared values are not the same",
0059                          toString(qPrintable(val1.toString())),
0060                          toString(val2), actual, expected, file, line);
0061 }
0062 
0063 KDBTESTUTILS_EXPORT bool qCompare(const char *val1, const KDbEscapedString &val2,
0064                                   const char *actual, const char *expected, const char *file,
0065                                   int line)
0066 {
0067     return val1 == val2
0068         ? compare_helper(true, "COMPARE()", toString(val1), toString(qPrintable(val2.toString())),
0069                          actual, expected, file, line)
0070         : compare_helper(false, "Compared values are not the same",
0071                          toString(val1), toString(qPrintable(val2.toString())),
0072                          actual, expected, file, line);
0073 }
0074 
0075 KDBTESTUTILS_EXPORT bool qCompare(const KDbEscapedString &val1, const QString &val2,
0076                                   const char *actual, const char *expected, const char *file,
0077                                   int line)
0078 {
0079     return val1 == KDbEscapedString(val2)
0080         ? compare_helper(true, "COMPARE()", toString(qPrintable(val1.toString())),
0081                          toString(val2), actual, expected, file, line)
0082         : compare_helper(false, "Compared values are not the same",
0083                          toString(qPrintable(val1.toString())),
0084                          toString(val2), actual, expected, file, line);
0085 }
0086 
0087 KDBTESTUTILS_EXPORT bool qCompare(const QString &val1, const KDbEscapedString &val2,
0088                                   const char *actual, const char *expected, const char *file,
0089                                   int line)
0090 {
0091     return KDbEscapedString(val1) == val2
0092         ? compare_helper(true, "COMPARE()", toString(val1), toString(qPrintable(val2.toString())),
0093                          actual, expected, file, line)
0094         : compare_helper(false, "Compared values are not the same",
0095                          toString(val1), toString(qPrintable(val2.toString())),
0096                          actual, expected, file, line);
0097 }
0098 
0099 static char *toString(const QStringList &list)
0100 {
0101     return toString(qPrintable(QStringLiteral("QStringList(%1)").arg(list.join(", "))));
0102 }
0103 
0104 KDBTESTUTILS_EXPORT bool qCompare(const QStringList &val1, const QStringList &val2,
0105                                   const char *actual, const char *expected, const char *file,
0106                                   int line)
0107 {
0108     return val1 == val2 ? compare_helper(true, "COMPARE()", toString(val1), toString(val2), actual,
0109                                          expected, file, line)
0110                         : compare_helper(false, "Compared values are not the same", toString(val1),
0111                                          toString(val2), actual, expected, file, line);
0112 }
0113 KDBTESTUTILS_EXPORT bool qCompare(const QByteArray &val1, const char *val2, const char *actual,
0114                                   const char *expected, const char *file, int line)
0115 {
0116     return qCompare(val1, QByteArray(val2), actual, expected, file, line);
0117 }
0118 KDBTESTUTILS_EXPORT bool qCompare(const QString &val1, const char *val2, const char *actual,
0119                                   const char *expected, const char *file, int line)
0120 {
0121     return qCompare(val1, QString::fromLatin1(val2), actual, expected, file, line);
0122 }
0123 }
0124 
0125 class KDbTestUtils::Private {
0126 public:
0127     Private() {}
0128     QScopedPointer<KDbNativeStatementBuilder> kdbBuilder;
0129     QScopedPointer<KDbNativeStatementBuilder> driverBuilder;
0130 
0131     KDbConnection *connection()
0132     {
0133         return m_connection.data();
0134     }
0135 
0136     void setConnection(KDbConnection *conn)
0137     {
0138         kdbBuilder.reset(); // dependency will be removed
0139         m_connection.reset(conn);
0140     }
0141 
0142     KDbConnection* takeConnection()
0143     {
0144         if (!m_connection) {
0145             return nullptr;
0146         }
0147         kdbBuilder.reset(); // dependency may be removed
0148         return m_connection.take();
0149     }
0150 
0151 private:
0152     QScopedPointer<KDbConnection> m_connection;
0153 };
0154 
0155 KDbTestUtils::KDbTestUtils()
0156     : d(new Private)
0157 {
0158     QCoreApplication::addLibraryPath(KDB_LOCAL_PLUGINS_DIR); // make plugins work without installing them
0159 }
0160 
0161 KDbTestUtils::~KDbTestUtils()
0162 {
0163     delete d;
0164 }
0165 
0166 KDbConnection* KDbTestUtils::connection()
0167 {
0168     return d->connection();
0169 }
0170 
0171 KDbNativeStatementBuilder* KDbTestUtils::kdbBuilder()
0172 {
0173     Q_ASSERT(connection());
0174     if (connection() && !d->kdbBuilder) {
0175         d->kdbBuilder.reset(new KDbNativeStatementBuilder(connection(), KDb::KDbEscaping));
0176     }
0177     return d->kdbBuilder.data();
0178 }
0179 
0180 KDbNativeStatementBuilder* KDbTestUtils::driverBuilder()
0181 {
0182     Q_ASSERT(connection());
0183     if (connection() && !d->driverBuilder) {
0184         d->driverBuilder.reset(new KDbNativeStatementBuilder(connection(), KDb::DriverEscaping));
0185     }
0186     return d->driverBuilder.data();
0187 }
0188 
0189 void KDbTestUtils::testDriverManagerInternal(bool forceEmpty)
0190 {
0191     DriverManagerInternal::self()->forceEmpty = forceEmpty;
0192     QStringList ids = manager.driverIds();
0193     //qDebug() << "DRIVERS:" << ids;
0194     QVERIFY2(forceEmpty == manager.result().isError(), "Error in driver manager");
0195     //qDebug() << manager.result().message();
0196     QVERIFY2(forceEmpty == ids.isEmpty(), "No db drivers found");
0197     if (forceEmpty) { // no drivers, so try to find one and expect failure
0198         ids << "org.kde.kdb.sqlite";
0199     }
0200     for (const QString &id : qAsConst(ids)) {
0201         const KDbDriverMetaData* driverMetaData;
0202         if (forceEmpty) {
0203             KDB_EXPECT_FAIL(manager.resultable(), driverMetaData = manager.driverMetaData(id),
0204                             ERR_DRIVERMANAGER, "Driver metadata not found");
0205             // find driver for the metadata
0206             KDB_EXPECT_FAIL(manager.resultable(), driver = manager.driver(id),
0207                             ERR_DRIVERMANAGER, "Driver not found");
0208         } else {
0209             KDB_VERIFY(manager.resultable(), driverMetaData = manager.driverMetaData(id),
0210                        "Driver metadata not found");
0211             QCOMPARE(driverMetaData->id(), id);
0212             // find driver for the metadata
0213             KDB_VERIFY(manager.resultable(), driver = manager.driver(id), "Driver not found");
0214         }
0215     }
0216     DriverManagerInternal::self()->forceEmpty = false; // default state
0217 }
0218 
0219 void KDbTestUtils::testDriverManagerInternal()
0220 {
0221     testDriverManagerInternal(true);
0222     testDriverManagerInternal(false);
0223 }
0224 
0225 void KDbTestUtils::testDriver(const QString &driverId, bool fileBased,
0226                               const QStringList &expectedMimeTypes,
0227                               const QStringList &possiblyInvalidMimeTypes)
0228 {
0229     // find the metadata
0230     const KDbDriverMetaData* driverMetaData;
0231     KDB_VERIFY(manager.resultable(), driverMetaData = manager.driverMetaData(driverId),
0232                qPrintable(QStringLiteral("Driver metadata not found for id=%1").arg(driverId)));
0233     QCOMPARE(driverMetaData->id(), driverId);
0234     QCOMPARE(driverMetaData->isFileBased(), fileBased);
0235     // test the mimetypes
0236     const QStringList foundMimeTypes(driverMetaData->mimeTypes());
0237     QVERIFY2(!KDb::defaultFileBasedDriverMimeType().isEmpty(),
0238              qPrintable(QStringLiteral("id=%1").arg(driverId)));
0239     QMimeDatabase mimeDb;
0240     for (const QString &mimeName : expectedMimeTypes) {
0241         if (!mimeDb.mimeTypeForName(mimeName).isValid()) {
0242             const QString msg = QStringLiteral("MIME type %1 not found in the MIME database").arg(mimeName);
0243             if (possiblyInvalidMimeTypes.contains(mimeName)) {
0244                 qInfo() << qPrintable(msg);
0245                 continue; // ignore
0246             } else {
0247                 QVERIFY2(mimeDb.mimeTypeForName(mimeName).isValid(), qPrintable(msg));
0248             }
0249         }
0250         const QStringList ids = manager.driverIdsForMimeType(mimeName);
0251         QVERIFY2(!ids.isEmpty(),
0252                  qPrintable(QStringLiteral("No drivers found for MIME type=%1").arg(mimeName)));
0253         QVERIFY2(ids.contains(driverId),
0254                  qPrintable(QStringLiteral("No driver with id=%1 found for MIME type=%2")
0255                                 .arg(driverId, mimeName)));
0256     }
0257     // each found mime type expected?
0258     for (const QString &mimeName : foundMimeTypes) {
0259         QVERIFY2(expectedMimeTypes.contains(mimeName),
0260                  qPrintable(QStringLiteral("Unexpected MIME type=%1 found for driver with id=%2")
0261                                 .arg(mimeName, driverId)));
0262     }
0263     // find driver for the metadata
0264     KDB_VERIFY(manager.resultable(), driver = manager.driver(driverId), "Driver not found");
0265 }
0266 
0267 void KDbTestUtils::testSqliteDriverInternal()
0268 {
0269     const QStringList mimeTypes { "application/x-kexiproject-sqlite3", "application/x-sqlite3",
0270                                   "application/x-vnd.kde.kexi", "application/vnd.sqlite3" };
0271     const QStringList possiblyInvalidMimeTypes { "application/vnd.sqlite3" };
0272     testDriver("org.kde.kdb.sqlite", true /* file-based */, mimeTypes, possiblyInvalidMimeTypes);
0273     QVERIFY2(mimeTypes.contains(KDb::defaultFileBasedDriverMimeType()),
0274              "SQLite's MIME types should include the default file based one");
0275 }
0276 
0277 void KDbTestUtils::testConnectInternal(const KDbConnectionData &cdata,
0278                                        const KDbConnectionOptions &options)
0279 {
0280     //qDebug() << cdata;
0281 
0282     if (!driver) {
0283         //! @todo don't hardcode SQLite here
0284         KDB_VERIFY(manager.resultable(), driver = manager.driver("org.kde.kdb.sqlite"), "Driver not found");
0285     }
0286 
0287     KDbConnectionOptions connOptionsOverride(options);
0288     QStringList extraSqliteExtensionPaths;
0289     extraSqliteExtensionPaths << SQLITE_LOCAL_ICU_EXTENSION_PATH;
0290     connOptionsOverride.insert("extraSqliteExtensionPaths", extraSqliteExtensionPaths);
0291 
0292     d->setConnection(nullptr); // remove previous connection if present
0293     const int connCount = driver->connections().count();
0294     d->setConnection(driver->createConnection(cdata, connOptionsOverride));
0295     KDB_VERIFY(driver, connection(), "Failed to create connection");
0296     QVERIFY2(cdata.driverId().isEmpty(), "Connection data has filled driver ID");
0297     QCOMPARE(connection()->data().driverId(), driver->metaData()->id());
0298     QVERIFY2(driver->connections().contains(connection()), "Driver does not list created connection");
0299     QCOMPARE(driver->connections().count(), connCount + 1); // one more
0300 
0301     const KDbUtils::Property extraSqliteExtensionPathsProperty = connection()->options()->property("extraSqliteExtensionPaths");
0302     QVERIFY2(!extraSqliteExtensionPathsProperty.isNull(), "extraSqliteExtensionPaths property not found");
0303     QCOMPARE(extraSqliteExtensionPathsProperty.value().type(), QVariant::StringList);
0304     QCOMPARE(extraSqliteExtensionPathsProperty.value().toStringList(), extraSqliteExtensionPaths);
0305 
0306     const KDbUtils::Property readOnlyProperty = connection()->options()->property("readOnly");
0307     QVERIFY2(!readOnlyProperty.isNull(), "readOnly property not found");
0308     QCOMPARE(readOnlyProperty.value().toBool(), connection()->options()->isReadOnly());
0309 
0310     //! @todo Add extensive test for a read-only connection
0311 
0312     KDB_VERIFY(connection(), connection()->connect(), "Failed to connect");
0313     KDB_VERIFY(connection(), connection()->isConnected(), "Database not connected after call to connect()");
0314 }
0315 
0316 void KDbTestUtils::testUseInternal()
0317 {
0318     KDB_VERIFY(connection(), connection()->databaseExists(connection()->data().databaseName()), "Database does not exist");
0319     KDB_VERIFY(connection(), connection()->useDatabase(), "Failed to use database");
0320     KDB_VERIFY(connection(), connection()->isDatabaseUsed(), "Database not used after call to useDatabase()");
0321 }
0322 
0323 void KDbTestUtils::testConnectAndUseInternal(const KDbConnectionData &cdata,
0324                                              const KDbConnectionOptions &options)
0325 {
0326     if (!testConnect(cdata, options) || !connection()) {
0327         qWarning() << driver->result();
0328         QFAIL("testConnect");
0329     }
0330     if (!testUse() || !connection()->isDatabaseUsed()) {
0331         qWarning() << connection()->result();
0332         bool result = testDisconnect();
0333         Q_UNUSED(result);
0334         QFAIL("testUse");
0335     }
0336 }
0337 
0338 void KDbTestUtils::testConnectAndUseInternal(const QString &path,
0339                                              const KDbConnectionOptions &options)
0340 {
0341     KDbConnectionData cdata;
0342     cdata.setDatabaseName(path);
0343     testConnectAndUseInternal(cdata, options);
0344 }
0345 
0346 void KDbTestUtils::testCreateDbInternal(const QString &dbName)
0347 {
0348     //open connection
0349     KDbConnectionData cdata;
0350     //! @todo don't hardcode SQLite (.kexi) extension here
0351     QString fullDbName(QDir::fromNativeSeparators(QFile::decodeName(FILES_OUTPUT_DIR "/")
0352                        + dbName + ".kexi"));
0353     cdata.setDatabaseName(fullDbName);
0354 
0355     QVERIFY(testConnect(cdata));
0356     QVERIFY(connection());
0357 
0358     //! @todo KDbDriver::metaData
0359     {
0360         QScopedPointer<KDbConnection> connGuard(d->takeConnection());
0361 
0362         if (connGuard->databaseExists(dbName)) {
0363             KDB_VERIFY(connGuard, connGuard->dropDatabase(fullDbName), "Failed to drop database");
0364         }
0365         KDB_VERIFY(connGuard, !connGuard->databaseExists(fullDbName), "Database exists");
0366         KDB_VERIFY(connGuard, connGuard->createDatabase(fullDbName), "Failed to create db");
0367         KDB_VERIFY(connGuard, connGuard->databaseExists(fullDbName), "Database does not exist after creation");
0368         d->setConnection(connGuard.take());
0369     }
0370 }
0371 
0372 void KDbTestUtils::testCreateDbWithTablesInternal(const QString &dbName)
0373 {
0374     QVERIFY(testCreateDb(dbName));
0375     KDB_VERIFY(connection(), connection()->useDatabase(), "Failed to use database");
0376     testCreateTablesInternal();
0377 }
0378 
0379 void KDbTestUtils::testPropertiesInternal()
0380 {
0381     QStringList properties;
0382     properties << connection()->databaseProperties().names();
0383     QVERIFY(properties.contains("kexidb_major_ver"));
0384     bool ok;
0385     QVERIFY(connection()->databaseProperties().value("kexidb_major_ver").toInt(&ok) >= 0);
0386     QVERIFY(ok);
0387     QVERIFY(properties.contains("kexidb_minor_ver"));
0388     QVERIFY(connection()->databaseProperties().value("kexidb_minor_ver").toInt(&ok) >= 0);
0389     QVERIFY(ok);
0390 }
0391 
0392 void KDbTestUtils::testCreateTablesInternal()
0393 {
0394     QVERIFY2(tablesTest_createTables(connection()) == 0, "Failed to create test data");
0395 }
0396 
0397 void KDbTestUtils::testDisconnectPrivate()
0398 {
0399     if (!connection()) {
0400         return;
0401     }
0402     KDB_VERIFY(connection(), connection()->closeDatabase(), "Failed to close database");
0403     KDB_VERIFY(connection(), !connection()->isDatabaseUsed(), "Database still used after closing");
0404     KDB_VERIFY(connection(), connection()->closeDatabase(), "Second closeDatabase() call  should not fail");
0405     KDB_VERIFY(connection(), connection()->disconnect(), "Failed to disconnect database");
0406     KDB_VERIFY(connection(), !connection()->isConnected(), "Database still connected after disconnecting");
0407     KDB_VERIFY(connection(), connection()->disconnect(), "Second disconnect() call should not fail");
0408 }
0409 
0410 void KDbTestUtils::testDisconnectInternal()
0411 {
0412     const int connCount = driver ? driver->connections().count() : 0;
0413     testDisconnectPrivate();
0414     QVERIFY(!QTest::currentTestFailed());
0415     d->setConnection(nullptr);
0416     QCOMPARE(driver ? driver->connections().count() : -1, connCount - 1); // one less
0417 }
0418 
0419 void KDbTestUtils::testDropDbInternal()
0420 {
0421     QVERIFY(connection()->dropDatabase(connection()->data().databaseName()));
0422 }
0423 
0424 void KDbTestUtils::testDisconnectAndDropDbInternal()
0425 {
0426     QString dbName(connection()->data().databaseName());
0427     testDisconnectPrivate();
0428     QVERIFY(!QTest::currentTestFailed());
0429     KDB_VERIFY(connection(), connection()->dropDatabase(dbName), "Failed to drop database");
0430     d->setConnection(nullptr);
0431 }