1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28#include <qtest.h>
29#include <QtQml/qqmlengine.h>
30#include <QtQml/qqmlcomponent.h>
31#include <QtQuick/private/qquicktext_p.h>
32#include <private/qqmlengine_p.h>
33#include <QtCore/qcryptographichash.h>
34#include <QtSql/qsqldatabase.h>
35#include <QtCore/qdir.h>
36#include <QtCore/qfile.h>
37#include "../../shared/util.h"
38
39class tst_qqmlsqldatabase : public QQmlDataTest
40{
41 Q_OBJECT
42public:
43 tst_qqmlsqldatabase()
44 {
45 qApp->setApplicationName("tst_qqmlsqldatabase");
46 qApp->setOrganizationName("QtProject");
47 qApp->setOrganizationDomain("www.qt-project.org");
48 engine = new QQmlEngine;
49 }
50
51 ~tst_qqmlsqldatabase()
52 {
53 delete engine;
54 }
55
56private slots:
57 void initTestCase();
58
59 void checkDatabasePath();
60
61 void testQml_data();
62 void testQml();
63 void testQml_cleanopen_data();
64 void testQml_cleanopen();
65 void totalDatabases();
66 void upgradeDatabase();
67
68 void cleanupTestCase();
69
70private:
71 QString dbDir() const;
72 QQmlEngine *engine;
73};
74
75void removeRecursive(const QString& dirname)
76{
77 QDir dir(dirname);
78 QFileInfoList entries(dir.entryInfoList(filters: QDir::Dirs|QDir::Files|QDir::NoDotAndDotDot));
79 for (int i = 0; i < entries.count(); ++i)
80 if (entries[i].isDir())
81 removeRecursive(dirname: entries[i].filePath());
82 else
83 dir.remove(fileName: entries[i].fileName());
84 QDir().rmdir(dirName: dirname);
85}
86
87void tst_qqmlsqldatabase::initTestCase()
88{
89 if (engine->offlineStoragePath().isEmpty())
90 QSKIP("offlineStoragePath is empty, skip this test.");
91 QQmlDataTest::initTestCase();
92 removeRecursive(dirname: dbDir());
93 QDir().mkpath(dirPath: dbDir());
94}
95
96void tst_qqmlsqldatabase::cleanupTestCase()
97{
98 if (engine->offlineStoragePath().isEmpty())
99 QSKIP("offlineStoragePath is empty, skip this test.");
100 removeRecursive(dirname: dbDir());
101}
102
103QString tst_qqmlsqldatabase::dbDir() const
104{
105 static QString tmpd = QDir::tempPath()+"/tst_qqmlsqldatabase_output-"
106 + QDateTime::currentDateTime().toString(format: QLatin1String("yyyyMMddhhmmss"));
107 return tmpd;
108}
109
110void tst_qqmlsqldatabase::checkDatabasePath()
111{
112 if (engine->offlineStoragePath().isEmpty())
113 QSKIP("offlineStoragePath is empty, skip this test.");
114
115 // Check default storage path (we can't use it since we don't want to mess with user's data)
116 QVERIFY(engine->offlineStoragePath().contains("tst_qqmlsqldatabase"));
117 QVERIFY(engine->offlineStoragePath().contains("OfflineStorage"));
118}
119
120static const int total_databases_created_by_tests = 13;
121void tst_qqmlsqldatabase::testQml_data()
122{
123 QTest::addColumn<QString>(name: "jsfile"); // The input file
124
125 // Each test should use a newly named DB to avoid inter-test dependencies
126 QTest::newRow(dataTag: "creation") << "creation.js";
127 QTest::newRow(dataTag: "creation-a") << "creation-a.js";
128 QTest::newRow(dataTag: "creation") << "creation.js";
129 QTest::newRow(dataTag: "error-creation") << "error-creation.js"; // re-uses above DB
130 QTest::newRow(dataTag: "changeversion") << "changeversion.js";
131 QTest::newRow(dataTag: "readonly") << "readonly.js";
132 QTest::newRow(dataTag: "readonly-error") << "readonly-error.js";
133 QTest::newRow(dataTag: "selection") << "selection.js";
134 QTest::newRow(dataTag: "selection-bindnames") << "selection-bindnames.js";
135 QTest::newRow(dataTag: "iteration") << "iteration.js";
136 QTest::newRow(dataTag: "iteration-forwardonly") << "iteration-forwardonly.js";
137 QTest::newRow(dataTag: "error-a") << "error-a.js";
138 QTest::newRow(dataTag: "error-notransaction") << "error-notransaction.js";
139 QTest::newRow(dataTag: "error-outsidetransaction") << "error-outsidetransaction.js"; // reuse above
140 QTest::newRow(dataTag: "reopen1") << "reopen1.js";
141 QTest::newRow(dataTag: "reopen2") << "reopen2.js"; // re-uses above DB
142 QTest::newRow(dataTag: "null-values") << "nullvalues.js";
143
144 // If you add a test, you should usually use a new database in the
145 // test - in which case increment total_databases_created_by_tests above.
146}
147
148void tst_qqmlsqldatabase::testQml()
149{
150 if (engine->offlineStoragePath().isEmpty())
151 QSKIP("offlineStoragePath is empty, skip this test.");
152
153 // Tests QML SQL Database support with tests
154 // that have been validated against Webkit.
155 //
156 QFETCH(QString, jsfile);
157
158 QString qml=
159 "import QtQuick 2.0\n"
160 "import \""+jsfile+"\" as JS\n"
161 "Text { text: JS.test() }";
162
163 engine->setOfflineStoragePath(dbDir());
164 QQmlComponent component(engine);
165 component.setData(qml.toUtf8(), baseUrl: testFileUrl(fileName: "empty.qml")); // just a file for relative local imports
166 QVERIFY(!component.isError());
167 QQuickText *text = qobject_cast<QQuickText*>(object: component.create());
168 QVERIFY(text != nullptr);
169 QCOMPARE(text->text(),QString("passed"));
170}
171
172void tst_qqmlsqldatabase::testQml_cleanopen_data()
173{
174 QTest::addColumn<QString>(name: "jsfile"); // The input file
175 QTest::newRow(dataTag: "reopen1") << "reopen1.js";
176 QTest::newRow(dataTag: "reopen2") << "reopen2.js";
177 QTest::newRow(dataTag: "error-creation") << "error-creation.js"; // re-uses creation DB
178}
179
180void tst_qqmlsqldatabase::testQml_cleanopen()
181{
182 if (engine->offlineStoragePath().isEmpty())
183 QSKIP("offlineStoragePath is empty, skip this test.");
184
185 // Same as testQml, but clean connections between tests,
186 // making it more like the tests are running in new processes.
187 testQml();
188
189 engine->collectGarbage();
190
191 foreach (QString dbname, QSqlDatabase::connectionNames()) {
192 QSqlDatabase::removeDatabase(connectionName: dbname);
193 }
194}
195
196void tst_qqmlsqldatabase::totalDatabases()
197{
198 if (engine->offlineStoragePath().isEmpty())
199 QSKIP("offlineStoragePath is empty, skip this test.");
200
201 QCOMPARE(QDir(dbDir()+"/Databases").entryInfoList(QDir::Files|QDir::NoDotAndDotDot).count(), total_databases_created_by_tests*2);
202}
203
204void tst_qqmlsqldatabase::upgradeDatabase()
205{
206 QQmlComponent component(engine, testFile(fileName: "changeVersion.qml"));
207 QVERIFY(component.isReady());
208
209 QObject *object = component.create();
210 QVERIFY(object);
211 QVERIFY(object->property("version").toString().isEmpty());
212
213 QVERIFY(QMetaObject::invokeMethod(object, "create"));
214 QCOMPARE(object->property("version").toString(), QLatin1String("2"));
215
216 QVERIFY(QMetaObject::invokeMethod(object, "upgrade"));
217 QCOMPARE(object->property("version").toString(), QLatin1String("22"));
218}
219
220QTEST_MAIN(tst_qqmlsqldatabase)
221
222#include "tst_qqmlsqldatabase.moc"
223

source code of qtdeclarative/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp