/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //#define QT_TST_QAPP_DEBUG #include #include #include "qabstracteventdispatcher.h" #include #include "private/qapplication_p.h" #include "private/qstylesheetstyle_p.h" #ifdef Q_OS_WINCE #include #endif #ifdef Q_OS_SYMBIAN #include #endif //TESTED_CLASS= //TESTED_FILES= #if defined(Q_OS_SYMBIAN) // In Symbian, the PluginsPath doesn't specify the only absolute path; just the dir that can be found on any drive static void addExpectedSymbianPluginsPath(QStringList& expected) { QString installPathPlugins = QDir::fromNativeSeparators(QLibraryInfo::location(QLibraryInfo::PluginsPath)); QFileInfoList driveList = QDir::drives(); QListIterator iter(driveList); while (iter.hasNext()) { QFileInfo testFi(iter.next().canonicalPath().append(installPathPlugins)); if (testFi.exists()) expected << testFi.canonicalFilePath(); } } #endif class tst_QApplication : public QObject { Q_OBJECT public: tst_QApplication(); virtual ~tst_QApplication(); public slots: void init(); void cleanup(); private slots: void sendEventsOnProcessEvents(); // this must be the first test void getSetCheck(); void staticSetup(); void alert(); void multiple_data(); void multiple(); void nonGui(); void setFont_data(); void setFont(); void args_data(); void args(); void lastWindowClosed(); void quitOnLastWindowClosed(); void closeAllWindows(); void testDeleteLater(); void testDeleteLaterProcessEvents(); void libraryPaths(); void libraryPaths_qt_plugin_path(); void libraryPaths_qt_plugin_path_2(); void sendPostedEvents(); void thread(); void desktopSettingsAware(); void setActiveWindow(); void focusChanged(); void focusOut(); void execAfterExit(); void wheelScrollLines(); void task109149(); void style(); void allWidgets(); void topLevelWidgets(); void setAttribute(); void windowsCommandLine_data(); void windowsCommandLine(); void touchEventPropagation(); void symbianNoApplicationPanes(); void symbianNeedForTraps(); void symbianLeaveThroughMain(); }; class EventSpy : public QObject { Q_OBJECT public: QList recordedEvents; bool eventFilter(QObject *, QEvent *event) { recordedEvents.append(event->type()); return false; } }; void tst_QApplication::sendEventsOnProcessEvents() { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); EventSpy spy; app.installEventFilter(&spy); QCoreApplication::postEvent(&app, new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); QVERIFY(spy.recordedEvents.contains(QEvent::User + 1)); } class MyInputContext : public QInputContext { public: MyInputContext() : QInputContext() {} QString identifierName() { return QString("NoName"); } QString language() { return QString("NoLanguage"); } void reset() {} bool isComposing() const { return false; } }; // Testing get/set functions void tst_QApplication::getSetCheck() { int argc = 0; QApplication obj1(argc, 0, QApplication::GuiServer); // QInputContext * QApplication::inputContext() // void QApplication::setInputContext(QInputContext *) MyInputContext *var1 = new MyInputContext; obj1.setInputContext(var1); QCOMPARE((QInputContext *)var1, obj1.inputContext()); QTest::ignoreMessage(QtWarningMsg, "QApplication::setInputContext: called with 0 input context"); obj1.setInputContext((QInputContext *)0); QCOMPARE((QInputContext *)var1, obj1.inputContext()); // delete var1; // No delete, since QApplication takes ownership } class CloseEventTestWindow : public QWidget { public: CloseEventTestWindow(QWidget *parent = 0) : QWidget(parent) { } void closeEvent(QCloseEvent *event) { QWidget dialog; dialog.show(); dialog.close(); hide(); event->ignore(); } }; static char *argv0; tst_QApplication::tst_QApplication() { #ifdef Q_OS_WINCE // Clean up environment previously to launching test qputenv("QT_PLUGIN_PATH", QByteArray()); #endif } tst_QApplication::~tst_QApplication() { } void tst_QApplication::init() { // TODO: Add initialization code here. // This will be executed immediately before each test is run. } void tst_QApplication::cleanup() { // TODO: Add cleanup code here. // This will be executed immediately after each test is run. } void tst_QApplication::staticSetup() { QVERIFY(!qApp); QStyle *style = QStyleFactory::create(QLatin1String("Windows")); QVERIFY(style); QApplication::setStyle(style); QPalette pal; QApplication::setPalette(pal); /*QFont font; QApplication::setFont(font);*/ int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); } // QApp subclass that exits the event loop after 150ms class TestApplication : public QApplication { public: TestApplication( int &argc, char **argv ) : QApplication( argc, argv, QApplication::GuiServer ) { startTimer( 150 ); } void timerEvent( QTimerEvent * ) { quit(); } }; void tst_QApplication::alert() { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); app.alert(0, 0); QWidget widget; QWidget widget2; app.alert(&widget, 100); widget.show(); widget2.show(); #ifdef Q_WS_X11 qt_x11_wait_for_window_manager(&widget); qt_x11_wait_for_window_manager(&widget2); #endif QTest::qWait(100); app.alert(&widget, -1); app.alert(&widget, 250); widget2.activateWindow(); QApplication::setActiveWindow(&widget2); app.alert(&widget, 0); widget.activateWindow(); QApplication::setActiveWindow(&widget); app.alert(&widget, 200); app.syncX(); } void tst_QApplication::multiple_data() { QTest::addColumn("features"); // return a list of things to try QTest::newRow( "data0" ) << QStringList( "" ); QTest::newRow( "data1" ) << QStringList( "QFont" ); QTest::newRow( "data2" ) << QStringList( "QPixmap" ); QTest::newRow( "data3" ) << QStringList( "QWidget" ); } void tst_QApplication::multiple() { QFETCH(QStringList,features); int i = 0; int argc = 0; while ( i++ < 5 ) { TestApplication app( argc, 0 ); if ( features.contains( "QFont" ) ) { // create font and force loading QFont font( "Arial", 12 ); QFontInfo finfo( font ); finfo.exactMatch(); } if ( features.contains( "QPixmap" ) ) { QPixmap pix( 100, 100 ); pix.fill( Qt::black ); } if ( features.contains( "QWidget" ) ) { QWidget widget; } QVERIFY(!app.exec()); } } void tst_QApplication::nonGui() { #ifdef Q_OS_HPUX // ### This is only to allow us to generate a test report for now. QSKIP("This test shuts down the window manager on HP-UX.", SkipAll); #endif int argc = 0; QApplication app(argc, 0, false); QCOMPARE(qApp, &app); } void tst_QApplication::setFont_data() { QTest::addColumn("family"); QTest::addColumn("pointsize"); QTest::addColumn("beforeAppConstructor"); int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); // Needed for QFontDatabase int cnt = 0; QFontDatabase fdb; QStringList families = fdb.families(); for (QStringList::const_iterator itr = families.begin(); itr != families.end(); ++itr) { if (cnt < 3) { QString family = *itr; QStringList styles = fdb.styles(family); if (styles.size() > 0) { QString style = styles.first(); QList sizes = fdb.pointSizes(family, style); if (!sizes.size()) sizes = fdb.standardSizes(); if (sizes.size() > 0) { QTest::newRow(QString("data%1a").arg(cnt).toLatin1().constData()) << family << sizes.first() << false; QTest::newRow(QString("data%1b").arg(cnt).toLatin1().constData()) << family << sizes.first() << true; } } } ++cnt; } QTest::newRow("nonexistingfont") << "nosuchfont_probably_quiteunlikely" << 0 << false; QTest::newRow("nonexistingfont") << "nosuchfont_probably_quiteunlikely" << 0 << true; QTest::newRow("largescaleable") << "smoothtimes" << 100 << false; QTest::newRow("largescaleable") << "smoothtimes" << 100 << true; QTest::newRow("largeunscaleale") << "helvetica" << 100 << false; QTest::newRow("largeunscaleale") << "helvetica" << 100 << true; } void tst_QApplication::setFont() { QFETCH( QString, family ); QFETCH( int, pointsize ); QFETCH( bool, beforeAppConstructor ); QFont font( family, pointsize ); if (beforeAppConstructor) { QApplication::setFont( font ); QCOMPARE(QApplication::font(), font); } int argc = 0; QApplication app( argc, 0, QApplication::GuiServer ); if (!beforeAppConstructor) QApplication::setFont( font ); QCOMPARE( app.font(), font ); } void tst_QApplication::args_data() { QTest::addColumn("argc_in"); QTest::addColumn("args_in"); QTest::addColumn("argc_out"); QTest::addColumn("args_out"); QTest::newRow( "App name" ) << 1 << "/usr/bin/appname" << 1 << "/usr/bin/appname"; QTest::newRow( "No arguments" ) << 0 << QString() << 0 << QString(); QTest::newRow( "App name, style" ) << 3 << "/usr/bin/appname -style motif" << 1 << "/usr/bin/appname"; QTest::newRow( "App name, style, arbitrary, reverse" ) << 5 << "/usr/bin/appname -style motif -arbitrary -reverse" << 2 << "/usr/bin/appname -arbitrary"; } void tst_QApplication::task109149() { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); QApplication::setFont(QFont("helvetica", 100)); QWidget w; w.setWindowTitle("hello"); w.show(); app.processEvents(); } static char ** QString2cstrings( const QString &args ) { static QList cache; int i; char **argarray = 0; QStringList list = args.split(' ');; argarray = new char*[list.count()+1]; for (i = 0; i < (int)list.count(); ++i ) { QByteArray l1 = list[i].toLatin1(); argarray[i] = l1.data(); cache.append(l1); } argarray[i] = 0; return argarray; } static QString cstrings2QString( char **args ) { QString string; if ( !args ) return string; int i = 0; while ( args[i] ) { string += args[i]; if ( args[i+1] ) string += " "; ++i; } return string; } void tst_QApplication::args() { QFETCH( int, argc_in ); QFETCH( QString, args_in ); QFETCH( int, argc_out ); QFETCH( QString, args_out ); char **argv = QString2cstrings( args_in ); QApplication app( argc_in, argv, QApplication::GuiServer ); QString argv_out = cstrings2QString(argv); QCOMPARE( argc_in, argc_out ); QCOMPARE( argv_out, args_out ); delete [] argv; } class CloseWidget : public QWidget { Q_OBJECT public: CloseWidget() { startTimer(500); } protected: void timerEvent(QTimerEvent *) { close(); } }; void tst_QApplication::lastWindowClosed() { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); QSignalSpy spy(&app, SIGNAL(lastWindowClosed())); QPointer dialog = new QDialog; QVERIFY(dialog->testAttribute(Qt::WA_QuitOnClose)); QTimer::singleShot(1000, dialog, SLOT(accept())); dialog->exec(); QVERIFY(dialog); QCOMPARE(spy.count(), 0); QPointerwidget = new CloseWidget; QVERIFY(widget->testAttribute(Qt::WA_QuitOnClose)); QObject::connect(&app, SIGNAL(lastWindowClosed()), widget, SLOT(deleteLater())); app.exec(); QVERIFY(!widget); QCOMPARE(spy.count(), 1); spy.clear(); #if 0 // everything is closed, so doing this should not emit lastWindowClosed() again QMetaObject::invokeMethod(dialog, "close", Qt::QueuedConnection); QTimer::singleShot(1000, &app, SLOT(quit())); app.exec(); QCOMPARE(spy.count(), 0); #endif delete dialog; // show 3 windows, close them, should only get lastWindowClosed once QWidget w1; QWidget w2; QWidget w3; w1.show(); w2.show(); w3.show(); QTimer::singleShot(1000, &app, SLOT(closeAllWindows())); app.exec(); QCOMPARE(spy.count(), 1); } class QuitOnLastWindowClosedDialog : public QDialog { Q_OBJECT public: QPushButton *okButton; QuitOnLastWindowClosedDialog() { QHBoxLayout *hbox = new QHBoxLayout(this); okButton = new QPushButton("&ok", this); hbox->addWidget(okButton); connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); connect(okButton, SIGNAL(clicked()), this, SLOT(ok_clicked())); } public slots: void ok_clicked() { QDialog other; QTimer timer; connect(&timer, SIGNAL(timeout()), &other, SLOT(accept())); QSignalSpy spy(&timer, SIGNAL(timeout())); QSignalSpy appSpy(qApp, SIGNAL(lastWindowClosed())); timer.start(1000); other.exec(); // verify that the eventloop ran and let the timer fire QCOMPARE(spy.count(), 1); QCOMPARE(appSpy.count(), 1); } }; class QuitOnLastWindowClosedWindow : public QWidget { Q_OBJECT public: QuitOnLastWindowClosedWindow() { } public slots: void execDialogThenShow() { QDialog dialog; QTimer timer1; connect(&timer1, SIGNAL(timeout()), &dialog, SLOT(accept())); QSignalSpy spy1(&timer1, SIGNAL(timeout())); timer1.setSingleShot(true); timer1.start(1000); dialog.exec(); QCOMPARE(spy1.count(), 1); show(); } }; void tst_QApplication::quitOnLastWindowClosed() { { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); QuitOnLastWindowClosedDialog d; d.show(); QTimer::singleShot(1000, d.okButton, SLOT(animateClick())); QSignalSpy appSpy(&app, SIGNAL(lastWindowClosed())); app.exec(); // lastWindowClosed() signal should only be sent after the last dialog is closed QCOMPARE(appSpy.count(), 2); } { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); QSignalSpy appSpy(&app, SIGNAL(lastWindowClosed())); QDialog dialog; QTimer timer1; connect(&timer1, SIGNAL(timeout()), &dialog, SLOT(accept())); QSignalSpy spy1(&timer1, SIGNAL(timeout())); timer1.setSingleShot(true); timer1.start(1000); dialog.exec(); QCOMPARE(spy1.count(), 1); QCOMPARE(appSpy.count(), 0); QTimer timer2; connect(&timer2, SIGNAL(timeout()), &app, SLOT(quit())); QSignalSpy spy2(&timer2, SIGNAL(timeout())); timer2.setSingleShot(true); timer2.start(1000); int returnValue = app.exec(); QCOMPARE(returnValue, 0); QCOMPARE(spy2.count(), 1); QCOMPARE(appSpy.count(), 0); } { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); QTimer timer; timer.setInterval(100); QSignalSpy spy(&app, SIGNAL(aboutToQuit())); QSignalSpy spy2(&timer, SIGNAL(timeout())); QPointer mainWindow = new QMainWindow; QPointer dialog = new QDialog(mainWindow); QVERIFY(app.quitOnLastWindowClosed()); QVERIFY(mainWindow->testAttribute(Qt::WA_QuitOnClose)); QVERIFY(dialog->testAttribute(Qt::WA_QuitOnClose)); mainWindow->show(); dialog->show(); timer.start(); QTimer::singleShot(1000, mainWindow, SLOT(close())); // This should quit the application QTimer::singleShot(2000, &app, SLOT(quit())); // This makes sure we quit even if it didn't app.exec(); QCOMPARE(spy.count(), 1); QVERIFY(spy2.count() < 15); // Should be around 10 if closing caused the quit } { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); QTimer timer; timer.setInterval(100); QSignalSpy spy(&app, SIGNAL(aboutToQuit())); QSignalSpy spy2(&timer, SIGNAL(timeout())); QPointer mainWindow = new CloseEventTestWindow; QVERIFY(app.quitOnLastWindowClosed()); QVERIFY(mainWindow->testAttribute(Qt::WA_QuitOnClose)); mainWindow->show(); timer.start(); QTimer::singleShot(1000, mainWindow, SLOT(close())); // This should quit the application QTimer::singleShot(2000, &app, SLOT(quit())); // This makes sure we quit even if it didn't app.exec(); QCOMPARE(spy.count(), 1); QVERIFY(spy2.count() > 15); // Should be around 20 if closing did not caused the quit } { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); QSignalSpy appSpy(&app, SIGNAL(lastWindowClosed())); // exec a dialog for 1 second, then show the window QuitOnLastWindowClosedWindow window; QTimer::singleShot(0, &window, SLOT(execDialogThenShow())); QTimer timer; QSignalSpy timerSpy(&timer, SIGNAL(timeout())); connect(&timer, SIGNAL(timeout()), &window, SLOT(close())); timer.setSingleShot(true); timer.start(2000); int returnValue = app.exec(); QCOMPARE(returnValue, 0); // failure here means the timer above didn't fire, and the // quit was caused the the dialog being closed (not the window) QCOMPARE(timerSpy.count(), 1); QCOMPARE(appSpy.count(), 2); } } class PromptOnCloseWidget : public QWidget { public: void closeEvent(QCloseEvent *event) { QMessageBox *messageBox = new QMessageBox(this); messageBox->setWindowTitle("Unsaved data"); messageBox->setText("Would you like to save or discard your current data?"); messageBox->setStandardButtons(QMessageBox::Save|QMessageBox::Discard|QMessageBox::Cancel); messageBox->setDefaultButton(QMessageBox::Save); messageBox->show(); QTest::qWaitForWindowShown(messageBox); // verify that all windows are visible foreach (QWidget *w, qApp->topLevelWidgets()) QVERIFY(w->isVisible()); // flush event queue qApp->processEvents(); // close all windows qApp->closeAllWindows(); if (messageBox->standardButton(messageBox->clickedButton()) == QMessageBox::Cancel) event->ignore(); else event->accept(); delete messageBox; } }; void tst_QApplication::closeAllWindows() { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); // create some windows new QWidget; new QWidget; new QWidget; // show all windows foreach (QWidget *w, app.topLevelWidgets()) { w->show(); QTest::qWaitForWindowShown(w); } // verify that they are visible foreach (QWidget *w, app.topLevelWidgets()) QVERIFY(w->isVisible()); // empty event queue app.processEvents(); // close all windows app.closeAllWindows(); // all windows should no longer be visible foreach (QWidget *w, app.topLevelWidgets()) QVERIFY(!w->isVisible()); // add a window that prompts the user when closed PromptOnCloseWidget *promptOnCloseWidget = new PromptOnCloseWidget; // show all windows foreach (QWidget *w, app.topLevelWidgets()) { w->show(); QTest::qWaitForWindowShown(w); } // close the last window to open the prompt (eventloop recurses) promptOnCloseWidget->close(); // all windows should not be visible, except the one that opened the prompt foreach (QWidget *w, app.topLevelWidgets()) { if (w == promptOnCloseWidget) QVERIFY(w->isVisible()); else QVERIFY(!w->isVisible()); } qDeleteAll(app.topLevelWidgets()); } bool isPathListIncluded(const QStringList &l, const QStringList &r) { int size = r.count(); if (size > l.count()) return false; #if defined (Q_OS_WIN) Qt::CaseSensitivity cs = Qt::CaseInsensitive; #else Qt::CaseSensitivity cs = Qt::CaseSensitive; #endif int i = 0, j = 0; for ( ; i < l.count() && j < r.count(); ++i) { if (QDir::toNativeSeparators(l[i]).compare(QDir::toNativeSeparators(r[j]), cs) == 0) { ++j; i = -1; } } return j == r.count(); } #define QT_TST_QAPP_DEBUG void tst_QApplication::libraryPaths() { { #ifndef Q_OS_WINCE QString testDir = QDir::current().canonicalPath() + "/test"; #else // On Windows CE we need QApplication object to have valid // current Path. Therefore we need to identify it ourselves // here for the test. QFileInfo filePath; wchar_t module_name[MAX_PATH]; GetModuleFileName(0, module_name, MAX_PATH); filePath = QString::fromWCharArray(module_name); QString testDir = filePath.path() + "/test"; #endif QApplication::setLibraryPaths(QStringList() << testDir); QCOMPARE(QApplication::libraryPaths(), (QStringList() << testDir)); // creating QApplication adds the applicationDirPath to the libraryPath int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); QString appDirPath = QDir(app.applicationDirPath()).canonicalPath(); QStringList actual = QApplication::libraryPaths(); actual.sort(); QStringList expected = QSet::fromList((QStringList() << testDir << appDirPath)).toList(); expected.sort(); QVERIFY2(isPathListIncluded(actual, expected), qPrintable("actual:\n - " + actual.join("\n - ") + "\nexpected:\n - " + expected.join("\n - "))); } { // creating QApplication adds the applicationDirPath and plugin install path to the libraryPath int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); QString appDirPath = app.applicationDirPath(); QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath); QStringList actual = QApplication::libraryPaths(); actual.sort(); #if defined(Q_OS_SYMBIAN) QStringList expected; addExpectedSymbianPluginsPath(expected); expected << appDirPath; #else QStringList expected = QSet::fromList((QStringList() << installPathPlugins << appDirPath)).toList(); #endif expected.sort(); QVERIFY2(isPathListIncluded(actual, expected), qPrintable("actual:\n - " + actual.join("\n - ") + "\nexpected:\n - " + expected.join("\n - "))); // setting the library paths overrides everything QString testDir = QDir::currentPath() + "/test"; QApplication::setLibraryPaths(QStringList() << testDir); QVERIFY2(isPathListIncluded(QApplication::libraryPaths(), (QStringList() << testDir)), qPrintable("actual:\n - " + QApplication::libraryPaths().join("\n - ") + "\nexpected:\n - " + testDir)); } { #ifdef QT_TST_QAPP_DEBUG qDebug() << "Initial library path:" << QApplication::libraryPaths(); #endif int count = QApplication::libraryPaths().count(); #if 0 // this test doesn't work if KDE 4 is installed QCOMPARE(count, 1); // before creating QApplication, only the PluginsPath is in the libraryPaths() #endif QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath); QApplication::addLibraryPath(installPathPlugins); #ifdef QT_TST_QAPP_DEBUG qDebug() << "installPathPlugins" << installPathPlugins; qDebug() << "After adding plugins path:" << QApplication::libraryPaths(); #endif QCOMPARE(QApplication::libraryPaths().count(), count); QApplication::addLibraryPath(QDir::currentPath() + "/test"); QCOMPARE(QApplication::libraryPaths().count(), count + 1); // creating QApplication adds the applicationDirPath to the libraryPath int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); QString appDirPath = app.applicationDirPath(); qDebug() << QApplication::libraryPaths(); // On Windows CE these are identical and might also be the case for other // systems too if (appDirPath != installPathPlugins) QCOMPARE(QApplication::libraryPaths().count(), count + 2); } { int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); #ifdef QT_TST_QAPP_DEBUG qDebug() << "Initial library path:" << app.libraryPaths(); #endif int count = app.libraryPaths().count(); QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath); app.addLibraryPath(installPathPlugins); #ifdef QT_TST_QAPP_DEBUG qDebug() << "installPathPlugins" << installPathPlugins; qDebug() << "After adding plugins path:" << app.libraryPaths(); #endif QCOMPARE(app.libraryPaths().count(), count); QString appDirPath = app.applicationDirPath(); app.addLibraryPath(appDirPath); #ifdef Q_OS_WINCE app.addLibraryPath(appDirPath + "/../.."); #else app.addLibraryPath(appDirPath + "/.."); #endif #ifdef QT_TST_QAPP_DEBUG qDebug() << "appDirPath" << appDirPath; qDebug() << "After adding appDirPath && appDirPath + /..:" << app.libraryPaths(); #endif QCOMPARE(app.libraryPaths().count(), count + 1); #ifdef Q_OS_MAC app.addLibraryPath(appDirPath + "/../MacOS"); #else app.addLibraryPath(appDirPath + "/tmp/.."); #endif #ifdef QT_TST_QAPP_DEBUG qDebug() << "After adding appDirPath + /tmp/..:" << app.libraryPaths(); #endif QCOMPARE(app.libraryPaths().count(), count + 1); } } void tst_QApplication::libraryPaths_qt_plugin_path() { int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); QString appDirPath = app.applicationDirPath(); // Our hook into libraryPaths() initialization: Set the QT_PLUGIN_PATH environment variable QString installPathPluginsDeCanon = appDirPath + QString::fromLatin1("/tmp/.."); QByteArray ascii = installPathPluginsDeCanon.toAscii(); qputenv("QT_PLUGIN_PATH", ascii); QVERIFY(!app.libraryPaths().contains(appDirPath + QString::fromLatin1("/tmp/.."))); } void tst_QApplication::libraryPaths_qt_plugin_path_2() { #ifdef Q_OS_SYMBIAN QByteArray validPath = "C:\\data"; QByteArray nonExistentPath = "Z:\\nonexistent"; QByteArray pluginPath = validPath + ";" + nonExistentPath; #elif defined(Q_OS_UNIX) QByteArray validPath = QDir("/tmp").canonicalPath().toLatin1(); QByteArray nonExistentPath = "/nonexistent"; QByteArray pluginPath = validPath + ":" + nonExistentPath; #elif defined(Q_OS_WIN) # ifdef Q_OS_WINCE QByteArray validPath = "/Temp"; QByteArray nonExistentPath = "/nonexistent"; QByteArray pluginPath = validPath + ";" + nonExistentPath; # else QByteArray validPath = "C:\\windows"; QByteArray nonExistentPath = "Z:\\nonexistent"; QByteArray pluginPath = validPath + ";" + nonExistentPath; # endif #endif { // Our hook into libraryPaths() initialization: Set the QT_PLUGIN_PATH environment variable qputenv("QT_PLUGIN_PATH", pluginPath); int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); // library path list should contain the default plus the one valid path #if defined(Q_OS_SYMBIAN) // In Symbian, the PluginsPath doesn't specify the only absolute path; just the dir that can be found on any drive QStringList expected; addExpectedSymbianPluginsPath(expected); expected << QDir(app.applicationDirPath()).canonicalPath() << QDir(QDir::fromNativeSeparators(QString::fromLatin1(validPath))).canonicalPath(); #else QStringList expected = QStringList() << QLibraryInfo::location(QLibraryInfo::PluginsPath) << QDir(app.applicationDirPath()).canonicalPath() << QDir(QDir::fromNativeSeparators(QString::fromLatin1(validPath))).canonicalPath(); # ifdef Q_OS_WINCE expected = QSet::fromList(expected).toList(); # endif #endif QVERIFY2(isPathListIncluded(app.libraryPaths(), expected), qPrintable("actual:\n - " + app.libraryPaths().join("\n - ") + "\nexpected:\n - " + expected.join("\n - "))); } { int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); // library paths are initialized by the QApplication, setting // the environment variable here doesn't work qputenv("QT_PLUGIN_PATH", pluginPath); // library path list should contain the default #if defined(Q_OS_SYMBIAN) QStringList expected; addExpectedSymbianPluginsPath(expected); expected << app.applicationDirPath(); #else QStringList expected = QStringList() << QLibraryInfo::location(QLibraryInfo::PluginsPath) << app.applicationDirPath(); # ifdef Q_OS_WINCE expected = QSet::fromList(expected).toList(); # endif #endif QVERIFY(isPathListIncluded(app.libraryPaths(), expected)); qputenv("QT_PLUGIN_PATH", QByteArray()); } } class SendPostedEventsTester : public QObject { Q_OBJECT public: QList eventSpy; bool event(QEvent *e); private slots: void doTest(); }; bool SendPostedEventsTester::event(QEvent *e) { eventSpy.append(e->type()); return QObject::event(e); } void SendPostedEventsTester::doTest() { QPointer p = this; QApplication::postEvent(this, new QEvent(QEvent::User)); // DeferredDelete should not be delivered until returning from this function QApplication::postEvent(this, new QEvent(QEvent::DeferredDelete)); QEventLoop eventLoop; QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); eventLoop.exec(); QVERIFY(p != 0); QCOMPARE(eventSpy.count(), 2); QCOMPARE(eventSpy.at(0), int(QEvent::MetaCall)); QCOMPARE(eventSpy.at(1), int(QEvent::User)); eventSpy.clear(); } void tst_QApplication::sendPostedEvents() { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); SendPostedEventsTester *tester = new SendPostedEventsTester; QMetaObject::invokeMethod(tester, "doTest", Qt::QueuedConnection); QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); QPointer p = tester; (void) app.exec(); QVERIFY(p == 0); } void tst_QApplication::thread() { QThread *currentThread = QThread::currentThread(); // no app, but still have a valid thread QVERIFY(currentThread != 0); // the thread should be running and not finished QVERIFY(currentThread->isRunning()); QVERIFY(!currentThread->isFinished()); // this should probably be in the tst_QObject::thread() test, but // we put it here since we want to make sure that objects created // *before* the QApplication has a thread QObject object; QObject child(&object); QVERIFY(object.thread() == currentThread); QVERIFY(child.thread() == currentThread); { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); // current thread still valid QVERIFY(QThread::currentThread() != 0); // thread should be the same as before QCOMPARE(QThread::currentThread(), currentThread); // app's thread should be the current thread QCOMPARE(app.thread(), currentThread); // the thread should still be running and not finished QVERIFY(currentThread->isRunning()); QVERIFY(!currentThread->isFinished()); QTestEventLoop::instance().enterLoop(1); } // app dead, current thread still valid QVERIFY(QThread::currentThread() != 0); QCOMPARE(QThread::currentThread(), currentThread); // the thread should still be running and not finished QVERIFY(currentThread->isRunning()); QVERIFY(!currentThread->isFinished()); // should still have a thread QVERIFY(object.thread() == currentThread); QVERIFY(child.thread() == currentThread); // do the test again, making sure that the thread is the same as // before { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); // current thread still valid QVERIFY(QThread::currentThread() != 0); // thread should be the same as before QCOMPARE(QThread::currentThread(), currentThread); // app's thread should be the current thread QCOMPARE(app.thread(), currentThread); // the thread should be running and not finished QVERIFY(currentThread->isRunning()); QVERIFY(!currentThread->isFinished()); // should still have a thread QVERIFY(object.thread() == currentThread); QVERIFY(child.thread() == currentThread); QTestEventLoop::instance().enterLoop(1); } // app dead, current thread still valid QVERIFY(QThread::currentThread() != 0); QCOMPARE(QThread::currentThread(), currentThread); // the thread should still be running and not finished QVERIFY(currentThread->isRunning()); QVERIFY(!currentThread->isFinished()); // should still have a thread QVERIFY(object.thread() == currentThread); QVERIFY(child.thread() == currentThread); } class DeleteLaterWidget : public QWidget { Q_OBJECT public: DeleteLaterWidget(QApplication *_app, QWidget *parent = 0) : QWidget(parent) { app = _app; child_deleted = false; } bool child_deleted; QApplication *app; public slots: void runTest(); void checkDeleteLater(); void childDeleted() { child_deleted = true; } }; void DeleteLaterWidget::runTest() { QObject *stillAlive = qFindChild(this, "deleteLater"); QWidget *w = new QWidget(this); connect(w, SIGNAL(destroyed()), this, SLOT(childDeleted())); w->deleteLater(); QVERIFY(!child_deleted); QDialog dlg; QTimer::singleShot(500, &dlg, SLOT(reject())); dlg.exec(); QVERIFY(!child_deleted); app->processEvents(); QVERIFY(!child_deleted); QTimer::singleShot(500, this, SLOT(checkDeleteLater())); app->processEvents(); QVERIFY(!stillAlive); // verify at the end to make test terminate } void DeleteLaterWidget::checkDeleteLater() { QVERIFY(child_deleted); close(); } void tst_QApplication::testDeleteLater() { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); DeleteLaterWidget *wgt = new DeleteLaterWidget(&app); QTimer::singleShot(500, wgt, SLOT(runTest())); QObject *object = new QObject(wgt); object->setObjectName("deleteLater"); object->deleteLater(); QObject *stillAlive = qFindChild(wgt, "deleteLater"); QVERIFY(stillAlive); app.exec(); delete wgt; } class EventLoopNester : public QObject { Q_OBJECT public slots: void deleteLaterAndEnterLoop() { QEventLoop eventLoop; QPointer p(this); deleteLater(); /* DeferredDelete events are compressed, meaning this second deleteLater() will *not* delete the object in the nested event loop */ QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); QTimer::singleShot(1000, &eventLoop, SLOT(quit())); eventLoop.exec(); QVERIFY(p); } void deleteLaterAndExitLoop() { // Check that 'p' is not deleted before exec returns, since the call // to QEventLoop::quit() should stop 'eventLoop' from processing // any more events (that is, delete later) until we return to the // _current_ event loop: QEventLoop eventLoop; QPointer p(this); QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); eventLoop.exec(); QVERIFY(p); // not dead yet } void processEventsOnly() { QApplication::processEvents(); } void processEventsWithDeferredDeletion() { QApplication::processEvents(QEventLoop::DeferredDeletion); } void sendPostedEventsWithDeferredDelete() { QApplication::sendPostedEvents(0, QEvent::DeferredDelete); } void deleteLaterAndProcessEvents1() { QEventLoop eventLoop; QPointer p = this; deleteLater(); // trying to delete this object in a deeper eventloop just won't work QMetaObject::invokeMethod(this, "processEventsOnly", Qt::QueuedConnection); QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); eventLoop.exec(); QVERIFY(p); QMetaObject::invokeMethod(this, "processEventsWithDeferredDeletion", Qt::QueuedConnection); QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); eventLoop.exec(); QVERIFY(p); QMetaObject::invokeMethod(this, "sendPostedEventsWithDeferredDelete", Qt::QueuedConnection); QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); eventLoop.exec(); QVERIFY(p); // trying to delete it from this eventloop still doesn't work QApplication::processEvents(); QVERIFY(p); // however, it *will* work with this magic incantation QApplication::processEvents(QEventLoop::DeferredDeletion); QVERIFY(!p); } void deleteLaterAndProcessEvents2() { QEventLoop eventLoop; QPointer p = this; deleteLater(); // trying to delete this object in a deeper eventloop just won't work QMetaObject::invokeMethod(this, "processEventsOnly", Qt::QueuedConnection); QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); eventLoop.exec(); QVERIFY(p); QMetaObject::invokeMethod(this, "processEventsWithDeferredDeletion", Qt::QueuedConnection); QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); eventLoop.exec(); QVERIFY(p); QMetaObject::invokeMethod(this, "sendPostedEventsWithDeferredDelete", Qt::QueuedConnection); QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); eventLoop.exec(); QVERIFY(p); // trying to delete it from this eventloop still doesn't work QApplication::processEvents(); QVERIFY(p); // however, it *will* work with this magic incantation QApplication::sendPostedEvents(0, QEvent::DeferredDelete); QVERIFY(!p); } }; void tst_QApplication::testDeleteLaterProcessEvents() { int argc = 0; // Calling processEvents() with no event dispatcher does nothing. QObject *object = new QObject; QPointer p(object); object->deleteLater(); QApplication::processEvents(); QVERIFY(p); delete object; { QApplication app(argc, 0, QApplication::GuiServer); // If you call processEvents() with an event dispatcher present, but // outside any event loops, deferred deletes are not processed unless // QEventLoop::DeferredDeletion is passed. object = new QObject; p = object; object->deleteLater(); app.processEvents(); QVERIFY(p); app.processEvents(QEventLoop::ProcessEventsFlag(0x10)); // 0x10 == QEventLoop::DeferredDeletion QVERIFY(!p); // sendPostedEvents(0, DeferredDelete); also works object = new QObject; p = object; object->deleteLater(); app.processEvents(); QVERIFY(p); QApplication::sendPostedEvents(0, QEvent::DeferredDelete); QVERIFY(!p); // If you call deleteLater() on an object when there is no parent // event loop, and then enter an event loop, the object will get // deleted. object = new QObject; p = object; object->deleteLater(); QEventLoop loop; QTimer::singleShot(1000, &loop, SLOT(quit())); loop.exec(); QVERIFY(!p); } { // When an object is in an event loop, then calls deleteLater() and enters // an event loop recursively, it should not die until the parent event // loop continues. QApplication app(argc, 0, QApplication::GuiServer); QEventLoop loop; EventLoopNester *nester = new EventLoopNester; p = nester; QTimer::singleShot(3000, &loop, SLOT(quit())); QTimer::singleShot(0, nester, SLOT(deleteLaterAndEnterLoop())); loop.exec(); QVERIFY(!p); } { // When the event loop that calls deleteLater() is exited // immediately, the object should die when returning to the // parent event loop QApplication app(argc, 0, QApplication::GuiServer); QEventLoop loop; EventLoopNester *nester = new EventLoopNester; p = nester; QTimer::singleShot(3000, &loop, SLOT(quit())); QTimer::singleShot(0, nester, SLOT(deleteLaterAndExitLoop())); loop.exec(); QVERIFY(!p); } { // when the event loop that calls deleteLater() also calls // processEvents() immediately afterwards, the object should // not die until the parent loop continues QApplication app(argc, 0, QApplication::GuiServer); QEventLoop loop; EventLoopNester *nester = new EventLoopNester(); p = nester; QTimer::singleShot(3000, &loop, SLOT(quit())); QTimer::singleShot(0, nester, SLOT(deleteLaterAndProcessEvents1())); loop.exec(); QVERIFY(!p); } { // when the event loop that calls deleteLater() also calls // processEvents() immediately afterwards, the object should // not die until the parent loop continues QApplication app(argc, 0, QApplication::GuiServer); QEventLoop loop; EventLoopNester *nester = new EventLoopNester(); p = nester; QTimer::singleShot(3000, &loop, SLOT(quit())); QTimer::singleShot(0, nester, SLOT(deleteLaterAndProcessEvents2())); loop.exec(); QVERIFY(!p); } } /* Test for crash whith QApplication::setDesktopSettingsAware(false). */ void tst_QApplication::desktopSettingsAware() { #ifndef QT_NO_PROCESS QProcess testProcess; #ifdef Q_OS_WINCE int argc = 0; QApplication tmpApp(argc, 0, QApplication::GuiServer); testProcess.start("desktopsettingsaware/desktopsettingsaware"); #else #if defined(Q_OS_WIN) && defined(QT_DEBUG) testProcess.start("desktopsettingsaware/debug/desktopsettingsaware"); #elif defined(Q_OS_WIN) testProcess.start("desktopsettingsaware/release/desktopsettingsaware"); #elif defined(Q_OS_SYMBIAN) testProcess.start("desktopsettingsaware"); #if defined(Q_CC_NOKIAX86) QEXPECT_FAIL("", "QProcess on Q_CC_NOKIAX86 cannot launch another Qt application, due to DLL conflicts.", Abort); // TODO: Remove XFAIL, as soon as we can launch Qt applications from within Qt applications on Symbian QVERIFY(testProcess.error() != QProcess::FailedToStart); #endif // defined(Q_CC_NOKIAX86) #else testProcess.start("desktopsettingsaware/desktopsettingsaware"); #endif #endif QVERIFY(testProcess.waitForFinished(10000)); QCOMPARE(int(testProcess.state()), int(QProcess::NotRunning)); QVERIFY(int(testProcess.error()) != int(QProcess::Crashed)); #endif } void tst_QApplication::setActiveWindow() { int argc = 0; QApplication MyApp(argc, 0, QApplication::GuiServer); QWidget* w = new QWidget; QVBoxLayout* layout = new QVBoxLayout(w); QLineEdit* pb1 = new QLineEdit("Testbutton1", w); QLineEdit* pb2 = new QLineEdit("Test Line Edit", w); layout->addWidget(pb1); layout->addWidget(pb2); pb2->setFocus(); pb2->setParent(0); delete pb2; w->show(); QApplication::setActiveWindow(w); // needs this on twm (focus follows mouse) QVERIFY(pb1->hasFocus()); delete w; } /* This might fail on some X11 window managers? */ void tst_QApplication::focusChanged() { int argc = 0; QApplication app(argc, 0, QApplication::GuiServer); QSignalSpy spy(&app, SIGNAL(focusChanged(QWidget *, QWidget *))); QWidget *now = 0; QWidget *old = 0; QWidget parent1; QHBoxLayout hbox1(&parent1); QLabel lb1(&parent1); QLineEdit le1(&parent1); QPushButton pb1(&parent1); hbox1.addWidget(&lb1); hbox1.addWidget(&le1); hbox1.addWidget(&pb1); QCOMPARE(spy.count(), 0); parent1.show(); QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) QCOMPARE(spy.count(), 1); QCOMPARE(spy.at(0).count(), 2); old = qVariantValue(spy.at(0).at(0)); now = qVariantValue(spy.at(0).at(1)); QVERIFY(now == &le1); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == 0); spy.clear(); QCOMPARE(spy.count(), 0); pb1.setFocus(); QCOMPARE(spy.count(), 1); old = qVariantValue(spy.at(0).at(0)); now = qVariantValue(spy.at(0).at(1)); QVERIFY(now == &pb1); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == &le1); spy.clear(); lb1.setFocus(); QCOMPARE(spy.count(), 1); old = qVariantValue(spy.at(0).at(0)); now = qVariantValue(spy.at(0).at(1)); QVERIFY(now == &lb1); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == &pb1); spy.clear(); lb1.clearFocus(); QCOMPARE(spy.count(), 1); old = qVariantValue(spy.at(0).at(0)); now = qVariantValue(spy.at(0).at(1)); QVERIFY(now == 0); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == &lb1); spy.clear(); QWidget parent2; QHBoxLayout hbox2(&parent2); QLabel lb2(&parent2); QLineEdit le2(&parent2); QPushButton pb2(&parent2); hbox2.addWidget(&lb2); hbox2.addWidget(&le2); hbox2.addWidget(&pb2); parent2.show(); QApplication::setActiveWindow(&parent2); // needs this on twm (focus follows mouse) QVERIFY(spy.count() > 0); // one for deactivation, one for activation on Windows old = qVariantValue(spy.at(spy.count()-1).at(0)); now = qVariantValue(spy.at(spy.count()-1).at(1)); QVERIFY(now == &le2); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == 0); spy.clear(); QTestKeyEvent tab(QTest::Press, Qt::Key_Tab, 0, 0); QTestKeyEvent backtab(QTest::Press, Qt::Key_Backtab, 0, 0); QTestMouseEvent click(QTest::MouseClick, Qt::LeftButton, 0, QPoint(5, 5), 0); bool tabAllControls = true; #ifdef Q_WS_MAC // Mac has two modes, one where you tab to everything, one where you can // only tab to input controls, here's what we get. Determine which ones we // should get. QSettings appleSettings(QLatin1String("apple.com")); QVariant appleValue = appleSettings.value(QLatin1String("AppleKeyboardUIMode"), 0); tabAllControls = (appleValue.toInt() & 0x2); #endif tab.simulate(now); if (!tabAllControls) { QVERIFY(spy.count() == 0); QVERIFY(now == QApplication::focusWidget()); } else { QVERIFY(spy.count() > 0); old = qVariantValue(spy.at(0).at(0)); now = qVariantValue(spy.at(0).at(1)); QVERIFY(now == &pb2); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == &le2); spy.clear(); } if (!tabAllControls) { QVERIFY(spy.count() == 0); QVERIFY(now == QApplication::focusWidget()); } else { tab.simulate(now); QVERIFY(spy.count() > 0); old = qVariantValue(spy.at(0).at(0)); now = qVariantValue(spy.at(0).at(1)); QVERIFY(now == &le2); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == &pb2); spy.clear(); } if (!tabAllControls) { QVERIFY(spy.count() == 0); QVERIFY(now == QApplication::focusWidget()); } else { backtab.simulate(now); QVERIFY(spy.count() > 0); old = qVariantValue(spy.at(0).at(0)); now = qVariantValue(spy.at(0).at(1)); QVERIFY(now == &pb2); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == &le2); spy.clear(); } if (!tabAllControls) { QVERIFY(spy.count() == 0); QVERIFY(now == QApplication::focusWidget()); old = &pb2; } else { backtab.simulate(now); QVERIFY(spy.count() > 0); old = qVariantValue(spy.at(0).at(0)); now = qVariantValue(spy.at(0).at(1)); QVERIFY(now == &le2); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == &pb2); spy.clear(); } click.simulate(old); if (!(pb2.focusPolicy() & Qt::ClickFocus)) { QVERIFY(spy.count() == 0); QVERIFY(now == QApplication::focusWidget()); } else { QVERIFY(spy.count() > 0); old = qVariantValue(spy.at(0).at(0)); now = qVariantValue(spy.at(0).at(1)); QVERIFY(now == &pb2); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == &le2); spy.clear(); click.simulate(old); QVERIFY(spy.count() > 0); old = qVariantValue(spy.at(0).at(0)); now = qVariantValue(spy.at(0).at(1)); QVERIFY(now == &le2); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == &pb2); spy.clear(); } parent1.activateWindow(); QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) QVERIFY(spy.count() == 1 || spy.count() == 2); // one for deactivation, one for activation on Windows //on windows, the change of focus is made in 2 steps //(the focusChanged SIGNAL is emitted twice) if (spy.count()==1) old = qVariantValue(spy.at(spy.count()-1).at(0)); else old = qVariantValue(spy.at(spy.count()-2).at(0)); now = qVariantValue(spy.at(spy.count()-1).at(1)); QVERIFY(now == &le1); QVERIFY(now == QApplication::focusWidget()); QVERIFY(old == &le2); spy.clear(); } class LineEdit : public QLineEdit { public: LineEdit(QWidget *parent = 0) : QLineEdit(parent) { } protected: void focusOutEvent(QFocusEvent *e) { QLineEdit::focusOutEvent(e); if (objectName() == "le1") setStyleSheet(""); } void focusInEvent(QFocusEvent *e) { QLineEdit::focusInEvent(e); if (objectName() == "le2") setStyleSheet(""); } }; void tst_QApplication::focusOut() { int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); // Tests the case where the style pointer changes when on focus in/out // (the above is the case when the stylesheet changes) QWidget w; QLineEdit *le1 = new LineEdit(&w); le1->setObjectName("le1"); le1->setStyleSheet("background: #fee"); le1->setFocus(); QLineEdit *le2 = new LineEdit(&w); le2->setObjectName("le2"); le2->setStyleSheet("background: #fee"); le2->move(100, 100); w.show(); QTest::qWait(2000); le2->setFocus(); QTest::qWait(2000); } void tst_QApplication::execAfterExit() { int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); // this should be ignored, as exec() will reset the exitCode QApplication::exit(1); int exitCode = app.exec(); QCOMPARE(exitCode, 0); // the quitNow flag should have been reset, so we can spin an // eventloop after QApplication::exec() returns QEventLoop eventLoop; QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); exitCode = eventLoop.exec(); QCOMPARE(exitCode, 0); } void tst_QApplication::wheelScrollLines() { int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); // If wheelScrollLines returns 0, the mose wheel will be disabled. QVERIFY(app.wheelScrollLines() > 0); } void tst_QApplication::style() { int argc = 1; { QApplication app(argc, &argv0, QApplication::GuiServer); QPointer style = app.style(); app.setStyle(new QWindowsStyle); QVERIFY(style.isNull()); } QApplication app(argc, &argv0, QApplication::GuiServer); // qApp style can never be 0 QVERIFY(QApplication::style() != 0); } void tst_QApplication::allWidgets() { int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); QWidget *w = new QWidget; QVERIFY(app.allWidgets().contains(w)); // uncreate widget test QVERIFY(app.allWidgets().contains(w)); // created widget test delete w; w = 0; QVERIFY(!app.allWidgets().contains(w)); // removal test } void tst_QApplication::topLevelWidgets() { int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); QWidget *w = new QWidget; w->show(); #ifndef QT_NO_CLIPBOARD QClipboard *clipboard = QApplication::clipboard(); QString originalText = clipboard->text(); clipboard->setText(QString("newText")); #endif app.processEvents(); QVERIFY(QApplication::topLevelWidgets().contains(w)); QCOMPARE(QApplication::topLevelWidgets().count(), 1); delete w; w = 0; app.processEvents(); QCOMPARE(QApplication::topLevelWidgets().count(), 0); } void tst_QApplication::setAttribute() { int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); QVERIFY(!QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation)); QWidget *w = new QWidget; QVERIFY(!w->testAttribute(Qt::WA_WState_Created)); delete w; QApplication::setAttribute(Qt::AA_ImmediateWidgetCreation); QVERIFY(QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation)); w = new QWidget; QVERIFY(w->testAttribute(Qt::WA_WState_Created)); QWidget *w2 = new QWidget(w); w2->setParent(0); QVERIFY(w2->testAttribute(Qt::WA_WState_Created)); delete w; delete w2; QApplication::setAttribute(Qt::AA_ImmediateWidgetCreation, false); QVERIFY(!QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation)); w = new QWidget; QVERIFY(!w->testAttribute(Qt::WA_WState_Created)); delete w; } void tst_QApplication::windowsCommandLine_data() { #if defined(Q_OS_WIN) QTest::addColumn("args"); QTest::addColumn("expected"); QTest::newRow("hello world") << QString("Hello \"World\"") << QString("Hello \"World\""); QTest::newRow("sql") << QString("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PNR' AND TABLE_TYPE = 'VIEW' ORDER BY TABLE_NAME") << QString("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PNR' AND TABLE_TYPE = 'VIEW' ORDER BY TABLE_NAME"); #endif } void tst_QApplication::windowsCommandLine() { #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) QFETCH(QString, args); QFETCH(QString, expected); QProcess testProcess; #if defined(QT_DEBUG) testProcess.start("wincmdline/debug/wincmdline", QStringList(args)); #else testProcess.start("wincmdline/release/wincmdline", QStringList(args)); #endif QVERIFY(testProcess.waitForFinished(10000)); QByteArray error = testProcess.readAllStandardError(); QString procError(error); QCOMPARE(procError, expected); #endif } class TouchEventPropagationTestWidget : public QWidget { Q_OBJECT public: bool seenTouchEvent, acceptTouchEvent, seenMouseEvent, acceptMouseEvent; TouchEventPropagationTestWidget(QWidget *parent = 0) : QWidget(parent), seenTouchEvent(false), acceptTouchEvent(false), seenMouseEvent(false), acceptMouseEvent(false) { } void reset() { seenTouchEvent = acceptTouchEvent = seenMouseEvent = acceptMouseEvent = false; } bool event(QEvent *event) { switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseMove: case QEvent::MouseButtonRelease: // qDebug() << objectName() << "seenMouseEvent = true"; seenMouseEvent = true; event->setAccepted(acceptMouseEvent); break; case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: // qDebug() << objectName() << "seenTouchEvent = true"; seenTouchEvent = true; event->setAccepted(acceptTouchEvent); break; default: return QWidget::event(event); } return true; } }; void tst_QApplication::touchEventPropagation() { int argc = 1; QApplication app(argc, &argv0, QApplication::GuiServer); QList pressedTouchPoints; QTouchEvent::TouchPoint press(0); press.setState(Qt::TouchPointPressed); pressedTouchPoints << press; QList releasedTouchPoints; QTouchEvent::TouchPoint release(0); release.setState(Qt::TouchPointReleased); releasedTouchPoints << release; { // touch event behavior on a window TouchEventPropagationTestWidget window; window.setObjectName("1. window"); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); QVERIFY(!window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); window.reset(); window.setAttribute(Qt::WA_AcceptTouchEvents); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); QVERIFY(window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); window.reset(); window.acceptTouchEvent = true; qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); QVERIFY(window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); } { // touch event behavior on a window with a child widget TouchEventPropagationTestWidget window; window.setObjectName("2. window"); TouchEventPropagationTestWidget widget(&window); widget.setObjectName("2. widget"); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); QVERIFY(!widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(!window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); window.reset(); widget.reset(); widget.setAttribute(Qt::WA_AcceptTouchEvents); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); QVERIFY(widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(!window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); window.reset(); widget.reset(); widget.acceptMouseEvent = true; qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); QVERIFY(widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(!window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); window.reset(); widget.reset(); widget.acceptTouchEvent = true; qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); QVERIFY(widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(!window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); window.reset(); widget.reset(); widget.setAttribute(Qt::WA_AcceptTouchEvents, false); window.setAttribute(Qt::WA_AcceptTouchEvents); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); QVERIFY(!widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); window.reset(); widget.reset(); window.acceptTouchEvent = true; qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); QVERIFY(!widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); window.reset(); widget.reset(); widget.acceptMouseEvent = true; // doesn't matter, touch events are propagated first window.acceptTouchEvent = true; qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); QVERIFY(!widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); } } void tst_QApplication::symbianNoApplicationPanes() { #ifndef Q_OS_SYMBIAN QSKIP("This is a Symbian only test", SkipAll); #else QApplication::setAttribute(Qt::AA_S60DontConstructApplicationPanes); // Run in a block so that QApplication is destroyed before resetting the attribute. { // Actually I wasn't able to get the forced orientation change to work properly, // but I'll leave the code here for the future in case we manage to test that // later. If someone knows how to force an orientation switch in an autotest, do // feel free to fix this testcase. int argc = 0; QApplication app(argc, 0); QWidget *w; w = new QWidget; w->show(); QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) ->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape)); app.processEvents(); delete w; w = new QWidget; w->show(); QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) ->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait)); app.processEvents(); delete w; w = new QWidget; w->showMaximized(); QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) ->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape)); app.processEvents(); delete w; w = new QWidget; w->showMaximized(); QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) ->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait)); app.processEvents(); delete w; w = new QWidget; w->showFullScreen(); QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) ->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape)); app.processEvents(); delete w; w = new QWidget; w->showFullScreen(); QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) ->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait)); app.processEvents(); delete w; // These will have no effect, since there is no status pane, but they shouldn't // crash either. w = new QWidget; w->show(); w->setWindowTitle("Testing title"); app.processEvents(); delete w; w = new QWidget; w->show(); w->setWindowIcon(QIcon(QPixmap("heart.svg"))); app.processEvents(); delete w; QDesktopWidget desktop; QCOMPARE(desktop.availableGeometry(), desktop.screenGeometry()); } QApplication::setAttribute(Qt::AA_S60DontConstructApplicationPanes, false); // No other error condition. Program will crash if unsuccessful. #endif } #ifdef Q_OS_SYMBIAN class CBaseDummy : public CBase { public: CBaseDummy(int *numDestroyed) : numDestroyed(numDestroyed) { } ~CBaseDummy() { (*numDestroyed)++; } private: int *numDestroyed; }; static void fakeMain(int *numDestroyed) { // Push a few objects, just so that the cleanup stack has something to clean up. CleanupStack::PushL(new (ELeave) CBaseDummy(numDestroyed)); int argc = 0; QApplication app(argc, 0); CleanupStack::PushL(new (ELeave) CBaseDummy(numDestroyed)); User::Leave(KErrGeneral); // Fake error } #endif void tst_QApplication::symbianNeedForTraps() { #ifndef Q_OS_SYMBIAN QSKIP("This is a Symbian-only test", SkipAll); #else int argc = 0; QApplication app(argc, 0); int numDestroyed = 0; // This next part should not require a trap. If it does, the test will crash. CleanupStack::PushL(new (ELeave) CBaseDummy(&numDestroyed)); CleanupStack::PopAndDestroy(); QCOMPARE(numDestroyed, 1); // No other failure condition. The program will crash if it does not pass. #endif } void tst_QApplication::symbianLeaveThroughMain() { #ifndef Q_OS_SYMBIAN QSKIP("This is a Symbian-only test", SkipAll); #else int numDestroyed = 0; TInt err; TRAP(err, fakeMain(&numDestroyed)); QCOMPARE(numDestroyed, 2); #endif } //QTEST_APPLESS_MAIN(tst_QApplication) int main(int argc, char *argv[]) { tst_QApplication tc; argv0 = argv[0]; return QTest::qExec(&tc, argc, argv); } #include "tst_qapplication.moc"