summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWarwick Allison <warwick.allison@nokia.com>2009-09-18 02:57:15 (GMT)
committerWarwick Allison <warwick.allison@nokia.com>2009-09-18 02:57:15 (GMT)
commit62615291477b6268263fad5c1f767d6291b541c4 (patch)
treebdd566135354472f6494b0cad264e324864da59e
parentf2d5e83afcc30de4d77b30f90df6f82e53796d59 (diff)
downloadQt-62615291477b6268263fad5c1f767d6291b541c4.zip
Qt-62615291477b6268263fad5c1f767d6291b541c4.tar.gz
Qt-62615291477b6268263fad5c1f767d6291b541c4.tar.bz2
Make SqlDatabase testable and add a basic test.
-rw-r--r--src/declarative/qml/qmlengine.cpp66
-rw-r--r--src/declarative/qml/qmlengine.h4
-rw-r--r--src/declarative/qml/qmlengine_p.h1
-rw-r--r--src/declarative/qml/qmlsqldatabase.cpp41
-rw-r--r--tests/auto/declarative/sql/data/test1.js18
-rw-r--r--tests/auto/declarative/sql/tst_sql.cpp108
6 files changed, 198 insertions, 40 deletions
diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp
index 1aa2cb9..a00b175 100644
--- a/src/declarative/qml/qmlengine.cpp
+++ b/src/declarative/qml/qmlengine.cpp
@@ -108,6 +108,45 @@ QScriptValue desktopOpenUrl(QScriptContext *ctxt, QScriptEngine *e)
return e->newVariant(QVariant(ret));
}
+// XXX Something like this should be exported by Qt.
+static QString userLocalDataPath(const QString& app)
+{
+ QString result;
+
+#ifdef Q_OS_WIN
+#ifndef Q_OS_WINCE
+ QLibrary library(QLatin1String("shell32"));
+#else
+ QLibrary library(QLatin1String("coredll"));
+#endif // Q_OS_WINCE
+ typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPWSTR, int, BOOL);
+ GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPathW");
+ if (SHGetSpecialFolderPath) {
+ wchar_t path[MAX_PATH];
+ SHGetSpecialFolderPath(0, path, CSIDL_APPDATA, FALSE);
+ result = QString::fromWCharArray(path);
+ }
+#endif // Q_OS_WIN
+
+#ifdef Q_OS_MAC
+ result = QLatin1String(qgetenv("HOME"));
+ result += "/Library/Application Support";
+#else
+ if (result.isEmpty()) {
+ // Fallback: UNIX style
+ result = QLatin1String(qgetenv("XDG_DATA_HOME"));
+ if (result.isEmpty()) {
+ result = QLatin1String(qgetenv("HOME"));
+ result += QLatin1String("/.local/share");
+ }
+ }
+#endif
+
+ result += QLatin1Char('/');
+ result += app;
+ return result;
+}
+
QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e)
: rootContext(0), currentExpression(0),
isDebugging(false), contextClass(0), objectClass(0), valueTypeClass(0),
@@ -121,6 +160,7 @@ QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e)
qtObject.setProperty(QLatin1String("DesktopServices"), desktopObject);
scriptEngine.globalObject().setProperty(QLatin1String("Qt"), qtObject);
+ offlineStoragePath = userLocalDataPath(QLatin1String("Nokia/Qt/QML/OfflineStorage"));
qt_add_qmlxmlhttprequest(&scriptEngine);
qt_add_qmlsqldatabase(&scriptEngine);
@@ -1601,6 +1641,32 @@ void QmlEngine::addImportPath(const QString& path)
}
/*!
+ \property QmlEngine::offlineStoragePath
+ \brief the directory for storing offline user data
+
+ Returns the directory where SQL and other offline
+ storage is placed.
+
+ QFxWebView and the SQL databases created with openDatabase()
+ are stored here.
+
+ The default is Nokia/Qt/QML/Databases/ in the platform-standard
+ user application data directory.
+*/
+void QmlEngine::setOfflineStoragePath(const QString& dir)
+{
+ Q_D(QmlEngine);
+ d->offlineStoragePath = dir;
+}
+
+QString QmlEngine::offlineStoragePath() const
+{
+ Q_D(const QmlEngine);
+ return d->offlineStoragePath;
+}
+
+
+/*!
\internal
Adds information to \a imports such that subsequent calls to resolveType()
diff --git a/src/declarative/qml/qmlengine.h b/src/declarative/qml/qmlengine.h
index 8caa505..b4233e4 100644
--- a/src/declarative/qml/qmlengine.h
+++ b/src/declarative/qml/qmlengine.h
@@ -65,6 +65,7 @@ class QScriptContext;
class QNetworkAccessManager;
class Q_DECLARATIVE_EXPORT QmlEngine : public QObject
{
+ Q_PROPERTY(QString offlineStoragePath READ offlineStoragePath WRITE setOfflineStoragePath)
Q_OBJECT
public:
QmlEngine(QObject *p = 0);
@@ -79,6 +80,9 @@ public:
void setNetworkAccessManager(QNetworkAccessManager *);
QNetworkAccessManager *networkAccessManager() const;
+ void setOfflineStoragePath(const QString& dir);
+ QString offlineStoragePath() const;
+
QUrl baseUrl() const;
void setBaseUrl(const QUrl &);
diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h
index 0eeae0c..cca8355 100644
--- a/src/declarative/qml/qmlengine_p.h
+++ b/src/declarative/qml/qmlengine_p.h
@@ -201,6 +201,7 @@ public:
QmlCompositeTypeManager typeManager;
QStringList fileImportPath;
+ QString offlineStoragePath;
mutable quint32 uniqueId;
quint32 getUniqueId() const {
diff --git a/src/declarative/qml/qmlsqldatabase.cpp b/src/declarative/qml/qmlsqldatabase.cpp
index 144a170..fda0342 100644
--- a/src/declarative/qml/qmlsqldatabase.cpp
+++ b/src/declarative/qml/qmlsqldatabase.cpp
@@ -217,45 +217,6 @@ static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptE
return engine->undefinedValue();
}
-// XXX Something like this should be exported by Qt.
-static QString userLocalDataPath(const QString& app)
-{
- QString result;
-
-#ifdef Q_OS_WIN
-#ifndef Q_OS_WINCE
- QLibrary library(QLatin1String("shell32"));
-#else
- QLibrary library(QLatin1String("coredll"));
-#endif // Q_OS_WINCE
- typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPWSTR, int, BOOL);
- GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPathW");
- if (SHGetSpecialFolderPath) {
- wchar_t path[MAX_PATH];
- SHGetSpecialFolderPath(0, path, CSIDL_APPDATA, FALSE);
- result = QString::fromWCharArray(path);
- }
-#endif // Q_OS_WIN
-
-#ifdef Q_OS_MAC
- result = QLatin1String(qgetenv("HOME"));
- result += "/Library/Application Support";
-#else
- if (result.isEmpty()) {
- // Fallback: UNIX style
- result = QLatin1String(qgetenv("XDG_DATA_HOME"));
- if (result.isEmpty()) {
- result = QLatin1String(qgetenv("HOME"));
- result += QLatin1String("/.local/share");
- }
- }
-#endif
-
- result += QLatin1Char('/');
- result += app;
- return result;
-}
-
static QScriptValue qmlsqldatabase_open(QScriptContext *context, QScriptEngine *engine)
{
@@ -279,7 +240,7 @@ static QScriptValue qmlsqldatabase_open(QScriptContext *context, QScriptEngine *
database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid);
}
if (!database.isOpen()) {
- QString basename = userLocalDataPath(QLatin1String("Nokia/Qt/QML/Databases/"));
+ QString basename = QmlEnginePrivate::get(engine)->offlineStoragePath + "/Databases/";
QDir().mkpath(basename);
basename += dbid;
database.setDatabaseName(basename+QLatin1String(".sqllite"));
diff --git a/tests/auto/declarative/sql/data/test1.js b/tests/auto/declarative/sql/data/test1.js
new file mode 100644
index 0000000..2ae9988
--- /dev/null
+++ b/tests/auto/declarative/sql/data/test1.js
@@ -0,0 +1,18 @@
+var db;
+db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000);
+var r="testerror";
+
+// Asynchronous in WebKit, so must wait before calling test()
+db.transaction(function(tx) {
+ r = "passed";
+ tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)', [],
+ function(tx, rs) { }, function(tx, error) { r="FAILED: "+error.message });
+ tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ],
+ function(tx, rs) { }, function(tx, error) { r="FAILED: "+error.message });
+}, function(tx, error) { r="TXFAILED: "+error.message }, function(tx, result) { if (r=="testerror") r="passed" });
+
+
+function test()
+{
+ return r;
+}
diff --git a/tests/auto/declarative/sql/tst_sql.cpp b/tests/auto/declarative/sql/tst_sql.cpp
new file mode 100644
index 0000000..3179c99
--- /dev/null
+++ b/tests/auto/declarative/sql/tst_sql.cpp
@@ -0,0 +1,108 @@
+#include <qtest.h>
+#include "../../../shared/util.h"
+#include <QtDeclarative/qmlengine.h>
+#include <QtDeclarative/qmlcomponent.h>
+#include <QtDeclarative/qfxtext.h>
+#include <QtWebKit/qwebpage.h>
+#include <QtWebKit/qwebframe.h>
+#include <QtWebKit/qwebdatabase.h>
+#include <QtWebKit/qwebsecurityorigin.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+
+class tst_sql : public QObject
+{
+ Q_OBJECT
+public:
+ tst_sql() {}
+
+private slots:
+ void verifyAgainstWebKit_data();
+ void verifyAgainstWebKit();
+
+private:
+ QmlEngine engine;
+};
+
+class QWebPageWithJavaScriptConsoleMessages : public QWebPage {
+public:
+ void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID)
+ {
+qDebug() << sourceID << ":" << lineNumber << ":" << message;
+ }
+};
+
+void removeRecursive(const QString& dirname)
+{
+ QDir dir(dirname);
+ QFileInfoList entries(dir.entryInfoList(QDir::Dirs|QDir::Files|QDir::NoDotAndDotDot));
+ for (int i = 0; i < entries.count(); ++i)
+ if (entries[i].isDir())
+ removeRecursive(entries[i].filePath());
+ else
+ dir.remove(entries[i].fileName());
+ QDir().rmdir(dirname);
+}
+
+
+void tst_sql::verifyAgainstWebKit_data()
+{
+ QTest::addColumn<QString>("jsfile"); // The input file
+ QTest::addColumn<QString>("result"); // The required output from the js test() function
+ QTest::addColumn<int>("databases"); // The number of databases that should have been created
+
+ QTest::newRow("basic creation") << "data/test1.js" << "passed" << 1;
+}
+
+void tst_sql::verifyAgainstWebKit()
+{
+ // Tests QML SQL Database support, and tests the same thing against
+ // WebKit (HTML5) support to validate the test.
+ //
+ // WebKit SQL is asynchronous, so tests are divided into code plus a test()
+ // function which is executed "later" (via QTRY_).
+ //
+ QFETCH(QString, jsfile);
+ QFETCH(QString, result);
+ QFETCH(int, databases);
+
+ QString tmpdir = QString(SRCDIR)+"/output";
+
+ // QML...
+ QString qml=
+ "import Qt 4.6\n"
+ "Text { Script { source: \""+jsfile+"\" } text: test() }";
+
+ // Check default storage path (we can't use it since we don't want to mess with user's data)
+ QVERIFY(engine.offlineStoragePath().contains("Nokia"));
+ QVERIFY(engine.offlineStoragePath().contains("OfflineStorage"));
+ engine.setOfflineStoragePath(tmpdir);
+ QmlComponent component(&engine, qml.toUtf8(), QUrl::fromLocalFile(SRCDIR "/empty.qml")); // just a file for relative local imports
+ QFxText *text = qobject_cast<QFxText*>(component.create());
+ QVERIFY(text != 0);
+ QCOMPARE(text->text(),result);
+ QCOMPARE(QDir(tmpdir+"/Databases").entryInfoList(QDir::Files|QDir::NoDotAndDotDot).count(), databases*2); // *2 = .ini file + .sqlite file
+
+ // WebKit...
+ QFile f(jsfile);
+ QVERIFY(f.open(QIODevice::ReadOnly));
+ QString js=f.readAll();
+
+ QWebPageWithJavaScriptConsoleMessages webpage;
+ QDir().mkpath(tmpdir);
+ webpage.settings()->setOfflineStoragePath(tmpdir);
+ webpage.mainFrame()->evaluateJavaScript(js);
+ QTest::qWait(200); // WebKit db access is asynchronous
+ QTRY_COMPARE(webpage.mainFrame()->evaluateJavaScript("test()").toString(),result);
+
+ QWebSecurityOrigin origin = webpage.mainFrame()->securityOrigin();
+ QList<QWebDatabase> dbs = origin.databases();
+ QCOMPARE(dbs.count(), databases);
+
+
+ removeRecursive(tmpdir);
+}
+
+QTEST_MAIN(tst_sql)
+
+#include "tst_sql.moc"