|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the test suite of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "windowmanager.h" |
|
43 |
|
44 #include <QtCore/QDir> |
|
45 #include <QtCore/QString> |
|
46 #include <QtTest/QtTest> |
|
47 #include <QtCore/QProcess> |
|
48 #include <QtCore/QByteArray> |
|
49 #include <QtCore/QLibraryInfo> |
|
50 #include <QtCore/QVariant> |
|
51 #include <QtCore/QDateTime> |
|
52 #include <QtCore/QMap> |
|
53 |
|
54 // AppLaunch: Launch gui applications, keep them running a while |
|
55 // (grabbing their top level from the window manager) and send |
|
56 // them a Close event via window manager. Verify that they do not |
|
57 // not crash nor produces unexpected error output. |
|
58 // Note: Do not play with the machine while it is running as otherwise |
|
59 // the top-level find algorithm might get confused (especially on Windows). |
|
60 // Environment variables are checked to turned off some tests |
|
61 // It is currently implemented for X11 and Windows, pending an |
|
62 // implementation of the WindowManager class and deployment on |
|
63 // the other platforms. |
|
64 |
|
65 enum { defaultUpTimeMS = 3000, defaultTopLevelWindowTimeoutMS = 30000, |
|
66 defaultTerminationTimeoutMS = 35000 }; |
|
67 |
|
68 // List the examples to test (Gui examples only). |
|
69 struct Example { |
|
70 const char *name; |
|
71 const char *directory; |
|
72 const char *binary; |
|
73 unsigned priority; // 0-highest |
|
74 int upTimeMS; |
|
75 }; |
|
76 |
|
77 const struct Example examples[] = { |
|
78 {"animation/animatedtiles Example", "animation/animatedtiles", "animatedtiles", 0, -1}, |
|
79 {"animation/appchooser Example", "animation/appchooser", "appchooser", 10, -1}, |
|
80 {"animation/easing Example", "animation/easing", "easing", 10, -1}, |
|
81 {"animation/moveblocks Example", "animation/moveblocks", "moveblocks", 10, -1}, |
|
82 {"animation/states Example", "animation/states", "states", 10, -1}, |
|
83 {"animation/stickman Example", "animation/stickman", "stickman", 10, -1}, |
|
84 {"designer/calculatorbuilder Example", "designer/calculatorbuilder", "calculatorbuilder", 10, -1}, |
|
85 {"dialogs/standarddialogs Example", "dialogs/standarddialogs", "standarddialogs", 10, -1}, |
|
86 {"draganddrop/dropsite Example", "draganddrop/dropsite", "dropsite", 10, -1}, |
|
87 {"draganddrop/fridgemagnets Example", "draganddrop/fridgemagnets", "fridgemagnets", 10, -1}, |
|
88 {"draganddrop/puzzle Example", "draganddrop/puzzle", "puzzle", 10, -1}, |
|
89 {"effects/blurpicker Example", "effects/blurpicker", "blurpicker", 10, -1}, |
|
90 {"effects/customshader Example", "effects/customshader", "customshader", 10, -1}, |
|
91 {"effects/fademessage Example", "effects/fademessage", "fademessage", 10, -1}, |
|
92 {"effects/lighting Example", "effects/lighting", "lighting", 10, -1}, |
|
93 {"graphicsview/anchorlayout Example", "graphicsview/anchorlayout", "anchorlayout", 10, -1}, |
|
94 {"graphicsview/basicgraphicslayouts Example", "graphicsview/basicgraphicslayouts", "basicgraphicslayouts", 0, -1}, |
|
95 {"graphicsview/collidingmice Example", "graphicsview/collidingmice", "collidingmice", 10, -1}, |
|
96 {"graphicsview/diagramscene Example", "graphicsview/diagramscene", "diagramscene", 10, -1}, |
|
97 {"graphicsview/dragdroprobot Example", "graphicsview/dragdroprobot", "dragdroprobot", 10, -1}, |
|
98 {"graphicsview/elasticnodes Example", "graphicsview/elasticnodes", "elasticnodes", 10, -1}, |
|
99 {"graphicsview/flowlayout Example", "graphicsview/flowlayout", "flowlayout", 10, -1}, |
|
100 {"graphicsview/padnavigator Example", "graphicsview/padnavigator", "padnavigator", 0, -1}, |
|
101 {"graphicsview/portedasteroids Example", "graphicsview/portedasteroids", "portedasteroids", 10, -1}, |
|
102 {"graphicsview/portedcanvas Example", "graphicsview/portedcanvas", "portedcanvas", 10, -1}, |
|
103 {"graphicsview/weatheranchorlayout Example", "graphicsview/weatheranchorlayout", "weatheranchorlayout", 10, -1}, |
|
104 {"itemviews/addressbook Example", "itemviews/addressbook", "addressbook", 0, -1}, |
|
105 {"itemviews/basicsortfiltermodel Example", "itemviews/basicsortfiltermodel", "basicsortfiltermodel", 10, -1}, |
|
106 {"itemviews/chart Example", "itemviews/chart", "chart", 0, -1}, |
|
107 {"itemviews/coloreditorfactory Example", "itemviews/coloreditorfactory", "coloreditorfactory", 10, -1}, |
|
108 {"itemviews/combowidgetmapper Example", "itemviews/combowidgetmapper", "combowidgetmapper", 6, -1}, |
|
109 {"itemviews/customsortfiltermodel Example", "itemviews/customsortfiltermodel", "customsortfiltermodel", 6, -1}, |
|
110 {"itemviews/dirview Example", "itemviews/dirview", "dirview", 0, -1}, |
|
111 {"itemviews/editabletreemodel Example", "itemviews/editabletreemodel", "editabletreemodel", 0, -1}, |
|
112 {"itemviews/fetchmore Example", "itemviews/fetchmore", "fetchmore", 10, -1}, |
|
113 {"itemviews/frozencolumn Example", "itemviews/frozencolumn", "frozencolumn", 10, -1}, |
|
114 {"itemviews/pixelator Example", "itemviews/pixelator", "pixelator", 10, -1}, |
|
115 {"itemviews/puzzle Example", "itemviews/puzzle", "puzzle", 10, -1}, |
|
116 {"itemviews/simpledommodel Example", "itemviews/simpledommodel", "simpledommodel", 10, -1}, |
|
117 {"itemviews/simpletreemodel Example", "itemviews/simpletreemodel", "simpletreemodel", 10, -1}, |
|
118 {"itemviews/simplewidgetmapper Example", "itemviews/simplewidgetmapper", "simplewidgetmapper", 10, -1}, |
|
119 {"itemviews/spinboxdelegate Example", "itemviews/spinboxdelegate", "spinboxdelegate", 0, -1}, |
|
120 {"itemviews/stardelegate Example", "itemviews/stardelegate", "stardelegate", 10, -1}, |
|
121 {"layouts/basiclayouts Example", "layouts/basiclayouts", "basiclayouts", 0, -1}, |
|
122 {"layouts/borderlayout Example", "layouts/borderlayout", "borderlayout", 10, -1}, |
|
123 {"layouts/dynamiclayouts Example", "layouts/dynamiclayouts", "dynamiclayouts", 10, -1}, |
|
124 {"layouts/flowlayout Example", "layouts/flowlayout", "flowlayout", 10, -1}, |
|
125 {"mainwindows/application Example", "mainwindows/application", "application", 6, -1}, |
|
126 {"mainwindows/dockwidgets Example", "mainwindows/dockwidgets", "dockwidgets", 0, -1}, |
|
127 {"mainwindows/mdi Example", "mainwindows/mdi", "mdi", 0, -1}, |
|
128 {"mainwindows/menus Example", "mainwindows/menus", "menus", 10, -1}, |
|
129 {"mainwindows/recentfiles Example", "mainwindows/recentfiles", "recentfiles", 10, -1}, |
|
130 {"mainwindows/sdi Example", "mainwindows/sdi", "sdi", 10, -1}, |
|
131 {"multitouch/dials Example", "multitouch/dials", "dials", 10, -1}, |
|
132 {"multitouch/fingerpaint Example", "multitouch/fingerpaint", "fingerpaint", 10, -1}, |
|
133 {"multitouch/knobs Example", "multitouch/knobs", "knobs", 10, -1}, |
|
134 {"multitouch/pinchzoom Example", "multitouch/pinchzoom", "pinchzoom", 10, -1}, |
|
135 {"opengl/2dpainting Example", "opengl/2dpainting", "2dpainting", 10, -1}, |
|
136 {"opengl/grabber Example", "opengl/grabber", "grabber", 10, -1}, |
|
137 {"opengl/hellogl Example", "opengl/hellogl", "hellogl", 10, -1}, |
|
138 {"opengl/overpainting Example", "opengl/overpainting", "overpainting", 10, -1}, |
|
139 {"opengl/samplebuffers Example", "opengl/samplebuffers", "samplebuffers", 10, -1}, |
|
140 {"opengl/textures Example", "opengl/textures", "textures", 10, -1}, |
|
141 {"painting/basicdrawing Example", "painting/basicdrawing", "basicdrawing", 10, -1}, |
|
142 {"painting/concentriccircles Example", "painting/concentriccircles", "concentriccircles", 0, -1}, |
|
143 {"painting/fontsampler Example", "painting/fontsampler", "fontsampler", 0, -1}, |
|
144 {"painting/imagecomposition Example", "painting/imagecomposition", "imagecomposition", 10, -1}, |
|
145 {"painting/painterpaths Example", "painting/painterpaths", "painterpaths", 10, -1}, |
|
146 {"painting/svggenerator Example", "painting/svggenerator", "svggenerator", 10, -1}, |
|
147 {"painting/svgviewer Example", "painting/svgviewer", "svgviewer", 0, -1}, |
|
148 {"painting/transformations Example", "painting/transformations", "transformations", 0, -1}, |
|
149 {"qtconcurrent/imagescaling Example", "qtconcurrent/imagescaling", "imagescaling", 10, -1}, |
|
150 {"richtext/calendar Example", "richtext/calendar", "calendar", 0, -1}, |
|
151 {"richtext/orderform Example", "richtext/orderform", "orderform", 10, -1}, |
|
152 {"richtext/syntaxhighlighter Example", "richtext/syntaxhighlighter", "syntaxhighlighter", 0, -1}, |
|
153 {"richtext/textobject Example", "richtext/textobject", "textobject", 10, -1}, |
|
154 {"script/calculator Example", "script/calculator", "calculator", 6, -1}, |
|
155 {"script/qstetrix Example", "script/qstetrix", "qstetrix", 0, -1}, |
|
156 {"statemachine/eventtransitions Example", "statemachine/eventtransitions", "eventtransitions", 10, -1}, |
|
157 {"statemachine/rogue Example", "statemachine/rogue", "rogue", 10, -1}, |
|
158 {"statemachine/trafficlight Example", "statemachine/trafficlight", "trafficlight", 0, -1}, |
|
159 {"statemachine/twowaybutton Example", "statemachine/twowaybutton", "twowaybutton", 10, -1}, |
|
160 {"tutorials/addressbook/part7 Example", "tutorials/addressbook/part7", "part7", 0, -1}, |
|
161 {"webkit/fancybrowser Example", "webkit/fancybrowser", "fancybrowser", 0, 7000}, |
|
162 {"widgets/analogclock Example", "widgets/analogclock", "analogclock", 6, -1}, |
|
163 {"widgets/calculator Example", "widgets/calculator", "calculator", 6, -1}, |
|
164 {"widgets/calendarwidget Example", "widgets/calendarwidget", "calendarwidget", 10, -1}, |
|
165 {"widgets/charactermap Example", "widgets/charactermap", "charactermap", 10, -1}, |
|
166 {"widgets/codeeditor Example", "widgets/codeeditor", "codeeditor", 0, -1}, |
|
167 {"widgets/digitalclock Example", "widgets/digitalclock", "digitalclock", 10, -1}, |
|
168 {"widgets/groupbox Example", "widgets/groupbox", "groupbox", 10, -1}, |
|
169 {"widgets/icons Example", "widgets/icons", "icons", 10, -1}, |
|
170 {"widgets/imageviewer Example", "widgets/imageviewer", "imageviewer", 10, -1}, |
|
171 {"widgets/lineedits Example", "widgets/lineedits", "lineedits", 10, -1}, |
|
172 {"widgets/scribble Example", "widgets/scribble", "scribble", 10, -1}, |
|
173 {"widgets/sliders Example", "widgets/sliders", "sliders", 10, -1}, |
|
174 {"widgets/spinboxes Example", "widgets/spinboxes", "spinboxes", 10, -1}, |
|
175 {"widgets/styles Example", "widgets/styles", "styles", 0, -1}, |
|
176 {"widgets/stylesheet Example", "widgets/stylesheet", "stylesheet", 0, -1}, |
|
177 {"widgets/tablet Example", "widgets/tablet", "tablet", 10, -1}, |
|
178 {"widgets/tetrix Example", "widgets/tetrix", "tetrix", 0, -1}, |
|
179 {"widgets/tooltips Example", "widgets/tooltips", "tooltips", 10, -1}, |
|
180 {"widgets/validators Example", "widgets/validators", "validators", 10, -1}, |
|
181 {"widgets/wiggly Example", "widgets/wiggly", "wiggly", 10, -1} |
|
182 }; |
|
183 |
|
184 const struct Example demos[] = { |
|
185 {"Affine Demo", "affine", "affine", 0, -1}, |
|
186 {"Books Demo", "books", "books", 0, -1}, |
|
187 {"Browser Demo", "browser", "browser", 0, 0000}, |
|
188 {"Chip Demo", "chip", "chip", 0, -1}, |
|
189 {"Composition Demo", "composition", "composition", 0, -1}, |
|
190 {"Deform Demo", "deform", "deform", 0, -1}, |
|
191 {"Embeddeddialogs Demo", "embeddeddialogs", "embeddeddialogs", 0, -1}, |
|
192 {"Gradients Demo", "gradients", "gradients", 0, -1}, |
|
193 {"Interview Demo", "interview", "interview", 0, -1}, |
|
194 {"Mainwindow Demo", "mainwindow", "mainwindow", 0, -1}, |
|
195 {"PathStroke Demo", "pathstroke", "pathstroke", 0, -1}, |
|
196 {"Spreadsheet Demo", "spreadsheet", "spreadsheet", 0, -1}, |
|
197 {"Sub-Attac Demo", "sub-attaq", "sub-attaq", 0, -1}, |
|
198 {"TextEdit Demo", "textedit", "textedit", 0, -1}, |
|
199 {"Undo Demo", "undo", "undo", 0, -1} |
|
200 }; |
|
201 |
|
202 // Data struct used in tests, specifying paths and timeouts |
|
203 struct AppLaunchData { |
|
204 AppLaunchData(); |
|
205 void clear(); |
|
206 |
|
207 QString binary; |
|
208 QStringList args; |
|
209 QString workingDirectory; |
|
210 int upTimeMS; |
|
211 int topLevelWindowTimeoutMS; |
|
212 int terminationTimeoutMS; |
|
213 bool splashScreen; |
|
214 }; |
|
215 |
|
216 AppLaunchData::AppLaunchData() : |
|
217 upTimeMS(defaultUpTimeMS), |
|
218 topLevelWindowTimeoutMS(defaultTopLevelWindowTimeoutMS), |
|
219 terminationTimeoutMS(defaultTerminationTimeoutMS), |
|
220 splashScreen(false) |
|
221 { |
|
222 } |
|
223 |
|
224 void AppLaunchData::clear() |
|
225 { |
|
226 binary.clear(); |
|
227 args.clear(); |
|
228 workingDirectory.clear(); |
|
229 upTimeMS = defaultUpTimeMS; |
|
230 topLevelWindowTimeoutMS = defaultTopLevelWindowTimeoutMS; |
|
231 terminationTimeoutMS = defaultTerminationTimeoutMS; |
|
232 splashScreen = false; |
|
233 } |
|
234 |
|
235 Q_DECLARE_METATYPE(AppLaunchData) |
|
236 |
|
237 |
|
238 class tst_GuiAppLauncher : public QObject |
|
239 { |
|
240 Q_OBJECT |
|
241 |
|
242 public: |
|
243 // Test name (static const char title!) + data |
|
244 typedef QPair<const char*, AppLaunchData> TestDataEntry; |
|
245 typedef QList<TestDataEntry> TestDataEntries; |
|
246 |
|
247 enum { TestTools = 0x1, TestDemo = 0x2, TestExamples = 0x4, |
|
248 TestAll = TestTools|TestDemo|TestExamples }; |
|
249 |
|
250 tst_GuiAppLauncher(); |
|
251 |
|
252 private Q_SLOTS: |
|
253 void initTestCase(); |
|
254 |
|
255 void run(); |
|
256 void run_data(); |
|
257 |
|
258 void cleanupTestCase(); |
|
259 |
|
260 private: |
|
261 QString workingDir() const; |
|
262 |
|
263 private: |
|
264 bool runApp(const AppLaunchData &data, QString *errorMessage) const; |
|
265 TestDataEntries testData() const; |
|
266 |
|
267 const unsigned m_testMask; |
|
268 const unsigned m_examplePriority; |
|
269 const QString m_dir; |
|
270 const QSharedPointer<WindowManager> m_wm; |
|
271 }; |
|
272 |
|
273 // Test mask from enviroment as test lib does not allow options. |
|
274 static inline unsigned testMask() |
|
275 { |
|
276 unsigned testMask = tst_GuiAppLauncher::TestAll; |
|
277 if (!qgetenv("QT_TEST_NOTOOLS").isEmpty()) |
|
278 testMask &= ~ tst_GuiAppLauncher::TestTools; |
|
279 if (!qgetenv("QT_TEST_NOEXAMPLES").isEmpty()) |
|
280 testMask &= ~tst_GuiAppLauncher::TestExamples; |
|
281 if (!qgetenv("QT_TEST_NODEMOS").isEmpty()) |
|
282 testMask &= ~tst_GuiAppLauncher::TestDemo; |
|
283 return testMask; |
|
284 } |
|
285 |
|
286 static inline unsigned testExamplePriority() |
|
287 { |
|
288 const QByteArray priorityD = qgetenv("QT_TEST_EXAMPLE_PRIORITY"); |
|
289 if (!priorityD.isEmpty()) { |
|
290 bool ok; |
|
291 const unsigned rc = priorityD.toUInt(&ok); |
|
292 if (ok) |
|
293 return rc; |
|
294 } |
|
295 return 5; |
|
296 } |
|
297 |
|
298 tst_GuiAppLauncher::tst_GuiAppLauncher() : |
|
299 m_testMask(testMask()), |
|
300 m_examplePriority(testExamplePriority()), |
|
301 m_dir(QLatin1String(SRCDIR)), |
|
302 m_wm(WindowManager::create()) |
|
303 { |
|
304 } |
|
305 |
|
306 void tst_GuiAppLauncher::initTestCase() |
|
307 { |
|
308 QString message = QString::fromLatin1("### App Launcher test on %1 in %2 (%3)"). |
|
309 arg(QDateTime::currentDateTime().toString(), QDir::currentPath()). |
|
310 arg(QLibraryInfo::buildKey()); |
|
311 qDebug("%s", qPrintable(message)); |
|
312 qWarning("### PLEASE LEAVE THE MACHINE UNATTENDED WHILE THIS TEST IS RUNNING\n"); |
|
313 |
|
314 // Does a window manager exist on the platform? |
|
315 if (!m_wm->openDisplay(&message)) { |
|
316 QSKIP(message.toLatin1().constData(), SkipAll); |
|
317 } |
|
318 |
|
319 // Paranoia: Do we have our test file? |
|
320 const QDir workDir(m_dir); |
|
321 if (!workDir.exists()) { |
|
322 message = QString::fromLatin1("Invalid working directory %1").arg(m_dir); |
|
323 QFAIL(message.toLocal8Bit().constData()); |
|
324 } |
|
325 } |
|
326 |
|
327 void tst_GuiAppLauncher::run() |
|
328 { |
|
329 QString errorMessage; |
|
330 QFETCH(AppLaunchData, data); |
|
331 const bool rc = runApp(data, &errorMessage); |
|
332 if (!rc) // Wait for windows to disappear after kill |
|
333 WindowManager::sleepMS(500); |
|
334 QVERIFY2(rc, qPrintable(errorMessage)); |
|
335 } |
|
336 |
|
337 // Cross platform galore! |
|
338 static inline QString guiBinary(QString in) |
|
339 { |
|
340 #ifdef Q_OS_MAC |
|
341 return in + QLatin1String(".app/Contents/MacOS/") + in; |
|
342 #endif |
|
343 in[0] = in.at(0).toLower(); |
|
344 #ifdef Q_OS_WIN |
|
345 in += QLatin1String(".exe"); |
|
346 #endif |
|
347 return in; |
|
348 } |
|
349 |
|
350 void tst_GuiAppLauncher::run_data() |
|
351 { |
|
352 QTest::addColumn<AppLaunchData>("data"); |
|
353 foreach(const TestDataEntry &data, testData()) |
|
354 QTest::newRow(data.first) << data.second; |
|
355 } |
|
356 |
|
357 // Read out the examples array structures and convert to test data. |
|
358 static tst_GuiAppLauncher::TestDataEntries exampleData(unsigned priority, |
|
359 const QString &path, |
|
360 bool debug, |
|
361 const Example *exArray, |
|
362 unsigned n) |
|
363 { |
|
364 Q_UNUSED(debug) |
|
365 tst_GuiAppLauncher::TestDataEntries rc; |
|
366 const QChar slash = QLatin1Char('/'); |
|
367 AppLaunchData data; |
|
368 for (unsigned e = 0; e < n; e++) { |
|
369 const Example &example = exArray[e]; |
|
370 if (example.priority <= priority) { |
|
371 data.clear(); |
|
372 const QString examplePath = path + slash + QLatin1String(example.directory); |
|
373 data.binary = examplePath + slash; |
|
374 #ifdef Q_OS_WIN |
|
375 data.binary += debug? QLatin1String("debug/") : QLatin1String("release/"); |
|
376 #endif |
|
377 data.binary += guiBinary(QLatin1String(example.binary)); |
|
378 data.workingDirectory = examplePath; |
|
379 if (example.upTimeMS > 0) |
|
380 data.upTimeMS = example.upTimeMS; |
|
381 rc.append(tst_GuiAppLauncher::TestDataEntry(example.name, data)); |
|
382 } |
|
383 } |
|
384 return rc; |
|
385 } |
|
386 |
|
387 tst_GuiAppLauncher::TestDataEntries tst_GuiAppLauncher::testData() const |
|
388 { |
|
389 TestDataEntries rc; |
|
390 const QChar slash = QLatin1Char('/'); |
|
391 const QString binPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + slash; |
|
392 const bool debug = QLibraryInfo::buildKey().contains(QLatin1String("debug")); |
|
393 Q_UNUSED(debug) |
|
394 |
|
395 AppLaunchData data; |
|
396 |
|
397 if (m_testMask & TestTools) { |
|
398 data.binary = binPath + guiBinary(QLatin1String("Designer")); |
|
399 data.args.append(m_dir + QLatin1String("test.ui")); |
|
400 rc.append(TestDataEntry("Qt Designer", data)); |
|
401 |
|
402 data.clear(); |
|
403 data.binary = binPath + guiBinary(QLatin1String("Linguist")); |
|
404 data.splashScreen = true; |
|
405 data.upTimeMS = 5000; // Slow loading |
|
406 data.args.append(m_dir + QLatin1String("test.ts")); |
|
407 rc.append(TestDataEntry("Qt Linguist", data)); |
|
408 } |
|
409 |
|
410 if (m_testMask & TestDemo) { |
|
411 data.clear(); |
|
412 data.upTimeMS = 5000; // Startup animation |
|
413 data.binary = binPath + guiBinary(QLatin1String("qtdemo")); |
|
414 rc.append(TestDataEntry("Qt Demo", data)); |
|
415 |
|
416 const QString demosPath = QLibraryInfo::location(QLibraryInfo::DemosPath); |
|
417 if (!demosPath.isEmpty()) |
|
418 rc += exampleData(m_examplePriority, demosPath, debug, demos, sizeof(demos)/sizeof(Example)); |
|
419 } |
|
420 |
|
421 if (m_testMask & TestExamples) { |
|
422 const QString examplesPath = QLibraryInfo::location(QLibraryInfo::ExamplesPath); |
|
423 if (!examplesPath.isEmpty()) |
|
424 rc += exampleData(m_examplePriority, examplesPath, debug, examples, sizeof(examples)/sizeof(Example)); |
|
425 } |
|
426 qDebug("Running %d tests...", rc.size()); |
|
427 return rc; |
|
428 } |
|
429 |
|
430 static inline void ensureTerminated(QProcess *p) |
|
431 { |
|
432 if (p->state() != QProcess::Running) |
|
433 return; |
|
434 p->terminate(); |
|
435 if (p->waitForFinished(300)) |
|
436 return; |
|
437 p->kill(); |
|
438 if (!p->waitForFinished(500)) |
|
439 qWarning("Unable to terminate process"); |
|
440 } |
|
441 |
|
442 static const QStringList &stderrWhiteList() |
|
443 { |
|
444 static QStringList rc; |
|
445 if (rc.empty()) { |
|
446 rc << QLatin1String("QPainter::begin: Paint device returned engine == 0, type: 2") |
|
447 << QLatin1String("QPainter::setRenderHint: Painter must be active to set rendering hints") |
|
448 << QLatin1String("QPainter::setPen: Painter not active") |
|
449 << QLatin1String("QPainter::setBrush: Painter not active") |
|
450 << QLatin1String("QPainter::end: Painter not active, aborted"); |
|
451 } |
|
452 return rc; |
|
453 } |
|
454 |
|
455 bool tst_GuiAppLauncher::runApp(const AppLaunchData &data, QString *errorMessage) const |
|
456 { |
|
457 qDebug("Launching: %s\n", qPrintable(data.binary)); |
|
458 QProcess process; |
|
459 process.setProcessChannelMode(QProcess::MergedChannels); |
|
460 if (!data.workingDirectory.isEmpty()) |
|
461 process.setWorkingDirectory(data.workingDirectory); |
|
462 process.start(data.binary, data.args); |
|
463 process.closeWriteChannel(); |
|
464 if (!process.waitForStarted()) { |
|
465 *errorMessage = QString::fromLatin1("Unable to execute %1: %2").arg(data.binary, process.errorString()); |
|
466 return false; |
|
467 } |
|
468 // Get window id. |
|
469 const QString winId = m_wm->waitForTopLevelWindow(data.splashScreen ? 2 : 1, process.pid(), data.topLevelWindowTimeoutMS, errorMessage); |
|
470 if (winId.isEmpty()) { |
|
471 ensureTerminated(&process); |
|
472 return false; |
|
473 } |
|
474 qDebug("Window: %s\n", qPrintable(winId)); |
|
475 // Wait a bit, then send close |
|
476 WindowManager::sleepMS(data.upTimeMS); |
|
477 if (m_wm->sendCloseEvent(winId, process.pid(), errorMessage)) { |
|
478 qDebug("Sent close to window: %s\n", qPrintable(winId)); |
|
479 } else { |
|
480 ensureTerminated(&process); |
|
481 return false; |
|
482 } |
|
483 // Terminate |
|
484 if (!process.waitForFinished(data.terminationTimeoutMS)) { |
|
485 *errorMessage = QString::fromLatin1("%1: Timeout %2ms").arg(data.binary).arg(data.terminationTimeoutMS); |
|
486 ensureTerminated(&process); |
|
487 return false; |
|
488 } |
|
489 if (process.exitStatus() != QProcess::NormalExit) { |
|
490 *errorMessage = QString::fromLatin1("%1: Startup crash").arg(data.binary); |
|
491 return false; |
|
492 } |
|
493 |
|
494 const int exitCode = process.exitCode(); |
|
495 // check stderr |
|
496 const QStringList stderrOutput = QString::fromLocal8Bit(process.readAllStandardOutput()).split(QLatin1Char('\n')); |
|
497 foreach(const QString &stderrLine, stderrOutput) { |
|
498 // Skip expected QPainter warnings from oxygen. |
|
499 if (stderrWhiteList().contains(stderrLine)) { |
|
500 qWarning("%s: stderr: %s\n", qPrintable(data.binary), qPrintable(stderrLine)); |
|
501 } else { |
|
502 if (!stderrLine.isEmpty()) { // Split oddity gives empty messages |
|
503 *errorMessage = QString::fromLatin1("%1: Unexpected output (ex=%2): '%3'").arg(data.binary).arg(exitCode).arg(stderrLine); |
|
504 return false; |
|
505 } |
|
506 } |
|
507 } |
|
508 |
|
509 if (exitCode != 0) { |
|
510 *errorMessage = QString::fromLatin1("%1: Exit code %2").arg(data.binary).arg(exitCode); |
|
511 return false; |
|
512 } |
|
513 return true; |
|
514 } |
|
515 |
|
516 void tst_GuiAppLauncher::cleanupTestCase() |
|
517 { |
|
518 } |
|
519 |
|
520 #if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) |
|
521 QTEST_NOOP_MAIN |
|
522 #else |
|
523 QTEST_APPLESS_MAIN(tst_GuiAppLauncher) |
|
524 #endif |
|
525 |
|
526 #include "tst_guiapplauncher.moc" |