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 | |
39 | class tst_qqmlsqldatabase : public QQmlDataTest |
40 | { |
41 | Q_OBJECT |
42 | public: |
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 | |
56 | private 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 | |
70 | private: |
71 | QString dbDir() const; |
72 | QQmlEngine *engine; |
73 | }; |
74 | |
75 | void 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 | |
87 | void 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 | |
96 | void tst_qqmlsqldatabase::cleanupTestCase() |
97 | { |
98 | if (engine->offlineStoragePath().isEmpty()) |
99 | QSKIP("offlineStoragePath is empty, skip this test." ); |
100 | removeRecursive(dirname: dbDir()); |
101 | } |
102 | |
103 | QString 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 | |
110 | void 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 | |
120 | static const int total_databases_created_by_tests = 13; |
121 | void 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 | |
148 | void 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 | |
172 | void 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 | |
180 | void 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 | |
196 | void 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 | |
204 | void 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 | |
220 | QTEST_MAIN(tst_qqmlsqldatabase) |
221 | |
222 | #include "tst_qqmlsqldatabase.moc" |
223 | |