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

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