File indexing completed on 2024-11-03 04:18:31
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 }