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
29#include <qtest.h>
30#include <QLibraryInfo>
31#include <QDir>
32#include <QDebug>
33#include <QtQuick/QQuickItem>
34#include <QtQuick/QQuickView>
35#include <QQmlComponent>
36#include <QQmlEngine>
37#include <QQmlError>
38
39static QtMessageHandler testlibMsgHandler = nullptr;
40void msgHandlerFilter(QtMsgType type, const QMessageLogContext &ctxt, const QString &msg)
41{
42 if (type == QtCriticalMsg || type == QtFatalMsg)
43 (*testlibMsgHandler)(type, ctxt, msg);
44}
45
46class tst_examples : public QObject
47{
48 Q_OBJECT
49public:
50 tst_examples();
51 ~tst_examples();
52
53private slots:
54 void init();
55 void cleanup();
56
57 void sgexamples_data();
58 void sgexamples();
59 void sgsnippets_data();
60 void sgsnippets();
61
62 void namingConvention();
63private:
64 QStringList excludedDirs;
65 QStringList excludedFiles;
66
67 void namingConvention(const QDir &);
68 QStringList findQmlFiles(const QDir &);
69
70 QQmlEngine engine;
71};
72
73tst_examples::tst_examples()
74{
75 // Add files to exclude here
76 excludedFiles << "snippets/qml/listmodel/listmodel.qml"; //Just a ListModel, no root QQuickItem
77 excludedFiles << "snippets/qml/tablemodel/fruit-example-delegatechooser.qml"; // Requires QtQuick.Controls import.
78
79 // Add directories you want excluded here
80 excludedDirs << "shared"; //Not an example
81 excludedDirs << "snippets/qml/path"; //No root QQuickItem
82 excludedDirs << "examples/qml/qmlextensionplugins"; //Requires special import search path
83
84 // These snippets are not expected to run on their own.
85 excludedDirs << "snippets/qml/visualdatamodel_rootindex";
86 excludedDirs << "snippets/qml/qtbinding";
87 excludedDirs << "snippets/qml/imports";
88 excludedFiles << "snippets/qml/image-ext.qml";
89 excludedFiles << "examples/quick/shapes/content/main.qml"; // relies on resources
90 excludedFiles << "examples/quick/shapes/content/interactive.qml"; // relies on resources
91
92#if !QT_CONFIG(opengl)
93 //No support for Particles
94 excludedFiles << "examples/qml/dynamicscene/dynamicscene.qml";
95 excludedFiles << "examples/quick/animation/basics/color-animation.qml";
96 excludedFiles << "examples/quick/particles/affectors/content/age.qml";
97 excludedFiles << "examples/quick/touchinteraction/multipointtouch/bearwhack.qml";
98 excludedFiles << "examples/quick/touchinteraction/multipointtouch/multiflame.qml";
99 excludedDirs << "examples/quick/particles";
100 // No Support for ShaderEffect
101 excludedFiles << "src/quick/doc/snippets/qml/animators.qml";
102#endif
103
104}
105
106tst_examples::~tst_examples()
107{
108}
109
110void tst_examples::init()
111{
112 if (!qstrcmp(str1: QTest::currentTestFunction(), str2: "sgsnippets"))
113 testlibMsgHandler = qInstallMessageHandler(msgHandlerFilter);
114}
115
116void tst_examples::cleanup()
117{
118 if (!qstrcmp(str1: QTest::currentTestFunction(), str2: "sgsnippets"))
119 qInstallMessageHandler(testlibMsgHandler);
120}
121
122/*
123This tests that the examples follow the naming convention required
124to have them tested by the examples() test.
125*/
126void tst_examples::namingConvention(const QDir &d)
127{
128 for (int ii = 0; ii < excludedDirs.count(); ++ii) {
129 QString s = excludedDirs.at(i: ii);
130 if (d.absolutePath().endsWith(s))
131 return;
132 }
133
134 QStringList files = d.entryList(nameFilters: QStringList() << QLatin1String("*.qml"),
135 filters: QDir::Files);
136
137 bool seenQml = !files.isEmpty();
138 bool seenLowercase = false;
139
140 foreach (const QString &file, files) {
141 if (file.at(i: 0).isLower())
142 seenLowercase = true;
143 }
144
145 if (!seenQml) {
146 QStringList dirs = d.entryList(filters: QDir::Dirs | QDir::NoDotAndDotDot |
147 QDir::NoSymLinks);
148 foreach (const QString &dir, dirs) {
149 QDir sub = d;
150 sub.cd(dirName: dir);
151 namingConvention(d: sub);
152 }
153 } else if(!seenLowercase) {
154 // QTBUG-28271 don't fail, but rather warn only
155 qWarning() << QString(
156 "Directory %1 violates naming convention; expected at least one qml file "
157 "starting with lower case, got: %2"
158 ).arg(a: d.absolutePath()).arg(a: files.join(sep: ","));
159
160// QFAIL(qPrintable(QString(
161// "Directory %1 violates naming convention; expected at least one qml file "
162// "starting with lower case, got: %2"
163// ).arg(d.absolutePath()).arg(files.join(","))));
164 }
165}
166
167void tst_examples::namingConvention()
168{
169 QStringList examplesLocations;
170 examplesLocations << QLibraryInfo::location(QLibraryInfo::ExamplesPath) + QLatin1String("/qml");
171 examplesLocations << QLibraryInfo::location(QLibraryInfo::ExamplesPath) + QLatin1String("/quick");
172
173 foreach(const QString &examples, examplesLocations) {
174 QDir d(examples);
175 if (d.exists())
176 namingConvention(d);
177 }
178}
179
180QStringList tst_examples::findQmlFiles(const QDir &d)
181{
182 for (int ii = 0; ii < excludedDirs.count(); ++ii) {
183 QString s = excludedDirs.at(i: ii);
184 if (d.absolutePath().endsWith(s))
185 return QStringList();
186 }
187
188 QStringList rv;
189
190 QStringList cppfiles = d.entryList(nameFilters: QStringList() << QLatin1String("*.cpp"), filters: QDir::Files);
191 if (cppfiles.isEmpty()) {
192 QStringList files = d.entryList(nameFilters: QStringList() << QLatin1String("*.qml"),
193 filters: QDir::Files);
194 foreach (const QString &file, files) {
195 if (file.at(i: 0).isLower()) {
196 bool superContinue = false;
197 for (int ii = 0; ii < excludedFiles.count(); ++ii) {
198 QString e = excludedFiles.at(i: ii);
199 if (d.absoluteFilePath(fileName: file).endsWith(s: e)) {
200 superContinue = true;
201 break;
202 }
203 }
204 if (superContinue)
205 continue;
206 rv << d.absoluteFilePath(fileName: file);
207 }
208 }
209 }
210
211
212 QStringList dirs = d.entryList(filters: QDir::Dirs | QDir::NoDotAndDotDot |
213 QDir::NoSymLinks);
214 foreach (const QString &dir, dirs) {
215 QDir sub = d;
216 sub.cd(dirName: dir);
217 rv << findQmlFiles(d: sub);
218 }
219
220 return rv;
221}
222
223/*
224This test runs all the examples in the QtQml UI source tree and ensures
225that they start and exit cleanly.
226
227Examples are any .qml files under the examples/ directory that start
228with a lower case letter.
229*/
230void tst_examples::sgexamples_data()
231{
232 QTest::addColumn<QString>(name: "file");
233
234 QString examples = QLatin1String(SRCDIR) + "/../../../../examples/";
235
236 QStringList files;
237 files << findQmlFiles(d: QDir(examples));
238
239 foreach (const QString &file, files)
240 QTest::newRow(qPrintable(file)) << file;
241}
242
243void tst_examples::sgexamples()
244{
245 QFETCH(QString, file);
246 QQuickWindow window;
247 window.setPersistentOpenGLContext(true);
248 window.setPersistentSceneGraph(true);
249
250 QQmlComponent component(&engine, QUrl::fromLocalFile(localfile: file));
251 if (component.status() == QQmlComponent::Error)
252 qWarning() << component.errors();
253 QCOMPARE(component.status(), QQmlComponent::Ready);
254
255 QScopedPointer<QObject> object(component.beginCreate(engine.rootContext()));
256 QQuickItem *root = qobject_cast<QQuickItem *>(object: object.data());
257 if (!root)
258 component.completeCreate();
259 QVERIFY(root);
260
261 window.resize(w: 240, h: 320);
262 window.show();
263 QVERIFY(QTest::qWaitForWindowExposed(&window));
264
265 root->setParentItem(window.contentItem());
266 component.completeCreate();
267
268 qApp->processEvents();
269}
270
271void tst_examples::sgsnippets_data()
272{
273 QTest::addColumn<QString>(name: "file");
274
275 QString snippets = QLatin1String(SRCDIR) + "/../../../../src/qml/doc/snippets/qml";
276 QStringList files;
277 files << findQmlFiles(d: QDir(snippets));
278 foreach (const QString &file, files)
279 QTest::newRow(qPrintable(file)) << file;
280
281 snippets = QLatin1String(SRCDIR) + "/../../../../src/quick/doc/snippets/qml";
282 files.clear();
283 files << findQmlFiles(d: QDir(snippets));
284 foreach (const QString &file, files)
285 QTest::newRow(qPrintable(file)) << file;
286}
287
288void tst_examples::sgsnippets()
289{
290
291 QFETCH(QString, file);
292
293 QQmlComponent component(&engine, QUrl::fromLocalFile(localfile: file));
294 if (component.status() == QQmlComponent::Error)
295 qWarning() << component.errors();
296 QCOMPARE(component.status(), QQmlComponent::Ready);
297
298 QScopedPointer<QObject> object(component.beginCreate(engine.rootContext()));
299 QQuickWindow *window = qobject_cast<QQuickWindow*>(object: object.data());
300 QQuickItem *root = qobject_cast<QQuickItem *>(object: object.data());
301 if (!root && !window) {
302 component.completeCreate();
303 QVERIFY(false);
304 }
305 if (!window)
306 window = new QQuickWindow;
307
308 window->resize(w: 240, h: 320);
309 window->show();
310 QVERIFY(QTest::qWaitForWindowExposed(window));
311
312 if (root)
313 root->setParentItem(window->contentItem());
314 component.completeCreate();
315
316 qApp->processEvents();
317 if (root)
318 delete window;
319}
320
321QTEST_MAIN(tst_examples)
322
323#include "tst_examples.moc"
324

source code of qtdeclarative/tests/auto/quick/examples/tst_examples.cpp