qt5base-lts/tests/auto/qapplication/tst_qapplication.cpp

2309 lines
69 KiB
C++
Raw Normal View History

/****************************************************************************
**
** Copyright (C) 2011 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$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
//#define QT_TST_QAPP_DEBUG
#include <qdebug.h>
#include <QtTest/QtTest>
#include "qabstracteventdispatcher.h"
#include <QtGui>
#include "private/qapplication_p.h"
#include "private/qstylesheetstyle_p.h"
#ifdef Q_OS_WINCE
#include <windows.h>
#endif
#ifdef Q_OS_SYMBIAN
#include <aknenv.h>
#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<QFileInfo> 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();
void qtbug_12673();
void globalStaticObjectDestruction(); // run this last
};
class EventSpy : public QObject
{
Q_OBJECT
public:
QList<int> 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);
MyInputContext *var1 = new MyInputContext;
// QApplication takes ownership, so check for reparenting:
obj1.setInputContext(var1);
QCOMPARE(var1->parent(), static_cast<QObject *>(&obj1));
// Test for self-assignment:
obj1.setInputContext(obj1.inputContext());
QVERIFY(obj1.inputContext());
QCOMPARE(static_cast<QInputContext *>(var1), obj1.inputContext());
// Resetting the input context to 0 is not allowed:
QTest::ignoreMessage(QtWarningMsg, "QApplication::setInputContext: called with 0 input context");
obj1.setInputContext(0);
QCOMPARE(static_cast<QInputContext *>(var1), obj1.inputContext());
}
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<QStringList>("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<QString>("family");
QTest::addColumn<int>("pointsize");
QTest::addColumn<bool>("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<int> 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<int>("argc_in");
QTest::addColumn<QString>("args_in");
QTest::addColumn<int>("argc_out");
QTest::addColumn<QString>("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<QByteArray> 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<QDialog> dialog = new QDialog;
QVERIFY(dialog->testAttribute(Qt::WA_QuitOnClose));
QTimer::singleShot(1000, dialog, SLOT(accept()));
dialog->exec();
QVERIFY(dialog);
QCOMPARE(spy.count(), 0);
QPointer<CloseWidget>widget = 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<QMainWindow> mainWindow = new QMainWindow;
QPointer<QDialog> 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<CloseEventTestWindow> 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<QString>::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<QString>::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<QString>::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<QString>::fromList(expected).toList();
# endif
#endif
QVERIFY(isPathListIncluded(app.libraryPaths(), expected));
qputenv("QT_PLUGIN_PATH", QByteArray());
}
}
class SendPostedEventsTester : public QObject
{
Q_OBJECT
public:
QList<int> 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<SendPostedEventsTester> 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<SendPostedEventsTester> 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<QObject*>(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<QObject*>(wgt, "deleteLater");
QVERIFY(stillAlive);
app.exec();
delete wgt;
}
class EventLoopNester : public QObject
{
Q_OBJECT
public slots:
void deleteLaterAndEnterLoop()
{
QEventLoop eventLoop;
QPointer<QObject> 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<QObject> 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<QObject> 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<QObject> 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<QObject> 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 with 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<QWidget*>(spy.at(0).at(0));
now = qVariantValue<QWidget*>(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<QWidget*>(spy.at(0).at(0));
now = qVariantValue<QWidget*>(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<QWidget*>(spy.at(0).at(0));
now = qVariantValue<QWidget*>(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<QWidget*>(spy.at(0).at(0));
now = qVariantValue<QWidget*>(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<QWidget*>(spy.at(spy.count()-1).at(0));
now = qVariantValue<QWidget*>(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<QWidget*>(spy.at(0).at(0));
now = qVariantValue<QWidget*>(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<QWidget*>(spy.at(0).at(0));
now = qVariantValue<QWidget*>(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<QWidget*>(spy.at(0).at(0));
now = qVariantValue<QWidget*>(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<QWidget*>(spy.at(0).at(0));
now = qVariantValue<QWidget*>(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<QWidget*>(spy.at(0).at(0));
now = qVariantValue<QWidget*>(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<QWidget*>(spy.at(0).at(0));
now = qVariantValue<QWidget*>(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<QWidget*>(spy.at(spy.count()-1).at(0));
else
old = qVariantValue<QWidget*>(spy.at(spy.count()-2).at(0));
now = qVariantValue<QWidget*>(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<QStyle> 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<QString>("args");
QTest::addColumn<QString>("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<QTouchEvent::TouchPoint> pressedTouchPoints;
QTouchEvent::TouchPoint press(0);
press.setState(Qt::TouchPointPressed);
pressedTouchPoints << press;
QList<QTouchEvent::TouchPoint> 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<CAknAppUi *>(CCoeEnv::Static()->AppUi())
->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape));
app.processEvents();
delete w;
w = new QWidget;
w->show();
QT_TRAP_THROWING(static_cast<CAknAppUi *>(CCoeEnv::Static()->AppUi())
->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait));
app.processEvents();
delete w;
w = new QWidget;
w->showMaximized();
QT_TRAP_THROWING(static_cast<CAknAppUi *>(CCoeEnv::Static()->AppUi())
->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape));
app.processEvents();
delete w;
w = new QWidget;
w->showMaximized();
QT_TRAP_THROWING(static_cast<CAknAppUi *>(CCoeEnv::Static()->AppUi())
->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait));
app.processEvents();
delete w;
w = new QWidget;
w->showFullScreen();
QT_TRAP_THROWING(static_cast<CAknAppUi *>(CCoeEnv::Static()->AppUi())
->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape));
app.processEvents();
delete w;
w = new QWidget;
w->showFullScreen();
QT_TRAP_THROWING(static_cast<CAknAppUi *>(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
}
void tst_QApplication::qtbug_12673()
{
#ifdef Q_OS_SYMBIAN
QSKIP("This might not make sense in Symbian, but since I do not know how to test it I'll just skip it for now.", SkipAll);
#else
QProcess testProcess;
QStringList arguments;
#ifdef Q_OS_MAC
testProcess.start("modal/modal.app", arguments);
#else
testProcess.start("modal/modal", arguments);
#endif
QVERIFY(testProcess.waitForFinished(20000));
QCOMPARE(testProcess.exitStatus(), QProcess::NormalExit);
#endif // Q_OS_SYMBIAN
}
/*
This test is meant to ensure that certain objects (public & commonly used)
can safely be used in a Q_GLOBAL_STATIC such that their destructors are
executed *after* the destruction of QApplication.
*/
Q_GLOBAL_STATIC(QLocale, tst_qapp_locale);
Q_GLOBAL_STATIC(QProcess, tst_qapp_process);
Q_GLOBAL_STATIC(QFileSystemWatcher, tst_qapp_fileSystemWatcher);
Q_GLOBAL_STATIC(QSharedMemory, tst_qapp_sharedMemory);
Q_GLOBAL_STATIC(QElapsedTimer, tst_qapp_elapsedTimer);
Q_GLOBAL_STATIC(QMutex, tst_qapp_mutex);
Q_GLOBAL_STATIC(QWidget, tst_qapp_widget);
Q_GLOBAL_STATIC(QPixmap, tst_qapp_pixmap);
Q_GLOBAL_STATIC(QFont, tst_qapp_font);
Q_GLOBAL_STATIC(QRegion, tst_qapp_region);
Q_GLOBAL_STATIC(QFontDatabase, tst_qapp_fontDatabase);
Q_GLOBAL_STATIC(QCursor, tst_qapp_cursor);
void tst_QApplication::globalStaticObjectDestruction()
{
int argc = 1;
QApplication app(argc, &argv0, QApplication::GuiServer);
QVERIFY(tst_qapp_locale());
QVERIFY(tst_qapp_process());
QVERIFY(tst_qapp_fileSystemWatcher());
QVERIFY(tst_qapp_sharedMemory());
QVERIFY(tst_qapp_elapsedTimer());
QVERIFY(tst_qapp_mutex());
QVERIFY(tst_qapp_widget());
QVERIFY(tst_qapp_pixmap());
QVERIFY(tst_qapp_font());
QVERIFY(tst_qapp_region());
QVERIFY(tst_qapp_fontDatabase());
QVERIFY(tst_qapp_cursor());
}
//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"