From f81667cacadc0d7ffa18a19c4574cd43f3ad4272 Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Fri, 20 Nov 2009 15:43:59 +1000 Subject: More SQL tests. Less non-spec functionality in API. --- src/declarative/qml/qmlsqldatabase.cpp | 127 +++------------------ tests/auto/declarative/sql/data/error-b.js | 13 +++ .../declarative/sql/data/error-notransaction.js | 15 +++ tests/auto/declarative/sql/data/readonly-error.js | 28 +++++ tests/auto/declarative/sql/data/readonly.js | 24 ++++ tests/auto/declarative/sql/data/reopen1.js | 14 +++ tests/auto/declarative/sql/data/reopen2.js | 16 +++ tests/auto/declarative/sql/sql.pro | 1 + tests/auto/declarative/sql/tst_sql.cpp | 84 ++++++++------ 9 files changed, 174 insertions(+), 148 deletions(-) create mode 100644 tests/auto/declarative/sql/data/error-b.js create mode 100644 tests/auto/declarative/sql/data/error-notransaction.js create mode 100644 tests/auto/declarative/sql/data/readonly-error.js create mode 100644 tests/auto/declarative/sql/data/readonly.js create mode 100644 tests/auto/declarative/sql/data/reopen1.js create mode 100644 tests/auto/declarative/sql/data/reopen2.js diff --git a/src/declarative/qml/qmlsqldatabase.cpp b/src/declarative/qml/qmlsqldatabase.cpp index dfe3982..933eb92 100644 --- a/src/declarative/qml/qmlsqldatabase.cpp +++ b/src/declarative/qml/qmlsqldatabase.cpp @@ -63,8 +63,6 @@ Q_DECLARE_METATYPE(QSqlDatabase) Q_DECLARE_METATYPE(QSqlQuery) -class QmlSqlQueryScriptClassPropertyIterator; - class QmlSqlQueryScriptClass: public QScriptClass { public: QmlSqlQueryScriptClass(QScriptEngine *engine) : QScriptClass(engine) @@ -75,22 +73,13 @@ public: QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, - QueryFlags flags, uint *id) + QueryFlags flags, uint *) { if (flags & HandlesReadAccess) { if (name == str_length) { return HandlesReadAccess; } else if (name == str_forwardOnly) { return flags; - } else { - bool ok; - qint32 pos = name.toString().toInt(&ok); - if (pos < 0 || !ok) - return 0; - QSqlQuery query = qscriptvalue_cast(object.data()); - *id = pos; - if (*id < (uint)query.size()) - return HandlesReadAccess; } } if (flags & HandlesWriteAccess) @@ -100,7 +89,7 @@ public: } QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id) + const QScriptString &name, uint) { QSqlQuery query = qscriptvalue_cast(object.data()); if (name == str_length) { @@ -117,15 +106,6 @@ public: } } else if (name == str_forwardOnly) { return query.isForwardOnly(); - } else { - if ((uint)query.at() == id || query.seek(id)) { // Qt 4.6 doesn't optimize seek(at()) - QSqlRecord r = query.record(); - QScriptValue row = engine()->newObject(); - for (int j=0; jundefinedValue(); } @@ -148,83 +128,15 @@ public: return QScriptValue::Undeletable; } - //QScriptClassPropertyIterator *newIterator(const QScriptValue &object); - private: QScriptString str_length; QScriptString str_forwardOnly; }; -/* -class QmlSqlQueryScriptClassPropertyIterator : public QScriptClassPropertyIterator -{ -public: - QmlSqlQueryScriptClassPropertyIterator(const QScriptValue &object) - : QScriptClassPropertyIterator(object) - { - toFront(); - } - - ~QmlSqlQueryScriptClassPropertyIterator() - { - } - - bool hasNext() const - { - QSqlQuery query = qscriptvalue_cast(object().data()); - return query.at() == m_index || query.seek(m_index); // Qt 4.6 doesn't optimize seek(at()) - } - - void next() - { - m_last = m_index; - ++m_index; - } +// If the spec changes to allow iteration, check git history... +// class QmlSqlQueryScriptClassPropertyIterator : public QScriptClassPropertyIterator - bool hasPrevious() const - { - return (m_index > 0); - } - - void previous() - { - --m_index; - m_last = m_index; - } - void toFront() - { - m_index = 0; - m_last = -1; - } - - void toBack() - { - QSqlQuery query = qscriptvalue_cast(object().data()); - m_index = query.size(); - m_last = -1; - } - - QScriptString name() const - { - return object().engine()->toStringHandle(QString::number(m_last)); - } - - uint id() const - { - return m_last; - } - -private: - int m_index; - int m_last; -}; - -QScriptClassPropertyIterator *QmlSqlQueryScriptClass::newIterator(const QScriptValue &object) -{ - return new QmlSqlQueryScriptClassPropertyIterator(object); -} -*/ enum SqlException { UNKNOWN_ERR, @@ -328,11 +240,10 @@ static QScriptValue qmlsqldatabase_executeSql_readonly(QScriptContext *context, { QString sql = context->argument(0).toString(); if (sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) { - qmlsqldatabase_executeSql(context,engine); + return qmlsqldatabase_executeSql(context,engine); } else { THROW_SQL(SYNTAX_ERR,QmlEngine::tr("Read-only Transaction")) } - return engine->undefinedValue(); } static QScriptValue qmlsqldatabase_change_version(QScriptContext *context, QScriptEngine *engine) @@ -380,7 +291,7 @@ static QScriptValue qmlsqldatabase_change_version(QScriptContext *context, QScri return engine->undefinedValue(); } -static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptEngine *engine) +static QScriptValue qmlsqldatabase_transaction_shared(QScriptContext *context, QScriptEngine *engine, bool readOnly) { QSqlDatabase db = qscriptvalue_cast(context->thisObject()); QScriptValue callback = context->argument(0); @@ -388,7 +299,8 @@ static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptE THROW_SQL(UNKNOWN_ERR,QmlEngine::tr("transaction: missing callback")); QScriptValue instance = engine->newObject(); - instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql,1)); + instance.setProperty(QLatin1String("executeSql"), + engine->newFunction(readOnly ? qmlsqldatabase_executeSql_readonly : qmlsqldatabase_executeSql,1)); QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db)); db.transaction(); @@ -402,26 +314,13 @@ static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptE return engine->undefinedValue(); } +static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptEngine *engine) +{ + return qmlsqldatabase_transaction_shared(context,engine,false); +} static QScriptValue qmlsqldatabase_read_transaction(QScriptContext *context, QScriptEngine *engine) { - QSqlDatabase db = qscriptvalue_cast(context->thisObject()); - QScriptValue callback = context->argument(0); - if (!callback.isFunction()) - return engine->undefinedValue(); - - QScriptValue instance = engine->newObject(); - instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql_readonly,1)); - QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db)); - - db.transaction(); - callback.call(QScriptValue(), QScriptValueList() << tx); - if (engine->hasUncaughtException()) { - db.rollback(); - } else { - if (!db.commit()) - db.rollback(); - } - return engine->undefinedValue(); + return qmlsqldatabase_transaction_shared(context,engine,true); } /* diff --git a/tests/auto/declarative/sql/data/error-b.js b/tests/auto/declarative/sql/data/error-b.js new file mode 100644 index 0000000..4dd0ecf --- /dev/null +++ b/tests/auto/declarative/sql/data/error-b.js @@ -0,0 +1,13 @@ +function test() { + var db = openDatabaseSync("QmlTestDB-error-b", "1.0", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; + + db.transaction( + function(tx) { + tx.executeSql('INSERT INTO Greeting VALUES("junk","junk")'); + notexist[123] = "oops" + } + ); + + return r; +} diff --git a/tests/auto/declarative/sql/data/error-notransaction.js b/tests/auto/declarative/sql/data/error-notransaction.js new file mode 100644 index 0000000..b9cc647 --- /dev/null +++ b/tests/auto/declarative/sql/data/error-notransaction.js @@ -0,0 +1,15 @@ +function test() { + var db = openDatabaseSync("QmlTestDB-data/error-notransaction", "1.0", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; + + try { + db.transaction(); + } catch (err) { + if (err.message == "transaction: missing callback") + r = "passed"; + else + r = "WRONG ERROR="+err.message; + } + + return r; +} diff --git a/tests/auto/declarative/sql/data/readonly-error.js b/tests/auto/declarative/sql/data/readonly-error.js new file mode 100644 index 0000000..ab82712 --- /dev/null +++ b/tests/auto/declarative/sql/data/readonly-error.js @@ -0,0 +1,28 @@ +function test() { + var r="transaction_not_finished"; + var db = openDatabaseSync("QmlTestDB-readonly-error", "1.0", "Test database from Qt autotests", 1000000); + + db.transaction( + function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)'); + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + r = "passed"; + } + ); + + try { + db.readTransaction( + function(tx) { + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + r = "FAILED"; + } + ); + } catch (err) { + if (err.message == "Read-only Transaction") + r = "passed"; + else + r = "WRONG ERROR="+err.message; + } + + return r; +} diff --git a/tests/auto/declarative/sql/data/readonly.js b/tests/auto/declarative/sql/data/readonly.js new file mode 100644 index 0000000..5ee862c --- /dev/null +++ b/tests/auto/declarative/sql/data/readonly.js @@ -0,0 +1,24 @@ +function test() { + var r="transaction_not_finished"; + var db = openDatabaseSync("QmlTestDB-readonly", "1.0", "Test database from Qt autotests", 1000000); + + db.transaction( + function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)'); + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + r = "passed"; + } + ); + + db.readTransaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting'); + if (rs.rows.item(0).salutation == 'hello') + r = "passed"; + else + r = "FAILED"; + } + ); + + return r; +} diff --git a/tests/auto/declarative/sql/data/reopen1.js b/tests/auto/declarative/sql/data/reopen1.js new file mode 100644 index 0000000..c1a8157 --- /dev/null +++ b/tests/auto/declarative/sql/data/reopen1.js @@ -0,0 +1,14 @@ +function test() { + var r="transaction_not_finished"; + var db = openDatabaseSync("QmlTestDB-reopen", "1.0", "Test database from Qt autotests", 1000000); + + db.transaction( + function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)'); + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + r = "passed"; + } + ); + + return r; +} diff --git a/tests/auto/declarative/sql/data/reopen2.js b/tests/auto/declarative/sql/data/reopen2.js new file mode 100644 index 0000000..4f7248f --- /dev/null +++ b/tests/auto/declarative/sql/data/reopen2.js @@ -0,0 +1,16 @@ +function test() { + var r="transaction_not_finished"; + var db = openDatabaseSync("QmlTestDB-reopen", "1.0", "Test database from Qt autotests", 1000000); + + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting'); + if (rs.rows.item(0).salutation == 'hello') + r = "passed"; + else + r = "FAILED"; + } + ); + + return r; +} diff --git a/tests/auto/declarative/sql/sql.pro b/tests/auto/declarative/sql/sql.pro index 48e06a2..875e9d8 100644 --- a/tests/auto/declarative/sql/sql.pro +++ b/tests/auto/declarative/sql/sql.pro @@ -1,5 +1,6 @@ load(qttest_p4) contains(QT_CONFIG,declarative): QT += declarative +QT += script macx:CONFIG -= app_bundle SOURCES += tst_sql.cpp diff --git a/tests/auto/declarative/sql/tst_sql.cpp b/tests/auto/declarative/sql/tst_sql.cpp index b5f98e3..d51b91c 100644 --- a/tests/auto/declarative/sql/tst_sql.cpp +++ b/tests/auto/declarative/sql/tst_sql.cpp @@ -43,10 +43,13 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include @@ -72,11 +75,11 @@ private slots: void checkDatabasePath(); - void validateAgainstWebkit_data(); - void validateAgainstWebkit(); - void testQml_data(); void testQml(); + void testQml_cleanopen_data(); + void testQml_cleanopen(); + void totalDatabases(); void cleanupTestCase(); @@ -109,7 +112,6 @@ void tst_sql::initTestCase() { removeRecursive(dbDir()); QDir().mkpath(dbDir()); - } void tst_sql::cleanupTestCase() @@ -131,44 +133,37 @@ void tst_sql::checkDatabasePath() QVERIFY(engine->offlineStoragePath().contains("OfflineStorage")); } +static const int total_databases_created_by_tests = 10; void tst_sql::testQml_data() { QTest::addColumn("jsfile"); // The input file - QTest::addColumn("result"); // The required output from the js test() function - QTest::addColumn("databases"); // The number of databases that should have been created - QTest::addColumn("qmlextension"); // Things WebKit can't do // Each test should use a newly named DB to avoid inter-test dependencies - QTest::newRow("creation") << "data/creation.js" << "passed" << 1 << false; - QTest::newRow("creation-a") << "data/creation-a.js" << "passed" << 2 << false; - QTest::newRow("changeversion") << "data/changeversion.js" << "passed" << 3 << false; - QTest::newRow("selection") << "data/selection.js" << "passed" << 4 << false; - QTest::newRow("selection-bindnames") << "data/selection-bindnames.js" << "passed" << 5 << true; - QTest::newRow("iteration") << "data/iteration.js" << "passed" << 6 << false; - QTest::newRow("error-a") << "data/error-a.js" << "passed" << 7 << false; -} - -void tst_sql::validateAgainstWebkit_data() -{ - QTest::addColumn("jsfile"); // The input file - QTest::addColumn("result"); // The required output from the js test() function - QTest::addColumn("databases"); // The number of databases that should have been created - QTest::addColumn("qmlextension"); // Things WebKit can't do - QTest::newRow("creation") << "data/creation.js" << "passed" << 1 << false; + QTest::newRow("creation") << "data/creation.js"; + QTest::newRow("creation-a") << "data/creation-a.js"; + QTest::newRow("changeversion") << "data/changeversion.js"; + QTest::newRow("readonly") << "data/readonly.js"; + QTest::newRow("readonly-error") << "data/readonly-error.js"; + QTest::newRow("selection") << "data/selection.js"; + QTest::newRow("selection-bindnames") << "data/selection-bindnames.js"; + QTest::newRow("iteration") << "data/iteration.js"; + QTest::newRow("error-a") << "data/error-a.js"; + QTest::newRow("error-notransaction") << "data/error-notransaction.js"; + QTest::newRow("reopen1") << "data/reopen1.js"; + QTest::newRow("reopen2") << "data/reopen2.js"; + + // If you add a test, you should usually use a new database in the + // test - in which case increment total_databases_created_by_tests above. } +/* void tst_sql::validateAgainstWebkit() { // Validates tests against WebKit (HTML5) support. // - QFETCH(QString, jsfile); QFETCH(QString, result); QFETCH(int, databases); - QFETCH(bool, qmlextension); - - if (qmlextension) // WebKit can't do it (yet?) - return; QFile f(jsfile); QVERIFY(f.open(QIODevice::ReadOnly)); @@ -180,14 +175,14 @@ void tst_sql::validateAgainstWebkit() QEXPECT_FAIL("","WebKit doesn't support openDatabaseSync yet", Continue); QCOMPARE(webpage.mainFrame()->evaluateJavaScript(js).toString(),result); - /* + QTest::qWait(100); // WebKit crashes if you quit it too fast QWebSecurityOrigin origin = webpage.mainFrame()->securityOrigin(); QList dbs = origin.databases(); QCOMPARE(dbs.count(), databases); - */ } +*/ void tst_sql::testQml() { @@ -195,8 +190,6 @@ void tst_sql::testQml() // that have been validated against Webkit. // QFETCH(QString, jsfile); - QFETCH(QString, result); - QFETCH(int, databases); QString qml= "import Qt 4.6\n" @@ -206,8 +199,31 @@ void tst_sql::testQml() QmlComponent component(engine, qml.toUtf8(), QUrl::fromLocalFile(SRCDIR "/empty.qml")); // just a file for relative local imports QmlGraphicsText *text = qobject_cast(component.create()); QVERIFY(text != 0); - QCOMPARE(text->text(),result); - QCOMPARE(QDir(dbDir()+"/Databases").entryInfoList(QDir::Files|QDir::NoDotAndDotDot).count(), databases*2); // *2 = .ini file + .sqlite file + QCOMPARE(text->text(),QString("passed")); +} + +void tst_sql::testQml_cleanopen_data() +{ + QTest::addColumn("jsfile"); // The input file + QTest::newRow("reopen1") << "data/reopen1.js"; + QTest::newRow("reopen2") << "data/reopen2.js"; +} + +void tst_sql::testQml_cleanopen() +{ + // Same as testQml, but clean connections between tests, + // making it more like the tests are running in new processes. + testQml(); + + QmlEnginePrivate::getScriptEngine(engine)->collectGarbage(); // close databases + foreach (QString dbname, QSqlDatabase::connectionNames()) { + QSqlDatabase::removeDatabase(dbname); + } +} + +void tst_sql::totalDatabases() +{ + QCOMPARE(QDir(dbDir()+"/Databases").entryInfoList(QDir::Files|QDir::NoDotAndDotDot).count(), total_databases_created_by_tests*2); } QTEST_MAIN(tst_sql) -- cgit v0.12