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 | |
39 | static QtMessageHandler testlibMsgHandler = nullptr; |
40 | void msgHandlerFilter(QtMsgType type, const QMessageLogContext &ctxt, const QString &msg) |
41 | { |
42 | if (type == QtCriticalMsg || type == QtFatalMsg) |
43 | (*testlibMsgHandler)(type, ctxt, msg); |
44 | } |
45 | |
46 | class tst_examples : public QObject |
47 | { |
48 | Q_OBJECT |
49 | public: |
50 | tst_examples(); |
51 | ~tst_examples(); |
52 | |
53 | private slots: |
54 | void init(); |
55 | void cleanup(); |
56 | |
57 | void sgexamples_data(); |
58 | void sgexamples(); |
59 | |
60 | void namingConvention(); |
61 | private: |
62 | QStringList excludedDirs; |
63 | QStringList excludedFiles; |
64 | |
65 | void namingConvention(const QDir &); |
66 | QStringList findQmlFiles(const QDir &); |
67 | |
68 | QQmlEngine engine; |
69 | }; |
70 | |
71 | tst_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 | |
114 | tst_examples::~tst_examples() |
115 | { |
116 | } |
117 | |
118 | void tst_examples::init() |
119 | { |
120 | if (!qstrcmp(str1: QTest::currentTestFunction(), str2: "sgsnippets" )) |
121 | testlibMsgHandler = qInstallMessageHandler(msgHandlerFilter); |
122 | } |
123 | |
124 | void tst_examples::cleanup() |
125 | { |
126 | if (!qstrcmp(str1: QTest::currentTestFunction(), str2: "sgsnippets" )) |
127 | qInstallMessageHandler(testlibMsgHandler); |
128 | } |
129 | |
130 | /* |
131 | This tests that the examples follow the naming convention required |
132 | to have them tested by the examples() test. |
133 | */ |
134 | void 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 | |
175 | void 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 | |
188 | QStringList 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 | /* |
232 | This test runs all the examples in the QtQml UI source tree and ensures |
233 | that they start and exit cleanly. |
234 | |
235 | Examples are any .qml files under the examples/ directory that start |
236 | with a lower case letter. |
237 | */ |
238 | void 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 | |
251 | void 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 | |
279 | QTEST_MAIN(tst_examples) |
280 | |
281 | #include "tst_examples.moc" |
282 | |