1
/****************************************************************************
2
**
3
** Copyright (C) 2011 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
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
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
    {"touch/dials Example", "touch/dials", "dials", 10, -1},
132
    {"touch/fingerpaint Example", "touch/fingerpaint", "fingerpaint", 10, -1},
133
    {"touch/knobs Example", "touch/knobs", "knobs", 10, -1},
134
    {"touch/pinchzoom Example", "touch/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"