summaryrefslogtreecommitdiffstats
path: root/old/libqsystemtest/qsystemtest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'old/libqsystemtest/qsystemtest.cpp')
-rw-r--r--old/libqsystemtest/qsystemtest.cpp3578
1 files changed, 3578 insertions, 0 deletions
diff --git a/old/libqsystemtest/qsystemtest.cpp b/old/libqsystemtest/qsystemtest.cpp
new file mode 100644
index 0000000..f41157d
--- /dev/null
+++ b/old/libqsystemtest/qsystemtest.cpp
@@ -0,0 +1,3578 @@
+/****************************************************************************
+**
+** 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 QtUiTest.
+**
+** $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$
+**
+****************************************************************************/
+
+#include <qsystemtest.h>
+#include "qsystemtestmaster_p.h"
+#include "qtuitest_config.h"
+#include "qtestide.h"
+#include "ui_recorddlg.h"
+
+#ifdef QTCREATOR_QTEST
+# include "testoutputwindow.h"
+#endif
+
+#include <QDir>
+#include <QProcess>
+#include <QMessageBox>
+#include <QTemporaryFile>
+#include <QTextEdit>
+#include <QSignalSpy>
+#include <QSettings>
+
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+
+#undef qLog
+#define qLog(A) if (0); else (QDebug(QtDebugMsg) << #A ":")
+#define OBJECT_EXIST_TIMEOUT 1000
+
+#define BT(message) (\
+ message["location"] = QString("%1:%2%3").arg(__FILE__).arg(__LINE__).arg(!message["location"].toString().isEmpty() ? QString("\n") + message["location"].toString() : QString()),\
+ message)
+
+#undef QFAIL
+#define QFAIL(message) \
+do {\
+ setQueryError(message);\
+ return;\
+} while(0)
+
+#define CHECK_FORCED_MANUAL(msg) \
+if (m_run_as_manual_test) {\
+ m_manual_commands += msg.event() + msg.msgId() + msg.toString() + "\n";\
+ QTestMessage reply;\
+ reply["status"] = "OK";\
+ return reply;\
+}
+
+QString quote(const QString &item)
+{
+ if (item == "MAGIC_DATA" || item.contains("'"))
+ return item;
+ else
+ return "'" + item + "'";
+}
+
+QString mouseButtons( QFlags<Qt::MouseButton> buttons )
+{
+ QString ret;
+ if (buttons) {
+ ret = "";
+ if ((buttons & Qt::LeftButton) > 0)
+ ret += "'Left'+ ";
+ if ((buttons & Qt::MidButton) > 0)
+ ret += "'Mid'+ ";
+ if ((buttons & Qt::RightButton) > 0)
+ ret += "'Right'+ ";
+ if ((buttons & Qt::XButton1) > 0)
+ ret += "'X1'+ ";
+ if ((buttons & Qt::XButton2) > 0)
+ ret += "'X2'+ ";
+ ret = ret.left(ret.length()-2);
+ }
+ return ret;
+}
+
+/*!
+ \enum QSystemTest::EnterMode
+
+ This enum specifies whether enter() should commit the entered value (eg, by simulating a Select
+ keypress) or not.
+
+ \value Commit Commit the value (default).
+ \value NoCommit Do not commit the value.
+*/
+
+/*!
+ \enum QSystemTest::StartApplicationFlag
+
+ This enum describes additional behaviour to use when starting applications
+ by startApplication().
+
+ \value NoFlag Don't apply any of the below flags.
+ \value WaitForFocus Wait for the application to gain keyboard focus before returning.
+ \value BackgroundCurrentApplication Use multitasking to background the current application.
+ The default behaviour is to exit the current application.
+*/
+
+/*!
+ \enum QSystemTest::SkipMode
+
+ This enum describes the modes for skipping tests during execution of the test data.
+ \value SkipSingle Skip the rest of this test function for the current test data entry, but continue execution of the remaining test data.
+ \value SkipAll Skip the rest of this test function, and do not process any further test data for this test function.
+
+ \sa skip(), QTest::SkipMode
+*/
+
+/*!
+ \enum QSystemTest::LabelOrientation
+
+ This enum is used to indicate the expected position of a label relative to the widget it is
+ associated with. Use setLabelOrientation() to change the expected layout.
+
+ \value LabelLeft Label is located to left of widget.
+ \value LabelRight Label is located to right of widget.
+ \value LabelAbove Label is located above widget.
+ \value LabelBelow Label is located below widget.
+
+ \sa labelOrientation(), setLabelOrientation()
+*/
+
+/*!
+ \preliminary
+ \namespace QSystemTest
+ \brief The QSystemTest namespace provides script based system test functionality for Qt.
+
+ \ingroup qtuitest_systemtest
+ \inpublicgroup QtUiTestModule
+
+ This documentation describes the API reference for the QtUiTest scripting language. Please read the \l{QtUiTest Manual} for a full description of the system test tool.
+
+*/
+
+/*! \internal */
+QMap<QString, int> QSystemTest::filteredMessages() const
+{
+ return m_filteredMessages;
+}
+
+/*! \internal */
+void QSystemTest::clearFilteredMessages()
+{
+ m_filteredMessages.clear();
+}
+
+/*! \internal */
+bool QSystemTest::shouldFilterMessage(char const *msg)
+{
+ static QList<QRegExp> filters;
+ if (filters.isEmpty()) {
+ QStringList defaultFilters;
+// defaultFilters
+// << "^Connected to VFB server"
+// << "^QTimeLine::start: already running$" // Bug
+// ;
+ QStringList stringFilters = QSettings("Trolltech", "QtUitest")
+ .value("Logging/Filters", defaultFilters)
+ .toStringList();
+ foreach (QString s, stringFilters) {
+ filters << QRegExp(s);
+ }
+ }
+
+ QString message = QString::fromLocal8Bit(msg);
+ foreach (QRegExp r, filters) {
+ if (-1 != r.indexIn(message)) {
+ ++m_filteredMessages[message];
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Creates the test class.
+ Generally you would use the \l {QTest}{QTEST_MAIN()} macro rather than create the class directly.
+*/
+QSystemTest::QSystemTest()
+ : QAbstractTest()
+ , event_timer(0)
+ , m_test_app(0)
+ , m_recorded_events_edit(0)
+ , recorded_events_as_code(true)
+ , record_prompt(false)
+ , query_warning_mode(true)
+ , fatal_timeouts(1)
+ , timeouts(0)
+ , m_keyclickhold_key((Qt::Key)0)
+ , current_application("")
+ , current_app_version("")
+ , m_loc_fname("")
+ , m_loc_line(-1)
+ , m_auto_mode(false)
+ , m_run_as_manual_test(false)
+ , m_aut_port(DEFAULT_AUT_PORT)
+ , m_keep_aut(false)
+ , m_silent_aut(false)
+ , m_no_aut(false)
+ , m_demo_mode(false)
+ , m_verbose(false)
+ , m_strict_mode(false)
+ , m_visible_response_time(4000)
+ , recorded_events()
+ , m_recorded_code()
+ , display_id(0)
+ , device()
+ , m_mousePreferred(false)
+ , screenGeometry()
+ , theme()
+ , m_config_id()
+ , m_recording_events(false)
+ , m_expect_app_close(false)
+{
+ m_env.clear();
+ ssh_param.host = DEFAULT_AUT_HOST;
+ ssh_param.port = DEFAULT_AUTSSH_PORT;
+ device_controller = 0;
+
+ QTestIDE::instance()->setSystemTest(this);
+ (void)qMetaTypeId<RecordEvent>();
+ (void)qMetaTypeId< QList<RecordEvent> >();
+ qRegisterMetaType<QTestMessage>("QTestMessage");
+ qRegisterMetaType<QTestMessage*>("QTestMessage*");
+}
+
+/*!
+ \internal
+ Destroys the test class.
+*/
+QSystemTest::~QSystemTest()
+{
+ if (device_controller)
+ device_controller->killApplications();
+
+ delete device_controller;
+ delete event_timer;
+ delete m_test_app;
+ while (expected_msg_boxes.count() > 0)
+ delete expected_msg_boxes.takeFirst();
+}
+
+void QSystemTest::setConnectionParameters( Qt4Test::TestController::TestDeviceType &type,
+ Core::SshConnectionParameters param )
+{
+ ssh_param = param;
+ device_controller = new Qt4Test::TestController( type, &param);
+}
+
+/*!
+ Returns the signature of the currently active window.
+
+ \sa {Query Paths}
+*/
+QString QSystemTest::activeWindow()
+{
+ if (runAsManualTest()) {
+ manualTestData( "the currently active window" );
+ return "MAGIC_DATA";
+ }
+
+ QString ret;
+ return queryWithReturn(ret, "activeWindow", "");
+}
+
+/*!
+ Returns the signature of the widget that has keyboard focus.
+
+ Example:
+ \code
+ // Get the currently focused widget in the current app
+ print( focusWidget() );
+ \endcode
+
+ \sa {Query Paths}
+*/
+QString QSystemTest::focusWidget()
+{
+ if (runAsManualTest()) {
+ manualTestData( "the currently focused widget" );
+ return "MAGIC_DATA";
+ }
+
+ QString ret;
+ return queryWithReturn(ret, "focusWidget", "");
+}
+
+
+/*!
+ Returns the currently selected text from the widget specified by \a {queryPath}.
+ If no text is selected, returns all text.
+ For list-type widgets, returns the text for the currently selected item.
+
+ Example:
+ \code
+ // Enter text in two fields, then go back to the first
+ // and make sure it still has the right text
+ enter("Australia", "Home");
+ enter("dog walker", "Occupation");
+ compare( getSelectedText("Home"), "Australia" );
+ \endcode
+
+ \sa {Query Paths}, {Querying Objects}
+*/
+QString QSystemTest::getSelectedText( const QString &queryPath )
+{
+ if (runAsManualTest()) {
+ manualTestData( "selected text for " + quote(queryPath) );
+ return "MAGIC_DATA";
+ }
+
+ QString ret = "";
+ return queryWithReturn(ret, "getSelectedText", queryPath);
+}
+
+/*!
+ Returns all text from the widget specified by \a {queryPath}, or the current
+ focus widget if \a {queryPath} is not specified.
+ For list-type widgets, returns the text for all items separated by newlines.
+
+ Example:
+ \code
+ // Get current content of "address" field
+ print( getText("Address") );
+ \endcode
+
+ \sa {Query Paths}, {Querying Objects}
+*/
+QString QSystemTest::getText( const QString &queryPath )
+{
+ if (runAsManualTest()) {
+ manualTestData( "text for " + quote(queryPath) );
+ return "MAGIC_DATA";
+ }
+
+ QString ret;
+ return queryWithReturn(ret, "getText", queryPath);
+}
+
+/*!
+ Returns the currently selected value from the widget specified by \a {queryPath}.
+ If no value is selected, returns all data in a single value.
+ For list-type widgets, returns the currently selected value.
+
+ \sa getSelectedText(), {Query Paths}, {Querying Objects}
+*/
+QVariant QSystemTest::getSelectedValue( const QString &queryPath )
+{
+ QVariant ret;
+ if (runAsManualTest()) {
+ manualTestData( "selected value for " + quote(queryPath) );
+ ret = "MAGIC_DATA";
+ return ret;
+ }
+
+ QString msg("getSelectedValue");
+ QTestMessage reply;
+ QTestMessage testMessage(msg);
+ if (!doQuery(testMessage, queryPath, &reply)) return ret;
+ if (!reply[msg].isValid()) {
+ reply["status"] = QString("ERROR: no data in reply to %1; status: %2").arg(msg).arg(reply["status"].toString());
+ setQueryError(reply);
+ return ret;
+ }
+ return reply[msg];
+}
+
+/*!
+ Returns the value from the widget specified by \a {queryPath}.
+ For list-type widgets, returns a list containing all values.
+
+ \sa getText(), {Query Paths}, {Querying Objects}
+*/
+QVariant QSystemTest::getValue( const QString &queryPath )
+{
+ QVariant ret;
+ if (runAsManualTest()) {
+ manualTestData( "value for " + quote(queryPath) );
+ ret = "MAGIC_DATA";
+ return ret;
+ }
+
+ QString msg("getValue");
+ QTestMessage reply;
+ QTestMessage testMessage(msg);
+ if (!doQuery(testMessage, queryPath, &reply)) return ret;
+ if (!reply[msg].isValid()) {
+ reply["status"] = QString("ERROR: no data in reply to %1; status: %2").arg(msg).arg(reply["status"].toString());
+ setQueryError(reply);
+ return ret;
+ }
+ return reply[msg];
+}
+
+/*!
+ Returns true if the primary input method is mouse/touchscreen.
+
+ \sa setMousePreferred()
+*/
+bool QSystemTest::mousePreferred()
+{
+ if (runAsManualTest()) {
+ qDebug() << "QSystemTest::mousePreferred() set to 'true'";
+ return true; //FIXME TODO
+ }
+
+ bool ret;
+ return queryWithReturn(ret, "mousePreferred", "");
+}
+
+/*!
+ If \a useMouse is true, indicates that QtUiTest should use mouse/touchscreen
+ events to select items on the screen. If false, the keyboard will be used.
+
+ The setting will remain active until the application under test closes, or
+ until the value is changed by another call to setMousePreferred().
+
+ \sa mousePreferred()
+*/
+void QSystemTest::setMousePreferred(bool useMouse)
+{
+ QTestMessage message("setMousePreferred");
+ message["useMouse"] = useMouse;
+ queryPassed( "OK", "", BT(message));
+}
+
+/*!
+ Returns the label orientation, used by QtUiTest to assign labels to
+ widgets.
+
+ \sa setLabelOrientation()
+*/
+QSystemTest::LabelOrientation QSystemTest::labelOrientation()
+{
+ if (runAsManualTest()) {
+ qDebug() << "QSystemTest::labelOrientation() set to 'LabelLeft'";
+ return LabelLeft; //FIXME TODO
+ }
+
+ int ret;
+ return static_cast<LabelOrientation>(queryWithReturn(ret, "labelOrientation", ""));
+}
+
+/*!
+ Set the expected label orientation to \a orientation. This value is used by
+ QtUiTest to work out which labels refer to which widgets. By default, this
+ value is LabelLeft (on left-to-right locales) or LabelRight (on right-to-left
+ locales).
+
+ The setting will remain active until the application under test closes, or
+ until the value is changed by another call to setLabelOrientation().
+
+ \sa labelOrientation()
+*/
+void QSystemTest::setLabelOrientation(LabelOrientation orientation)
+{
+ QTestMessage message("setLabelOrientation");
+ message["orientation"] = orientation;
+ queryPassed( "OK", "", BT(message));
+}
+
+/*!
+ Returns a list of all items from the list-type widget specified by \a {queryPath}.
+
+ The items will be returned in the order they are stored in the widget (for example,
+ for a simple list view the items will be returned from top to bottom).
+
+ Example:
+ \code
+ // Verify that "gender" combobox contains male and female only
+ var g = getList("Gender");
+ compare( g.length == 2 );
+ verify( g.contains("Male") );
+ verify( g.contains("Female") );
+ \endcode
+
+ \sa {Query Paths}, {Querying Objects}
+*/
+QStringList QSystemTest::getList( const QString &queryPath )
+{
+ if (runAsManualTest()) {
+ manualTestData( "the contents of list " + quote(queryPath) );
+ return QStringList("MAGIC_DATA");
+ }
+
+ QStringList ret;
+ return queryWithReturn(ret, "getList", queryPath);
+}
+
+QPoint QSystemTest::getCenter( const QString &item, const QString &queryPath )
+{
+ if (item.isNull()) return QPoint();
+
+ if (runAsManualTest()) {
+ manualTestData( "the center of " + quote(item) );
+ return QPoint();
+ }
+
+ QTestMessage message("getCenter");
+ message["item"] = item;
+ queryPassed("OK", "", BT(message), queryPath);
+ QTestMessage reply;
+ if (!queryPassed( "OK", "", BT(message), queryPath, &reply )) return QPoint();
+ if (!reply["getCenter"].isValid()) {
+ fail("Failed to get center for item");
+ return QPoint();
+ }
+ return reply["getCenter"].value<QPoint>();
+}
+/*!
+ Returns a list of all the labels that are visible in the current active window or the widget specified by \a {queryPath}.
+ A label is usually a non-editable widget (such as a QLabel) that is associated with an editable field. The label is used to
+ give the user a visual clue of the meaning of the editable field. Labels are used by the user, and by QtUitest, to
+ navigate to fields.
+
+ The items will be returned in the order they are displayed, i.e. from top left to bottom right.
+
+ Example:
+ \code
+ // Verify that the current dialogs contains Labels named 'Name' and 'Email'
+ var g = getLabels();
+ verify( g.length == 2 );
+ verify( g.contains("Name") );
+ verify( g.contains("Email") );
+ \endcode
+
+ \sa {Query Paths}, {Querying Objects}
+*/
+QStringList QSystemTest::getLabels( const QString &queryPath )
+{
+ QStringList ret;
+ if (runAsManualTest()) {
+ manualTestData( "all labels for " + quote(queryPath) );
+ ret = QStringList() << "MAGIC_DATA";
+ return ret;
+ }
+
+ return queryWithReturn(ret, "getLabels", queryPath);
+}
+
+/*!
+ Returns text stored in the clipboard, or an empty string if the clipboard does not contain any text.
+
+ \sa setClipboardText()
+*/
+QString QSystemTest::getClipboardText( )
+{
+ if (runAsManualTest()) {
+ manualTestData( "the current clipboard text" );
+ return "MAGIC_DATA";
+ }
+ QString ret;
+ return queryWithReturn(ret, "getClipboardText", "");
+}
+
+/*!
+ Copies \a {text} into the clipboard.
+
+ \sa getClipboardText()
+*/
+void QSystemTest::setClipboardText( const QString& text )
+{
+ if (runAsManualTest()) {
+ manualTest( "save " + quote(text) + " to the clipboard" );
+ return;
+ }
+
+ QTestMessage message("setClipboardText");
+ message["text"] = text;
+ queryPassed( "OK", "", BT(message));
+}
+
+/*!
+ Returns the current window title for the application specified by \a {queryPath}.
+ If \a queryPath contains a widget component, it will be ignored.
+
+ Example:
+ \code
+ startApplication("Contacts") );
+
+ ...
+
+ // Make sure we are still in contacts
+ compare( currentTitle(), "Contacts" );
+ \endcode
+
+ \sa {Query Paths}
+*/
+QString QSystemTest::currentTitle( const QString &queryPath )
+{
+ if (runAsManualTest()) {
+ if (queryPath.isEmpty())
+ manualTestData( "the current title" );
+ else
+ manualTestData( "the current title for " + quote(queryPath) );
+ return "MAGIC_DATA";
+ }
+
+ QString ret;
+ return queryWithReturn(ret, "currentTitle", queryPath);
+}
+
+/*!
+ Returns the window titles for all top level windows in the application.
+*/
+QStringList QSystemTest::getWindowTitles()
+{
+ if (runAsManualTest()) {
+ manualTestData( "all window titles" );
+ return QStringList("MAGIC_DATA");
+ }
+
+ QStringList ret;
+ return queryWithReturn(ret, "getWindowTitles", "");
+}
+
+/*!
+ Brings the window specified by \a titleOrSignature to the foreground.
+*/
+void QSystemTest::activateWindow( const QString &titleOrSignature )
+{
+ if (runAsManualTest()) {
+ manualTest( "activate window " + quote(titleOrSignature) );
+ return;
+ }
+
+ QTestMessage message("activateWindow");
+ message["window"] = titleOrSignature;
+ queryPassed( "OK", "", BT(message));
+}
+
+/*!
+ Returns the name of the application which currently has keyboard focus.
+ The name will be the name returned by QCoreApplication::applicationName(),
+ which may be an empty string.
+
+ Example:
+ \code
+ compare( currentApplication(), "addressbook" );
+ \endcode
+
+ \sa applicationVersion()
+*/
+QString QSystemTest::currentApplication()
+{
+ return m_test_app->appName();
+}
+
+/*!
+ Returns the version of the application under test. This is the value
+ returned by QCoreApplication::applicationVersion(), which may be an
+ empty string.
+
+ \sa applicationName()
+*/
+QString QSystemTest::applicationVersion()
+{
+ return m_test_app->appVersion();
+}
+
+/*!
+ Returns the version of Qt used by the application under test.
+
+ \sa applicationName()
+*/
+QString QSystemTest::qtVersion()
+{
+ return m_test_app->qtVersion();
+}
+
+/*!
+ \internal
+ Returns the value of the environment variable for \a key set on the test
+ system.
+*/
+QString QSystemTest::getenv(QString const& key)
+{
+ if (runAsManualTest()) {
+ manualTestData( "environment variable " + quote(key) );
+ return "MAGIC_DATA";
+ }
+
+ QTestMessage message("getenv");
+ message["key"] = key;
+
+ QTestMessage reply;
+ if (!queryPassed( "OK", "", BT(message), "", &reply )) return QString();
+ if (!reply["getenv"].isValid()) {
+ fail("No data in reply to getenv");
+ return QString();
+ }
+ return reply["getenv"].toString();
+}
+
+/*!
+ Returns true if the test system is running the operating system specified by \a os.
+
+ The supported values are: UNIX, LINUX, MAEMO, MAC, WIN32, WINCE and SYMBIAN.
+*/
+bool QSystemTest::checkOS(QString const& os)
+{
+ if (runAsManualTest()) {
+ manualTestData( "Operating System equals " + quote(os) );
+ return true;
+ }
+
+ QTestMessage message("checkOS");
+ message["os"] = os;
+
+ QTestMessage reply;
+ if (!queryPassed( "OK", "", BT(message), "", &reply )) return false;
+ if (!reply["checkOS"].isValid()) {
+ fail("Failed to get details of OS");
+ return false;
+ }
+ return reply["checkOS"].toBool();
+}
+
+/*!
+ Returns the target identifier is used when needing to uniquely identifying
+ the target.
+ Default value for target identifier is \bold default unless set changed by
+ \list
+ \o the -targetID option to qtuitestrunner
+ \o the value of $QTUITEST_TARGETID environment variable when qtuitestrunner is launched
+ \o the value set using the function setTargetIdentifier() in the test script
+ \endlist
+
+ /sa setTargetIdentifier()
+ */
+QString QSystemTest::targetIdentifier()
+{
+ if (m_run_as_manual_test)
+ return QString();
+
+ QTestMessage message("targetIdentifier");
+
+ QTestMessage reply;
+ if (!queryPassed( "OK", "", BT(message), "", &reply )) return QString();
+ if (!reply["targetIdentifier"].isValid()) {
+ fail("No data in reply to targetIdentifier");
+ return QString();
+ }
+ return reply["targetIdentifier"].toString();
+}
+
+/*!
+ Sets the target identifier is used when needing to uniquely identifying
+ the target.
+
+ /sa targetIdentifier()
+ */
+void QSystemTest::setTargetIdentifier(const QString &id)
+{
+ if (m_run_as_manual_test)
+ return;
+
+ QTestMessage reply;
+ QTestMessage message("setTargetIdentifier");
+ message["targetIdentifier"] = id;
+
+ queryPassed( "OK", "", BT(message), "", &reply);
+}
+
+/*!
+ \internal
+ Grabs a snapshot of the widget specified by \a {queryPath}, optionally excluding \a maskedWidgets from the snapshot. Each masked widget is replaced by a black rectangle, and any overlapping widgets will also be concealed.
+
+ To get a snapshot of the entire application window, use the query path "".
+
+ Example:
+ \code
+ // Get current screenshot and save to disk
+ QImage img = grabImage("");
+ verify( img.save(currentDataPath() + "/snapshot.png", "PNG") );
+ \endcode
+
+ \sa {Query Paths}, verifyImage(), saveScreen()
+*/
+QImage QSystemTest::grabImage(const QString &queryPath, const QStringList &maskedWidgets )
+{
+ if (runAsManualTest()) {
+ // FIXME TODO
+ qDebug() << "QSystemTest::grabImage() skipped";
+ return QImage();
+ }
+
+ QImage im;
+
+ QTestMessage message("grabPixmap");
+ message["mask"] = maskedWidgets;
+
+ QTestMessage reply = query( BT(message), queryPath );
+ if (reply["status"] != "OK" || queryFailed()) {
+ fail( "Couldn't grab image: " + reply.toString());
+ } else {
+ if (!reply["grabPixmap"].isValid()) {
+ fail( "Test slave returned no image" );
+ } else {
+ im = reply["grabPixmap"].value<QImage>();
+ }
+ }
+
+ return im;
+}
+
+/*!
+ Grabs a snapshot of the widget specified by \a {queryPath}, and compares it against the reference snapshot \a expectedName.
+
+ New snapshots are gathered by running the test in \l {Learn Mode}{learn mode}, and are stored in the \c testdata subdirectory of the directory containing the test script. When learn mode is used, a failed image comparison will result in a tester being presented with a manual verification dialog.
+
+ If there is a mismatch between the images, the current test fails.
+
+ When in learn mode, if \a comment is provided it will be shown to the user to help in determining whether or not the pixmap should be accepted.
+
+ \a maskedWidgets is a list of query paths specifying widgets to be excluded from the snapshot. This allows constantly changing widgets to be hidden from view while the snapshot is taken. Each masked widget is replaced by a black rectangle, and any overlapping widgets will also be concealed.
+
+ Example:
+ \code
+ verifyImage( "task_completed", "", "Verify that the current task is shown with a green tick indicating completion" );
+ \endcode
+
+ \sa saveScreen()
+*/
+void QSystemTest::verifyImage( const QString &expectedName, const QString &queryPath, const QString &comment, const QStringList &maskedWidgets )
+{
+ if (runAsManualTest()) {
+ if (!comment.isEmpty())
+ manualTest( comment );
+ else
+ manualTest( "verify that " + quote(queryPath) + " looks as expected" );
+ return;
+ }
+
+ // Determine the filename
+ // If the function was passed an explicit filename (indicated by an extension) it will be used directly
+ // otherwise system specific values are prepended to the name.
+ QString expectedFilename = currentDataPath();
+ if ( expectedName.endsWith( ".png" ) ) {
+ expectedFilename += "/" + expectedName;
+ } else {
+ expectedFilename += QString("/%1_%2.png")
+ .arg( m_config_id )
+ .arg( expectedName );
+ }
+
+ // The reference snapshot should exist in the data directory for this testcase.
+ // If it's not there, and we're not in learn mode, we will fail.
+ if ( (learnMode() == LearnNone || m_auto_mode) && !QFile::exists(expectedFilename) ) {
+ fail(QString("Reference snapshot '%1' doesn't exist yet. Please manually run test in learn mode.").arg(expectedFilename));
+ return;
+ }
+
+ // Now query AUT for the current snapshot
+ QImage actualIm( grabImage(queryPath, maskedWidgets) );
+ if (actualIm.isNull()) return;
+
+ bool snapshotOk = false;
+ QDir("/").mkpath(currentDataPath());
+ QImage expectedIm(expectedFilename);
+
+ // Now do the actual comparisons.
+ // If we are not in learn mode, a difference in snapshots will cause a failure.
+ // If we are in learn mode, a difference will cause a user prompt to accept the new snapshot.
+ // If we are in learn-all mode, the prompt will be displayed every time.
+
+ if ( (learnMode() != LearnAll || m_auto_mode) && QFile::exists(expectedFilename) ) {
+ snapshotOk = imagesAreEqual(actualIm, expectedIm);
+ }
+
+ if ( !m_auto_mode && !snapshotOk && learnMode() != LearnNone ) {
+ if ( learnImage(actualIm, expectedIm, comment) ) {
+ QFile::remove(expectedFilename);
+ if ( !actualIm.save(expectedFilename, "PNG", 0) ) {
+ QWARN(QString("Failed to save image to %1!").arg(expectedFilename).toLatin1());
+ } else {
+ QTestIDE::instance()->newTestData(expectedFilename);
+ }
+ snapshotOk = true;
+ } else {
+ fail( "New image not accepted by tester" );
+ return;
+ }
+ }
+
+ if (!snapshotOk) {
+ // Failure, so rename the snapshot to identify it for future reference
+ expectedFilename.replace(".png", "_failure.png");
+ if ( !actualIm.save(expectedFilename, "PNG", 0) ) {
+ QWARN(QString("Failed to save failure pixmap to %1!").arg(expectedFilename).toLatin1());
+ }
+ fail( "Snapshots are not the same" );
+ return;
+ }
+
+ /* By using compare, we go through the "expected failure" processing. */
+ /* Without this, we won't get XPASS */
+ QTest::compare_helper( true, "Snapshots are the same", qPrintable(currentFile()), currentLine() );
+}
+
+/*!
+ Compares the widget specified by \a {queryPath} against the reference snapshot \a expectedName.
+
+ \a maskedWidgets is a list of query paths specifying widgets to be excluded from the snapshot. This allows constantly changing widgets to be hidden from view while the snapshot is taken. Each masked widget is replaced by a black rectangle, and any overlapping widgets will also be concealed.
+
+ Returns true if the images match, or false otherwise.
+
+ The reference snapshot can be one previously learned using verifyImage(), or an image saved using saveScreen(), in which case
+ the .png filename extension must be specified.
+
+ \sa verifyImage(), saveScreen()
+*/
+bool QSystemTest::compareImage( const QString &expectedName, const QString &queryPath, const QStringList &maskedWidgets )
+{
+ if (runAsManualTest()) {
+ manualTestData( quote(expectedName) + " looks similar to " + quote(queryPath) );
+ return true;
+ }
+
+ // Determine the filename
+ // If the function was passed an explicit filename (indicated by an extension) it will be used directly
+ // otherwise system specific values are prepended to the name.
+ QString expectedFilename = currentDataPath();
+ if ( expectedName.endsWith( ".png" ) ) {
+ expectedFilename += "/" + expectedName;
+ } else {
+ expectedFilename += QString("/%1_%2.png")
+ .arg( m_config_id )
+ .arg( expectedName );
+ }
+
+ // The reference snapshot should exist in the data directory for this testcase.
+ if ( !QFile::exists(expectedFilename) ) {
+ fail(QString("Reference snapshot '%1' doesn't exist.").arg(expectedFilename));
+ return false;
+ }
+
+ // Now query AUT for the current snapshot
+ QImage actualIm( grabImage(queryPath, maskedWidgets) );
+ if (queryFailed()) return false;
+
+ QImage expectedIm(expectedFilename);
+
+ // Now do the actual comparisons.
+ return imagesAreEqual(actualIm, expectedIm);
+}
+
+/*!
+ Reads \a srcFile from the test system and returns its contents.
+ if \a srcFile contains environment variables, they will be expanded on the test system.
+
+ \sa {File Management}, getFile(), putData(), putFile()
+*/
+QString QSystemTest::getData( const QString &srcFile )
+{
+ if (runAsManualTest()) {
+ manualTestData( "the contents of file " + quote(srcFile) );
+ return "MAGIC_DATA";
+ }
+
+ QTestMessage message("getFile");
+ message["path"] = srcFile;
+
+ QTestMessage reply;
+ if (!queryPassed( "OK", "", BT(message), "", &reply )) return QString();
+ if (!reply["getFile"].isValid()) {
+ fail("No data in reply to getData");
+ return QString();
+ }
+ return reply["getFile"].toString();
+}
+
+/*!
+ \internal
+ Returns image size of image specified in \a srcFile.
+ if \a srcFile contains environment variables, they will be expanded on the test system.
+
+ Note: QSize does not have toString(), see QSize docs for methods returning common types.
+
+ Example:
+ \code
+ // Find the image size of specified image
+ var imgSize = getImageSize( documentsPath() + "image/foo.jpg" );
+ prompt( "Image size is: " + imgSize.width() + " by " + imgSize.height() + ".\n" );
+ \endcode
+*/
+QSize QSystemTest::getImageSize( const QString &srcFile)
+{
+ if (runAsManualTest()) {
+ manualTestData( "the image size of " + quote(srcFile) );
+ return QSize();
+ }
+
+ QTestMessage message("getImageSize");
+ message["path"] = srcFile;
+
+ QTestMessage reply;
+ if (!queryPassed( "OK", "", BT(message), "", &reply )) return QSize();
+ if (!reply["getImageSize"].isValid()) {
+ fail("No data in reply to getImageSize");
+ return QSize();
+ }
+ return reply["getImageSize"].value<QSize>();
+}
+/*!
+ Returns geometry of the widget specified in \a queryPath, with position (x,y) co-ordinates being global.
+ Note: QRect does not have toString() method, refer to QRect docs for methods returning common types.
+
+ Example:
+ \code
+ // pass the test if widgets do not overlap
+ var first_widget = getGeometry("Button1");
+ var second_widget = getGeometry("Button2");
+ // intersects returns true on overlap, false when not; verify causes test to fail on false
+ verify( !first_widget.intersects(second_widget), "Specified widgets overlap.");
+ \endcode
+ Example two - a non-mainstream situation:
+
+ select() may work in an undefined manner with custom widgets/items, implementing custom select() methods isn't ideal - each would require writing and testing.
+ On a device with a primary input method of mouse/touchscreen there may not be key code mapping for keys which don't exist - therefore mouse events should be used. However devices may have different geometry, and widget geometry can change between invocations. The example below uses mouseClick() without prior geometry knowledge, though a way is needed to determine where to click, the example shows mouseClick() in the middle of an area defined by the 4th col and 4th row in a uniform grid of the area of the active widget.
+ \code
+ // mouseClick() a widget or item with a fixed position inside its parent widget
+ var geo = getGeometry();
+ var select_x = geo.x() + (( geo.width() / 8) * 7);
+ var select_y = geo.y() + (( geo.height() / 8) * 7);
+ mouseClick(select_x, select_y);
+ \endcode
+
+ \sa select(), mouseClick(), QRect
+*/
+QRect QSystemTest::getGeometry( const QString &queryPath )
+{
+ if (runAsManualTest()) {
+ manualTestData( "the geometry of " + quote(queryPath) );
+ return QRect();
+ }
+
+ QRect ret;
+ return queryWithReturn(ret, "getGeometry", queryPath);
+}
+
+/*!
+ Retrieves \a srcFile from the test system and copies it to \a destFile on the local machine.
+ if \a srcFile contains environment variables, they will be expanded on the test system.
+
+ Example:
+ \code
+ // Copy a settings file to the local machine
+ getFile("$HOME/Settings/foo.conf", "/tmp/foo.conf" );
+ \endcode
+
+ \sa {File Management}, getData(), putData(), putFile()
+*/
+void QSystemTest::getFile( const QString &srcFile, const QString &destFile )
+{
+ if (runAsManualTest()) {
+ manualTest( "copy file " + quote(srcFile) + " from the tested device to " + quote(destFile) + " on your local machine" );
+ return;
+ }
+
+ QTestMessage message("getFile");
+ message["path"] = srcFile;
+
+ QTestMessage reply;
+ if (!queryPassed( "OK", "", BT(message), "", &reply )) return;
+ if (!reply["getFile"].isValid()) {
+ reply["status"] = "ERROR_MISSING_DATA";
+ QFAIL(reply);
+ }
+ QByteArray data = reply["getFile"].toByteArray();
+ QFile out(destFile);
+ if (!out.open(QIODevice::WriteOnly|QIODevice::Truncate))
+ QFAIL( "Couldn't open local file '" + destFile + "'");
+ qint64 b;
+ for (b = out.write(data); b < data.size() && -1 != b; ++b) {
+ qint64 this_b = out.write(data.mid(b));
+ if (-1 == this_b) b = -1;
+ else b += this_b;
+ }
+ if (-1 == b)
+ QFAIL( "Couldn't write to local file '" + destFile + "'");
+}
+
+/*!
+ Transfers \a srcFile from the local machine and copies it to \a destFile on the test system.
+ if \a destFile contains environment variables, they will be expanded on the test system.
+
+ By default, the file permissions of the destination file will be set to those of the source
+ file. This can be overridden by specifying \a permissions.
+
+ Example:
+ \code
+ // Force test system to use certain settings
+ putFile("testdata/my_settings.conf", "$HOME/Settings/foo.conf");
+
+ // Specify file permissions
+ putFile("testdata/my_file", "$HOME/my_file", QFile.WriteOwner | QFile.ReadOwner | QFile.ReadOther);
+ \endcode
+
+ \sa {File Management}, putData()
+*/
+void QSystemTest::putFile( const QString &srcFile, const QString &destFile, QFile::Permissions permissions )
+{
+ if (runAsManualTest()) {
+ QString perm;
+ if (permissions == 0) {
+ perm = " using the current file permissions";
+ } else {
+ perm = " using permissions: ";
+ if ((permissions & QFile::ReadOwner) > 0) perm += "ReadOwner, ";
+ if ((permissions & QFile::WriteOwner) > 0) perm += "WriteOwner, ";
+ if ((permissions & QFile::ExeOwner) > 0) perm += "ExeOwner, ";
+ if ((permissions & QFile::ReadUser) > 0) perm += "ReadUser, ";
+ if ((permissions & QFile::WriteUser) > 0) perm += "WriteUser, ";
+ if ((permissions & QFile::ExeUser) > 0) perm += "ExeUser, ";
+ if ((permissions & QFile::ReadGroup) > 0) perm += "ReadGroup, ";
+ if ((permissions & QFile::WriteGroup) > 0) perm += "WriteGroup, ";
+ if ((permissions & QFile::ExeGroup) > 0) perm += "ExeGroup, ";
+ if ((permissions & QFile::ReadOther) > 0) perm += "ReadOther, ";
+ if ((permissions & QFile::WriteOther) > 0) perm += "WriteOther, ";
+ if ((permissions & QFile::ExeOther) > 0) perm += "ExeOther, ";
+ if (perm.endsWith(", ")) perm = perm.left(perm.length()-2);
+ }
+ manualTest( "copy file " + quote(srcFile) + " from your local machine to " + quote(destFile) + " on the tested device" + perm);
+ return;
+ }
+
+ QFile f(srcFile);
+ if (!f.open(QIODevice::ReadOnly))
+ QFAIL( "Couldn't open '" + srcFile + "'" );
+
+ putData(f.readAll(), destFile, permissions ? permissions : f.permissions());
+}
+
+/*!
+ Reads text from the specified \a file and returns the contents as a QString.
+
+ This can be useful when prompting the user with larger amounts of text.
+
+ \sa {File Management}, prompt()
+*/
+QString QSystemTest::readLocalFile( const QString &file )
+{
+ QFile f(file);
+ QString ret;
+ if (!f.open(QIODevice::ReadOnly)) {
+ setQueryError( "Couldn't open '" + file + "'" );
+ return ret;
+ }
+ ret = f.readAll();
+ return ret;
+}
+
+/*!
+ Transfers \a data from the local machine and copies it to \a destFile on the test system.
+ if \a destFile contains environment variables, they will be expanded on the test system.
+ The file permissions of the destination file can be specified using \a permissions.
+
+ \sa {File Management}, putFile()
+*/
+void QSystemTest::putData( const QByteArray &data, const QString &destFile, QFile::Permissions permissions )
+{
+ QTestMessage message("putFile");
+ message["path"] = destFile;
+ message["data"] = data;
+ if (permissions) {
+ message["permissions"] = static_cast<int>(permissions);
+ }
+ queryPassed("OK", "", BT(message));
+}
+
+/*!
+ Delete \a path from the test system. Can be a file, or can be a directory
+ tree, in which case the entire tree is recursively deleted.
+ If \a path contains environment variables, they will be expanded on the
+ test system.
+
+ Example:
+ \code
+ // Force test system to start with clean settings
+ deletePath("$HOME/Settings");
+ \endcode
+
+ \sa {File Management}
+*/
+void QSystemTest::deletePath( const QString &path )
+{
+ if (runAsManualTest()) {
+ manualTest( "delete the contents of path " + quote(path) + " on your device" );
+ return;
+ }
+
+ QTestMessage message("deletePath");
+ message["path"] = path;
+ queryPassed( "OK", "", BT(message) );
+}
+
+/*!
+ Invoke method \a method on object \a queryPath on the test system.
+ Invokable methods include only Qt signals and slots.
+
+ The method will be invoked using the Qt connection type \a type. This can
+ almost always be Qt::AutoConnection, but in a few cases Qt.QueuedConnection may
+ be necessary.
+
+ The optional arguments \a arg0, \a arg1, \a arg2, \a arg3, \a arg4, \a arg5, \a arg6,
+ \a arg7, \a arg8 and \a arg9 will be passed to the method if given.
+
+ Returns true if the method could be invoked, false otherwise.
+
+ Example:
+ \code
+ // Hide this field because it keeps changing and we want a snapshot
+ verify( invokeMethod("Time", "setVisible(bool)", Qt.AutoConnection, false) );
+ verifyImage("good_snapshot");
+ // Put the field back
+ verify( invokeMethod("Time", "setVisible(bool)", Qt.AutoConnection, true) );
+ \endcode
+
+ \sa {Query Paths}, {Querying Objects}
+*/
+bool QSystemTest::invokeMethod( const QString &queryPath, const QString &method, Qt::ConnectionType type,
+ const QVariant &arg0, const QVariant &arg1, const QVariant &arg2,
+ const QVariant &arg3, const QVariant &arg4, const QVariant &arg5,
+ const QVariant &arg6, const QVariant &arg7, const QVariant &arg8,
+ const QVariant &arg9 )
+{
+ if (runAsManualTest()) {
+ manualTestData( "invoke method " + quote(method) + " on " + quote(queryPath) );
+ return true;
+ }
+
+ QTestMessage message("invokeMethod");
+ message["method"] = method;
+ message["returns"] = false;
+ message["conntype"] = (int)type;
+
+ QVariantList argList;
+ if (arg0.isValid()) argList << arg0;
+ if (arg1.isValid()) argList << arg1;
+ if (arg2.isValid()) argList << arg2;
+ if (arg3.isValid()) argList << arg3;
+ if (arg4.isValid()) argList << arg4;
+ if (arg5.isValid()) argList << arg5;
+ if (arg6.isValid()) argList << arg6;
+ if (arg7.isValid()) argList << arg7;
+ if (arg8.isValid()) argList << arg8;
+ if (arg9.isValid()) argList << arg9;
+
+ message["args"] = argList;
+
+ QTestMessage reply;
+ if (!queryPassed( QStringList("OK"), QStringList()
+ << "ERROR_NO_METHOD"
+ << "ERROR_METHOD_NOT_INVOKABLE"
+ << "ERROR_WRONG_ARG_COUNT"
+ << "ERROR_NO_RETURN"
+ << "ERROR_IN_INVOKE", BT(message), queryPath, &reply)) return false;
+
+ return true;
+}
+
+/*!
+ \overload
+ Invokes the given method using connection type Qt.AutoConnection.
+*/
+bool QSystemTest::invokeMethod( const QString &queryPath, const QString &method,
+ const QVariant &arg0, const QVariant &arg1, const QVariant &arg2,
+ const QVariant &arg3, const QVariant &arg4, const QVariant &arg5,
+ const QVariant &arg6, const QVariant &arg7, const QVariant &arg8,
+ const QVariant &arg9 )
+{
+ return invokeMethod(queryPath, method, Qt::AutoConnection, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 );
+}
+
+/*!
+ Set the Qt property named \a name on object \a queryPath to value \a value
+ on the test system.
+
+ Errors can occur in this function.
+
+ Example:
+ \code
+ // Set the text of this field without simulating key presses
+ setProperty("Name", "text", "Billy Jones");
+ \endcode
+
+ \sa {Query Paths}, {Querying Objects}
+*/
+void QSystemTest::setProperty( const QString &queryPath, const QString &name, const QVariant &value )
+{
+ if (runAsManualTest()) {
+ manualTest( "set property " + quote(name) + " to " + quote(value.toString()) );
+ return;
+ }
+
+ QTestMessage message("setProperty");
+ message["property"] = name;
+ message["value"] = value;
+
+ queryPassed( "OK", "", BT(message), queryPath );
+}
+
+/*!
+ Get the value of the Qt property named \a name on object \a queryPath on the test system.
+
+ Example:
+ \code
+ // Get the text of this field without using getText()
+ var t = getProperty("Name", "text").toString();
+ \endcode
+
+ \sa {Query Paths}, {Querying Objects}
+*/
+QVariant QSystemTest::getProperty( const QString &queryPath, const QString &name )
+{
+ if (runAsManualTest()) {
+ manualTest( "property " + quote(name) + " from " + quote(queryPath) );
+ return QVariant("MAGIC_DATA");
+ }
+
+ QTestMessage message("getProperty");
+ message["property"] = name;
+
+ QVariant out;
+
+ QTestMessage reply;
+ if (!queryPassed( "OK", "", BT(message), queryPath, &reply)) return out;
+
+ return reply["getProperty"];
+}
+
+/*!
+ Returns the signatures of the widgets that have \a property matching \a value.
+ This can be used to identify widgets that do not have an associated label text.
+
+ In addition to normal QObject properties, "inherits" and "className" may also
+ be used, with the class specified as a string.
+
+ Example:
+ \code
+ // Select the button with the tool tip "Quit"
+ // assumes that only one widget will match
+ var button = findByProperty("toolTip", "Quit");
+ activate(button);
+ \endcode
+
+ If more than one widget matches, the list returned is sorted by widget position.
+ If no matching widgets are found, the list returned is empty, but the test does
+ not fail. Use findWidget() to cause a test failure when no matching widgets are found.
+
+ \sa findWidget(), signature(), {Query Paths}, {Querying Objects}
+*/
+QStringList QSystemTest::findByProperty( const QString &property, const QVariant &searchValue )
+{
+ if (runAsManualTest()) {
+ manualTest( "the property " + quote(property) + " of " + quote(searchValue.toString()) );
+ return QStringList("MAGIC_DATA");
+ }
+
+ QTestMessage message("findByProperty");
+ message["property"] = property;
+ message["searchValue"] = searchValue;
+
+ QStringList out;
+ QTestMessage reply;
+ if (!queryPassed( "OK", "", BT(message), "", &reply)) return out;
+ out = reply["findByProperty"].toStringList();
+ return out;
+}
+
+/*!
+ Returns the signatures of the widgets that match all of the property values
+ specified in \a searchValues.
+
+ This can be used to identify widgets that do not have an associated label text.
+
+ Example:
+ \code
+ // Select the button with the tool tip "Quit"
+ // assumes that only one widget will match
+ var button = findByProperty( {className: "QToolButton",
+ toolTip: "Quit"} );
+ activate(button);
+ \endcode
+
+ If more than one widget matches, the list returned is sorted by widget position.
+ If no matching widgets are found, the list returned is empty, but the test does
+ not fail. Use findWidget() to cause a test failure when no matching widgets are found.
+
+ \sa findWidget(), signature(), {Query Paths}, {Querying Objects}
+*/
+QStringList QSystemTest::findByProperty( const QVariantMap &searchValues )
+{
+ QTestMessage message("findByProperties");
+ message["searchValues"] = searchValues;
+
+ QStringList out;
+ QTestMessage reply;
+ if (!queryPassed( "OK", "", BT(message), "", &reply)) return out;
+ out = reply["findByProperties"].toStringList();
+ return out;
+}
+
+/*!
+ Returns the signatures of the widgets that have \a property matching \a value.
+
+ This function is the same as findByProperty(), but results in a test failure
+ if no matching widgets are found.
+
+ \sa findByProperty()
+*/
+QStringList QSystemTest::findWidget( const QString &property, const QVariant &searchValue )
+{
+ QStringList ret = findByProperty(property, searchValue);
+ if (ret.isEmpty()) {
+ fail("No widget found with specified property.");
+ }
+ return ret;
+}
+
+/*!
+ Returns the signatures of the widgets that match all of the property values
+ specified in \a searchValues.
+
+ This function is the same as findByProperty(), but results in a test failure
+ if no matching widgets are found.
+
+ \sa findByProperty()
+*/
+QStringList QSystemTest::findWidget( const QVariantMap &searchValues )
+{
+ QStringList ret = findByProperty(searchValues);
+ if (ret.isEmpty()) {
+ fail("No widget found with specified properties.");
+ }
+ return ret;
+}
+
+/*!
+ Retrieves a QSettings settings value from the test system located in \a file, settings group \a group, key \a key.
+ If \a file contains environment variables, they will be expanded on the test system.
+
+ Example:
+ \code
+ // What's our primary input mode?
+ var primaryInput = getSetting("$QPEDIR/etc/defaultbuttons.conf", "Device", "PrimaryInput");
+ \endcode
+
+ \sa setSetting(), QSettings
+*/
+QVariant QSystemTest::getSetting( const QString &file, const QString &group, const QString &key )
+{
+ return getSetting(QString(), QString(), file, group, key);
+}
+
+/*!
+ Retrieves a QSettings settings value from the test system located in the settings file for
+ organization \a organization and application \a application, as passed to the QSettings
+ constructor. The settings value retrieved will be group \a group, key \a key.
+
+ \sa setSetting(), QSettings
+*/
+QVariant QSystemTest::getSetting( const QString &organization, const QString &application, const QString &group, const QString &key )
+{
+ return getSetting(organization, application, QString(), group, key);
+}
+
+/*!
+ Set a QSettings settings \a value on the test system located in \a file, settings group \a group, key \a key.
+
+ Example:
+ \code
+ // Turn on english and deutsch input languages
+ setSetting("$HOME/Settings/Trolltech/locale.conf", "Language", "InputLanguages", "en_US de" );
+ \endcode
+
+ \sa getSetting(), QSettings
+*/
+void QSystemTest::setSetting( const QString &file, const QString &group, const QString &key, const QVariant &value )
+{
+ setSetting(QString(), QString(), file, group, key, value);
+}
+
+/*!
+ Set a QSettings settings \a value on the test system located in the settings file
+ for the given \a organization and \a application, as passed to a QSettings constructor.
+ The value set will be in settings group \a group, key \a key.
+
+ \sa getSetting(), QSettings
+*/
+void QSystemTest::setSetting( const QString &organization, const QString &application, const QString &group, const QString &key, const QVariant &value )
+{
+ setSetting(organization, application, QString(), group, key, value);
+}
+
+static QStringList filter = QStringList();
+
+
+/*! \internal */
+void QSystemTest::applicationStandardOutput(QList<QByteArray> const& lines)
+{
+ foreach (QByteArray const& line, lines)
+ QDebug(QtDebugMsg) << line;
+}
+
+/*! \internal */
+void QSystemTest::applicationStandardError(QList<QByteArray> const& lines)
+{
+ foreach (QByteArray const& line, lines)
+ QDebug(QtDebugMsg) << line;
+}
+
+/*!
+ Switches the 'strict syntax' checking mode for the System test to \a on.
+
+ In strict mode the following commands are no longer allowed and will cause an immediate failure:
+ \list
+ \i keyClick()
+ \i keyPress()
+ \i keyRelease()
+ \i keyClickHold()
+ \endlist
+
+ Strict mode also verifies that every 'Title' change is covered by a call to waitForTitle(): any
+ action that results in a Dialog to be shown (with a different title) will cause a test failure
+ unless a waitForTitle() is called on the next line of the test script.
+*/
+void QSystemTest::strict( bool on )
+{
+ m_strict_mode = on;
+}
+
+/*!
+ Simulates a \a key press for the application specified by \a queryPath.
+ \a key is a Qt::Key describing the key to be pressed.
+
+ Example:
+ \code
+ // Press (do not release) F23 key in current app
+ keyPress( Qt.Key_F23 );
+ \endcode
+
+ \sa {Query Paths}, {Keypad Simulation}
+*/
+void QSystemTest::keyPress( Qt::Key key, const QString &queryPath )
+{
+ if (m_strict_mode) QFAIL( "ERROR: keyPress is not allowed in strict mode" );
+
+ QTestMessage message("keyPress");
+ message["key"] = (int)key;
+ QString qp = queryPath;
+ if (qp.isEmpty()) qp = "";
+ queryPassed( "OK", "", BT(message), qp );
+}
+
+/*!
+ Simulates a \a key release for the application specified by \a queryPath.
+ \a key is a Qt::Key describing the key to be released.
+
+ Example:
+ \code
+ // Release Up key in current app
+ keyRelease( Qt.Key_Up );
+ \endcode
+
+ \sa {Query Paths}, {Keypad Simulation}
+*/
+void QSystemTest::keyRelease( Qt::Key key, const QString &queryPath )
+{
+ if (m_strict_mode) QFAIL( "ERROR: keyRelease is not allowed in strict mode" );
+
+ QTestMessage message("keyRelease");
+ message["key"] = (int)key;
+ QString qp = queryPath;
+ if (qp.isEmpty()) qp = "";
+ queryPassed( "OK", "", BT(message), qp );
+}
+
+/*!
+ Simulates a \a key click (press and release) for the application specified by \a queryPath.
+ \a key is a string describing the key to be released.
+
+ Example:
+ \code
+ // Go right 5 times, then select
+ for (int i = 0; i < 5; ++i) keyClick( Qt.Key_Right );
+ keyClick( Qt.Key_Select );
+ \endcode
+
+ \sa {Query Paths}, {Keypad Simulation}
+*/
+void QSystemTest::keyClick( Qt::Key key, const QString &queryPath )
+{
+ if (QSystemTest::runAsManualTest()) {
+ manualTest( QString("click key %1 in application %2")
+ .arg(QKeySequence(key).toString())
+ .arg(quote(queryPath)) );
+ return;
+ }
+
+ if (m_strict_mode) QFAIL( "ERROR: keyClick is not allowed in strict mode" );
+
+ QTestMessage message("keyClick");
+ message["key"] = (int)key;
+ QString qp = queryPath;
+ if (qp.isEmpty()) qp = "";
+ queryPassed( "OK", "", BT(message), qp );
+}
+
+/*!
+ Simulates a \a key click and hold (press + wait + release) for \a queryPath.
+ The interval between press and release is set in milliseconds by \a duration.
+
+ Example:
+ \code
+ // Hold hangup key to bring up shutdown app
+ keyClickHold(Qt.Key_Hangup, 3000);
+ \endcode
+
+ \sa {Query Paths}, {Keypad Simulation}
+*/
+void QSystemTest::keyClickHold( Qt::Key key, int duration, const QString &queryPath )
+{
+ if (m_strict_mode) QFAIL( "ERROR: keyClickHold is not allowed in strict mode" );
+
+ m_keyclickhold_key = key;
+ m_keyclickhold_path = queryPath;
+
+ QTestMessage message("keyPress");
+ message["key"] = (int)key;
+ message["duration"] = duration;
+ QString qp = queryPath;
+ if (qp.isEmpty()) qp = "";
+ queryPassed( "OK", "", BT(message), qp );
+
+ if (queryFailed()) return;
+
+ wait(duration);
+
+ if (m_keyclickhold_key) {
+ m_keyclickhold_key = (Qt::Key)0;
+ m_keyclickhold_path = "";
+
+ keyRelease(key, queryPath);
+ }
+}
+
+/*!
+ Simulates a mouse click / touchscreen tap at co-ordinates ( \a x, \a y ).
+
+ Example:
+ \code
+ mouseClick(200, 300);
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mouseClick( int x, int y, QFlags<Qt::MouseButton> buttons )
+{
+ mouseClick(QPoint(x, y), buttons);
+}
+
+/*!
+ Simulates a mouse click / touchscreen tap at \a point.
+
+ Example:
+ \code
+ mouseClick( new QPoint(200, 300) );
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mouseClick( const QPoint &point, QFlags<Qt::MouseButton> buttons )
+{
+ if (QSystemTest::runAsManualTest()) {
+ manualTestData( QString("position (x,y) %1,%2").arg(point.x()).arg(point.y()), true);
+ manualTest( QString("%1 click the mouse on MAGIC_DATA")
+ .arg(mouseButtons(buttons)));
+ return;
+ }
+
+ if (m_strict_mode) QFAIL( "ERROR: mouseClick is not allowed in strict mode" );
+ QTestMessage message("mouseClick");
+ message["pos"] = point;
+ message["buttons"] = (int)buttons;
+ queryPassed( "OK", "", BT(message) );
+}
+
+/*!
+ Simulates a mouse click / touchscreen tap at the center of the widget
+ specified by \a queryPath.
+
+ Example:
+ \code
+ // Click on Accept button
+ mouseClick("Accept");
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mouseClick( const QString &queryPath, QFlags<Qt::MouseButton> buttons )
+{
+ if (QSystemTest::runAsManualTest()) {
+ manualTest( QString("%1 click the mouse on %2")
+ .arg(mouseButtons(buttons)).arg(quote(queryPath)));
+ return;
+ }
+
+ if (m_strict_mode) QFAIL( "ERROR: mouseClick is not allowed in strict mode" );
+ QTestMessage message("mouseClick");
+ message["buttons"] = (int)buttons;
+ queryPassed( "OK", "", BT(message), queryPath );
+ wait(200);
+}
+
+/*!
+ Simulates a mouse click / touchscreen tap at co-ordinates ( \a x, \a y ),
+ with a custom \a duration in milliseconds between press and release.
+
+ Example:
+ \code
+ // Hold at (200, 300) for three seconds
+ mouseClickHold(200, 300, 3000);
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mouseClickHold( int x, int y, int duration, QFlags<Qt::MouseButton> buttons )
+{
+ mouseClickHold(QPoint(x, y), duration, buttons);
+}
+
+/*!
+ Simulates a mouse click / touchscreen tap at \a point,
+ with a custom \a duration in milliseconds between press and release.
+
+ Example:
+ \code
+ // Hold at (200, 300) for three seconds
+ mouseClickHold( new QPoint(200, 300), 3000 );
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mouseClickHold( const QPoint &point, int duration, QFlags<Qt::MouseButton> buttons )
+{
+ if (QSystemTest::runAsManualTest()) {
+ manualTest( QString("%1 click - and HOLD - the mouse on position (x,y) %2,%3 for approximately %4 milliseconds, and then release")
+ .arg(mouseButtons(buttons)).arg(point.x()).arg(point.y()).arg(duration));
+ return;
+ }
+
+ if (m_strict_mode) QFAIL( "ERROR: mouseClickHold is not allowed in strict mode" );
+
+ mousePress(point, buttons);
+ wait(duration);
+ mouseRelease(point, buttons);
+}
+
+/*!
+ Simulates a mouse click / touchscreen tap at the center of the widget
+ specified by \a queryPath, with a custom \a duration in milliseconds
+ between press and release.
+
+ Example:
+ \code
+ // Hold on the "Shutdown" button for three seconds
+ mouseClickHold("Shutdown", 3000);
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mouseClickHold( const QString &queryPath, int duration, QFlags<Qt::MouseButton> buttons )
+{
+ if (QSystemTest::runAsManualTest()) {
+ manualTest( QString("%1 click - and HOLD - the mouse on %2 for approximately %3 milliseconds, and then release")
+ .arg(mouseButtons(buttons)).arg(quote(queryPath)).arg(duration));
+ return;
+ }
+
+ if (m_strict_mode) QFAIL( "ERROR: mouseClickHold is not allowed in strict mode" );
+
+ mousePress(queryPath, buttons);
+ wait(duration);
+ mouseRelease(queryPath, buttons);
+}
+
+/*!
+ Simulates a mouse / touchscreen press at co-ordinates ( \a x, \a y ).
+
+ Example:
+ \code
+ mousePress(200, 300);
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mousePress( int x, int y, QFlags<Qt::MouseButton> buttons )
+{
+ mousePress(QPoint(x, y), buttons);
+}
+
+/*!
+ Simulates a mouse / touchscreen press at \a point.
+
+ Example:
+ \code
+ mousePress( new QPoint(200, 300) );
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mousePress( const QPoint &point, QFlags<Qt::MouseButton> buttons )
+{
+ if (QSystemTest::runAsManualTest()) {
+ manualTest( QString("%1 press - and HOLD - the mouse on position (x,y) %2,%3")
+ .arg(mouseButtons(buttons)).arg(point.x()).arg(point.y()));
+ return;
+ }
+
+ if (m_strict_mode) QFAIL( "ERROR: mousePress is not allowed in strict mode" );
+
+ QTestMessage message("mousePress");
+ message["pos"] = point;
+ message["buttons"] = (int)buttons;
+ queryPassed( "OK", "", BT(message) );
+}
+
+/*!
+ Simulates a mouse / touchscreen press at the center of the widget
+ specified by \a queryPath.
+
+ Example:
+ \code
+ // Press "Edit" button
+ mousePress("Edit");
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mousePress( const QString &queryPath, QFlags<Qt::MouseButton> buttons )
+{
+ if (QSystemTest::runAsManualTest()) {
+ manualTest( QString("%1 press - and HOLD - the mouse on %2")
+ .arg(mouseButtons(buttons)).arg(quote(queryPath)));
+ return;
+ }
+
+ if (m_strict_mode) QFAIL( "ERROR: mousePress is not allowed in strict mode" );
+
+ QTestMessage message("mousePress");
+ message["buttons"] = (int)buttons;
+ queryPassed( "OK", "", BT(message), queryPath );
+}
+
+/*!
+ Simulates a mouse / touchscreen release at co-ordinates ( \a x, \a y ).
+
+ Example:
+ \code
+ mouseRelease(200, 300);
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mouseRelease( int x, int y, QFlags<Qt::MouseButton> buttons )
+{
+ mouseRelease(QPoint(x, y), buttons);
+}
+
+/*!
+ Simulates a mouse / touchscreen release at \a point.
+
+ Example:
+ \code
+ mouseRelease( new QPoint(200, 300) );
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mouseRelease( const QPoint &point, QFlags<Qt::MouseButton> buttons )
+{
+ if (QSystemTest::runAsManualTest()) {
+ manualTest( QString("%1 release the mouse on position (x,y) %2,%3")
+ .arg(mouseButtons(buttons)).arg(point.x()).arg(point.y()));
+ return;
+ }
+
+ if (m_strict_mode) QFAIL( "ERROR: mouseRelease is not allowed in strict mode" );
+
+ QTestMessage message("mouseRelease");
+ message["buttons"] = (int)buttons;
+ message["pos"] = point;
+ queryPassed( "OK", "", BT(message) );
+}
+
+/*!
+ Simulates a mouse / touchscreen release at the center of the widget
+ specified by \a queryPath.
+
+ Example:
+ \code
+ // Release mouse over "Edit" button
+ mouseRelease("Edit");
+ \endcode
+
+ \sa {Query Paths}, {Mouse / Touchscreen Simulation}
+*/
+void QSystemTest::mouseRelease( const QString &queryPath, QFlags<Qt::MouseButton> buttons )
+{
+ if (QSystemTest::runAsManualTest()) {
+ manualTest( QString("%1 release the mouse on %2")
+ .arg(mouseButtons(buttons)).arg(quote(queryPath)));
+ return;
+ }
+
+ if (m_strict_mode) QFAIL( "ERROR: mouseRelease is not allowed in strict mode" );
+
+ QTestMessage message("mouseRelease");
+ message["buttons"] = (int)buttons;
+ queryPassed( "OK", "", BT(message), queryPath );
+}
+
+/*!
+ Simulates \a value being entered into the widget specified by \a queryPath.
+
+ Some widgets go into an editing mode when entering text and need to be taken
+ out of the editing mode by e.g. a Qt::Key_Select or by navigating to
+ another field in the dialog. By default, enter() will automatically take whatever
+ action is necessary to commit the text and leave edit mode. Set \a mode to NoCommit to
+ override this.
+
+ Example:
+ \code
+ // Enter my job in "Occupation" field
+ enter( "Dog Walker", "Occupation" );
+
+ // Enter date of birth in "Birthday" field
+ enter( new Date(1985, 11, 10), "Birthday" );
+
+ // Enter start time in "Start" field
+ enter( new QTime(11, 30), "Start" );
+ \endcode
+
+ \sa {Query Paths}, {Keypad Simulation}
+*/
+void QSystemTest::enter( const QVariant &value, const QString &queryPath, EnterMode mode )
+{
+ if (QSystemTest::runAsManualTest()) {
+ QString type = value.typeName();
+ if (type == "QDate") type = "date";
+ else if (type == "QTime" ) type = "time";
+ else if (type == "QString" ) type = "string";
+
+ manualTest( QString("enter %1 %2 in field %3")
+ .arg(type)
+ .arg(quote(value.toString()))
+ .arg(quote(queryPath)));
+ return;
+ }
+
+ if (value.isNull()) return;
+ QTestMessage message("enter");
+ message["value"] = value;
+
+ switch (mode)
+ {
+ case NoCommit: message["mode"] = "NoCommit"; break;
+ default: message["mode"] = "Commit"; break;
+ }
+
+ queryPassed( "OK", "", BT(message), queryPath );
+}
+
+/*!
+ Selects the \a item from the application/widget specified by \a queryPath.
+ This can be used to, e.g., select a certain item from a list widget or combo box.
+ select() works with widgets which are conceptually a list, including
+ list views, combo boxes and menus.
+
+ When used with a list widget, the specified item is navigated to and no further
+ action is taken.
+
+ When used with a combo box, the drop-down list is opened,
+ the item is selected, and the drop-down list is closed again.
+
+ Items in submenus are denoted using '/' delimiters (e.g., "View/All" means
+ navigate to the "View" submenu, then the "All" item). Menu items which
+ have a '/' in their name can be escaped using '\' (e.g. "Add\\/Remove Words").
+
+ Example:
+ \code
+ // Select "Female" from "Gender" field
+ select("Female", "Gender");
+ \endcode
+
+ \sa {Query Paths}, {Keypad Simulation}
+*/
+void QSystemTest::select( const QString &item, const QString &queryPath )
+{
+ if (m_run_as_manual_test) {
+ manualTest( "select " + quote(item) + " from " + quote(queryPath));
+ return;
+ }
+
+ if (item.isNull()) return;
+ QTestMessage message("select");
+ message["text"] = item;
+ queryPassed("OK", "", BT(message), queryPath);
+
+ /* FIXME: this wait should be able to be put in the test widgets.
+ * Currently it can't because of the new bop problem (bug 194361).
+ */
+ wait((m_demo_mode) ? 1500 : 150);
+}
+
+/*!
+ Selects the item with \a index from an indexed widget (such as a QAbstractItemView)
+ specified by \a queryPath.
+
+ This function is lower level than using select(), but is more appropriate when there
+ are large numbers of items in the view, or the view contains unnamed or ambiguously
+ named items.
+
+ The \a index is specified as a list of row and column values.
+
+ Example:
+ \code
+ // Select item (row 2, column 3) from table view
+ select( [2, 3], table );
+ \endcode
+
+ The index may be hierarchical, in which case the row and column values should be
+ specified as [row, column, parentRow, parentColumn, ...].
+
+ \sa select(), selectedIndex()
+*/
+void QSystemTest::selectIndex( const QVariantList &index, const QString &queryPath )
+{
+ if (m_run_as_manual_test) {
+ QString index_str = "[";
+ for (int i=0; i<index.count(); i++) {
+ index_str += index[i].toString() + ", ";
+ }
+ index_str = index_str.left(index_str.length()-2);
+ index_str += "]";
+
+ QString example_str = "item";
+ if (index.count() >= 2) {
+ example_str = "<row, column";
+ if (index.count() == 3) example_str += ", ...";
+ if (index.count() >= 4) example_str += ", parentRow, parentColumn";
+ if (index.count() == 5) example_str += ", ...";
+ if (index.count() >= 6) example_str += ", ...";
+ example_str += ">";
+ }
+ manualTest( "select " + example_str + " " + index_str + " from list " + quote(queryPath) );
+ return;
+ }
+
+ QTestMessage message("selectIndex");
+ message["index"] = index;
+ queryPassed("OK", "", BT(message), queryPath);
+
+ /* FIXME: this wait should be able to be put in the test widgets.
+ * Currently it can't because of the new bop problem (bug 194361).
+ */
+ wait((m_demo_mode) ? 1500 : 150);
+}
+
+
+/*!
+ Returns the currently selected index of an indexed widget specified by \a queryPath.
+ The index is returned as an array to QtScript, in the format required for selectIndex().
+
+ \sa selectIndex()
+*/
+QVariantList QSystemTest::getSelectedIndex( const QString &queryPath )
+{
+ if (m_run_as_manual_test) {
+ manualTestData( "the contents of list " + quote(queryPath) );
+ return QVariantList();
+ }
+
+ QVariantList ret;
+ return queryWithReturn(ret, "getSelectedIndex", queryPath);
+}
+
+/*!
+ Activate the widget specified by \a queryPath.
+ This is typically used to activate button widgets.
+
+ \sa {Query Paths}
+*/
+void QSystemTest::activate( const QString &queryPath )
+{
+ if (m_run_as_manual_test) {
+ manualTest( "activate " + quote(queryPath) );
+ return;
+ }
+
+ QTestMessage message("activate");
+ queryPassed("OK", "", BT(message), queryPath);
+
+ /* FIXME: this wait should be able to be put in the test widgets.
+ * Currently it can't because of the new bop problem (bug 194361).
+ */
+ wait((m_demo_mode) ? 1500 : 150);
+}
+
+/*!
+ Make sure the specified \a item is visible in the widget specified by \a queryPath.
+ The action taken depends on the widget, but may for example involve moving the
+ widget's scrollbars.
+
+ \sa {Query Paths}
+*/
+void QSystemTest::ensureVisible( const QString &item, const QString &queryPath )
+{
+ if (m_run_as_manual_test) {
+ manualTest( "verify item " + quote(item) + " is visible in " + quote(queryPath) );
+ return;
+ }
+
+ if (item.isNull()) return;
+ QTestMessage message("ensureVisible");
+ message["item"] = item;
+ queryPassed("OK", "", BT(message), queryPath);
+
+ /* FIXME: this wait should be able to be put in the test widgets.
+ * Currently it can't because of the new bop problem (bug 194361).
+ */
+ wait((m_demo_mode) ? 1500 : 150);
+}
+
+/*!
+ Start the specified \a application.
+
+ \a application is the combined path and arguments of a program to launch.
+ The application must connect to the test framework within \a timeout ms or
+ the test fails.
+
+ \a flags specifies additional behaviour.
+
+ \sa {Application Management}
+*/
+void QSystemTest::startApplication( const QString &application, const QStringList &arguments, int timeout, StartApplicationFlags flags )
+{
+ if (m_run_as_manual_test) {
+ manualTest( "start application '"
+ + application
+ + "' "
+ + arguments.join(" ") );
+ return;
+ }
+
+ QString app = application;
+ QStringList args = arguments;
+
+ if (!runsOnDevice()) {
+ if (!device_controller)
+ device_controller = new Qt4Test::TestController(Qt4Test::TestController::Desktop);
+
+ args = processEnvironment(arguments);
+ app = processEnvironment(application);
+ app = which(app);
+ if (app.isEmpty()) {
+ fail(QString("Application '%1' not found in PATH (%2)").arg(application).arg(PATH()));
+ return;
+ }
+
+ } else {
+ if (!device_controller)
+ device_controller = new Qt4Test::TestController(Qt4Test::TestController::Maemo, &ssh_param);
+ }
+
+ if (device_controller)
+ device_controller->killApplications();
+
+ // setup configuration for target, used by configTarget() later
+ QByteArray defTargetID = qgetenv("QTUITEST_TARGETID");
+ if (!defTargetID.isEmpty())
+ m_targetID = QString(defTargetID);
+
+ QString reply;
+ if (device_controller) {
+ device_controller->startApplication(app, args, true, m_env, reply);
+#ifdef QTCREATOR_QTEST
+ testOutputPane()->append(reply);
+#endif
+ }
+
+ // Give it a little time for the slave to come up.
+ wait(100);
+
+ if (!connectToAut(timeout)) {
+ device_controller->killApplications();
+ if (reply.isEmpty())
+ fail(QString("Could not connect to remote process '%1'").arg(app).arg(reply));
+ else
+ fail(QString("Could not connect to remote process '%1'\nProcess output is '%2'").arg(app).arg(reply));
+ }
+ configTarget();
+}
+
+/*!
+ Terminate any application(s) started by the test system. Normally, this won't be necessary
+ as startApplication will do this for you.
+
+ \sa {startApplication}
+*/
+void QSystemTest::killApplication()
+{
+ if (device_controller)
+ device_controller->killApplications();
+}
+
+/*!
+ Returns true if the widget specified by \a queryPath exists and is currently visible
+ to the user.
+
+ The widget is considered to be visible to the user if QWidget::visibleRegion() returns
+ a non-empty region. Thus, isVisible() will return true if even a single pixel of the
+ widget is unobscured.
+
+ \sa {Query Paths}, {Querying Objects}
+*/
+bool QSystemTest::isVisible( const QString &queryPath )
+{
+ if (m_run_as_manual_test) {
+ manualTestData( "'" + queryPath + "' is visible" );
+ return true;
+ }
+
+ bool ret = false;
+ return queryWithReturn(ret, "isVisible", queryPath);
+}
+
+/*!
+ The function returns whether the widget specified by \a queryPath is enabled.
+
+ Example:
+ \code
+ // Verify the AM/PM field is disabled when using 24 hour format
+ select("24 hour", "Time format");
+ verify( !isEnabled("AM-PM"), "AM-PM field still enabled." );
+ \endcode
+
+ \sa {Query Paths}, {Querying Objects}
+*/
+bool QSystemTest::isEnabled( const QString &queryPath )
+{
+ return getProperty( queryPath, "enabled" ).toBool();
+}
+
+/*!
+ \internal
+ Returns a string of all the visible widgets that are children of the object specified by \a queryPath.
+ If only the application part of \a queryPath is defined, then all visible widgets for that application
+ are listed. The string is organized in a tree type hierarchy showing the relationship between
+ widgets.
+
+ This function is intended to help with the development of testcases.
+
+ \sa {Query Paths}
+*/
+QString QSystemTest::activeWidgetInfo()
+{
+ QString ret;
+ return queryWithReturn(ret, "activeWidgetInfo", "");
+}
+
+/*!
+ Returns the checked state of the checkbox-like widget specified by \a queryPath.
+ Checkbox-like widgets include QCheckBox and anything inheriting QAbstractButton.
+
+ \sa {Query Paths}, {Querying Objects}, setChecked(), checkState()
+*/
+bool QSystemTest::isChecked( const QString &queryPath )
+{
+ bool ret = false;
+ return queryWithReturn(ret, "isChecked", queryPath);
+}
+
+/*!
+ Returns the checked state of \a item in the widget specified by \a queryPath.
+
+ \sa {Query Paths}, {Querying Objects}, setChecked(), checkState()
+*/
+bool QSystemTest::isChecked( const QString &item, const QString &queryPath )
+{
+ if (item.isNull()) return false;
+ QTestMessage message("isChecked");
+ QTestMessage reply;
+ message["item"] = item;
+ if (!queryPassed("OK", "", BT(message), queryPath, &reply)) return false;
+ return reply["isChecked"].toBool();
+}
+
+/*!
+ Based on the value of \a doCheck checks or un-checks a checkbox-like widget specified by \a queryPath.
+ Checkbox-like widgets include QCheckBox and anything inheriting QAbstractButton.
+
+ \sa {Query Paths}, {Querying Objects}, isChecked(), setCheckState()
+*/
+void QSystemTest::setChecked( bool doCheck, const QString &queryPath)
+{
+ QTestMessage message("setChecked");
+ message["doCheck"] = doCheck;
+ queryPassed("OK", "", BT(message), queryPath);
+}
+
+/*!
+ Based on the value of \a doCheck checks or un-checks \a item in the widget specified by \a queryPath.
+
+ \sa {Query Paths}, {Querying Objects}, isChecked(), setCheckState()
+*/
+void QSystemTest::setChecked( bool doCheck, const QString &item, const QString &queryPath)
+{
+ QTestMessage message("setChecked");
+ message["doCheck"] = doCheck;
+ message["item"] = item;
+ queryPassed("OK", "", BT(message), queryPath);
+}
+
+/*!
+ Returns the checked state of the checkbox-like widget specified by \a queryPath.
+ Checkbox-like widgets include QCheckBox and anything inheriting QAbstractButton.
+
+ \sa {Query Paths}, {Querying Objects}, isChecked(), setCheckState()
+*/
+int QSystemTest::checkState( const QString &queryPath )
+{
+ int ret = Qt::Unchecked;
+ return queryWithReturn(ret, "checkState", queryPath);
+}
+
+/*!
+ Sets the check state of a checkbox-like widget specified by \a queryPath to \a state.
+ Checkbox-like widgets include QCheckBox and anything inheriting QAbstractButton.
+
+ Example:
+ \code
+ // Set tri-state button to partially checked state
+ setCheckState(Qt.PartiallyChecked, "Tri-state button");
+ \endcode
+
+ \sa {Query Paths}, {Querying Objects}, checkState(), setChecked()
+*/
+void QSystemTest::setCheckState( int state, const QString &queryPath)
+{
+ QTestMessage message("setCheckState");
+ message["state"] = state;
+ queryPassed("OK", "", BT(message), queryPath);
+}
+
+/*!
+ Returns the signature of the widget that is associated with \a labelText.
+
+ If \a offset is a number != 0 the query will return the n'th next or previous widget (excluding labels). The widgets are ordered from
+ top to bottom, left to right. A positive offset indicates a widget to the right or below, a negative offset indicates a widget to
+ the left or above the label.
+
+ This function can be used for situations where a field doesn't have a label associated with it.
+
+ \sa {Query Paths}, {Querying Objects}, {QObject::inherits()}
+*/
+QString QSystemTest::signature( const QString &labelText, int offset )
+{
+ QTestMessage message("widget");
+ message["offset"] = offset;
+ QTestMessage reply;
+ if (!queryPassed("OK", "", BT(message), labelText, &reply)) return "";
+ return reply["widget"].toString();
+}
+
+/*!
+ List the contents of directory \a dir on the test system, applying \a filters to the listing.
+ If \a dir contains environment variables, they will be expanded on the test system.
+
+ The returned listing will be relative to \a dir.
+
+ Example:
+ \code
+ // Delete entire contents of Documents directory on the test system
+ var list = getDirectoryEntries( documentsPath(), QDir.AllEntries);
+ for (var i = 0; i < list.length; ++i) {
+ deletePath( documentsPath() + list[i]);
+ }
+ \endcode
+
+ \sa QDir::entryList(), {File Management}
+*/
+QStringList QSystemTest::getDirectoryEntries( const QString &dir, QDir::Filters filters )
+{
+ QTestMessage message("getDirectoryEntries");
+ message["path"] = dir;
+ message["filters"] = (int)filters;
+
+ QStringList out;
+ QTestMessage reply;
+ if (!queryPassed( "OK", "", BT(message), "", &reply)) return out;
+ if (reply["getDirectoryEntries"].isNull()) {
+ setQueryError("Got no data in response to getDirectoryEntries");
+ return out;
+ }
+ out = reply["getDirectoryEntries"].toStringList();
+ return out;
+}
+
+/*!
+ Returns the current date and time of the test system.
+*/
+QDateTime QSystemTest::getDateTime()
+{
+ QDateTime ret;
+ return queryWithReturn(ret, "systemTime", "");
+}
+
+/*!
+ Returns true if the test is running on an actual device, and false if it is running locally.
+*/
+bool QSystemTest::runsOnDevice()
+{
+ return ssh_param.host != "127.0.0.1";
+}
+
+/*!
+ Wait for \a msecs milliseconds, while still processing events from the event loop.
+*/
+void QSystemTest::wait(int msecs)
+{
+ QTime t;
+ t.start();
+ while (t.elapsed() < msecs) {
+ qApp->processEvents();
+ }
+}
+
+/*!
+ Returns the currently set Visual Response Time. This time is used in QtUiTest to decide whether the User
+ Interface is responsive to user events. For instance, after selecting "New Event" from the options menu a user
+ expects a dialog in which a new event can be entered. If the Application Under Test does not respond in some
+ visible way within the visual response time, the test will fail.
+
+ By default the visual response time is set to 4 seconds, i.e. any UI that doesn't respond to events within this time
+ is considered at fault.
+
+ The visibleResponseTime is also used as the default value for some queries such as waitForTitle().
+
+ \sa setVisibleResponseTime(), waitForTitle()
+*/
+int QSystemTest::visibleResponseTime()
+{
+ return m_visible_response_time;
+}
+
+/*!
+ Sets the Visual Response Time to \a time.
+
+ \sa visibleResponseTime(), waitForTitle()
+*/
+void QSystemTest::setVisibleResponseTime( int time )
+{
+ if (m_visible_response_time != time) {
+ qLog(QtUitest) << "Need to set a new visual response time" ;
+ }
+ m_visible_response_time = time;
+}
+
+/*!
+ Take a full screenshot and save it as \a name.
+ The screenshot will be placed in the test data directory in PNG format,
+ and will automatically have .png appended to the name.
+
+ This function is intended to be used as a simple way to automate the
+ gathering of screenshots, i.e. to be used in documentation and such.
+
+ If a \a queryPath is specified the snapshot will be limited to the Widget
+ that is identified by the queryPath.
+
+ \sa verifyImage()
+*/
+void QSystemTest::saveScreen(const QString &name, const QString &queryPath)
+{
+ QString cdp = currentDataPath();
+ if (!QDir("/").exists(cdp) && !QDir("/").mkpath(cdp)) QFAIL(QString("Path '' didn't exist and I couldn't create it").arg(cdp));
+ QImage img;
+ img = grabImage(queryPath);
+ if (queryFailed()) return;
+ if (img.isNull()) QFAIL( "AUT returned a null screenshot." );
+ if (!img.save(cdp + "/" + name + ".png", "PNG")) QFAIL(QString("Couldn't save image '%1' to '%2'").arg(name).arg(cdp) );
+}
+
+
+/*!
+ \fn QSystemTest::expectMessageBox(String title, String text, String option, Number timeout)
+ Denotes the start of a block of code which, immediately after or during execution, should
+ cause a message box to pop up with the given \a title and \a text. When the message box
+ appears, the given menu \a option will be chosen from the message box softmenu bar (if one exists).
+
+ If the message box hasn't appeared by the end of the block of code, the test will
+ wait until the \a timeout expires. If it still doesn't appear, the current test fails.
+
+ If a message box appears which hasn't been marked as expected, the current test fails.
+
+ Example:
+ \code
+ // Delete a contact - select "Yes" on the popped-up message box
+ // If the message box doesn't pop up, the test fails.
+ expectMessageBox("Contacts", "Are you sure you want to delete: " + contact_name + "?", "Yes") {
+ select("Delete contact", optionsMenu());
+ }
+ \endcode
+*/
+
+/*!
+ Indicate to the test framework if the application under test is expected to close.
+
+ If \a value is true, the test framework will not report a failure when it loses its connection to the application.
+ If \a value is false, unexpected application terminations will result in a test failure.
+
+ Example:
+ \code
+ expectApplicationClose( true );
+ select( "Close" ); // Selecting this causes the current application to close
+ expectApplicationClose( false ); // Resume normal checking
+ \endcode
+*/
+void QSystemTest::expectApplicationClose( bool value )
+{
+ m_expect_app_close = value;
+}
+
+/*!
+ \internal
+ Processes the command line parameters.
+*/
+void QSystemTest::processCommandLine( QStringList &args )
+{
+ QMutableStringListIterator it(args);
+
+ while (it.hasNext()) {
+ QString arg = it.next();
+ if (!arg.compare("-remote", Qt::CaseInsensitive)) {
+ it.remove();
+ if (!it.hasNext()) qFatal("Expected a value after %s", qPrintable(arg));
+ QString host_port = it.next();
+ it.remove();
+ QStringList host_port_parts = host_port.split(":");
+
+ bool ok = (host_port_parts.count() == 2);
+ int port = -1;
+ QString host;
+ if (ok) port = host_port_parts[1].toInt(&ok);
+ if (ok) host = host_port_parts[0];
+ ok = ok && port > 0 && port < 65536 && !host.isEmpty();
+
+ if (!ok)
+ qFatal("'%s' is not a valid host:port argument", qPrintable(host_port));
+
+ QTestIDE::instance()->openRemote( host, port );
+ connect(QTestIDE::instance(), SIGNAL(abort()), this, SLOT(abortTest()));
+ } else if ( !arg.compare("-autip", Qt::CaseInsensitive) || !arg.compare("-authost", Qt::CaseInsensitive) ) {
+ it.remove();
+ if (!it.hasNext()) qFatal("Expected a value after %s", qPrintable(arg));
+ ssh_param.host = it.next();
+ it.remove();
+ ssh_param.timeout = 20000;
+ } else if ( !arg.compare("-username", Qt::CaseInsensitive) ) {
+ it.remove();
+ if (!it.hasNext()) qFatal("Expected a value after %s", qPrintable(arg));
+ ssh_param.uname = it.next();
+ it.remove();
+ } else if ( !arg.compare("-pwd", Qt::CaseInsensitive) ) {
+ it.remove();
+ if (!it.hasNext()) qFatal("Expected a value after %s", qPrintable(arg));
+ ssh_param.pwd = it.next();
+ it.remove();
+ ssh_param.authType = Core::SshConnectionParameters::AuthByPwd;
+ ssh_param.privateKeyFile = "";
+ } else if ( !arg.compare("-private-key", Qt::CaseInsensitive) ) {
+ it.remove();
+ if (!it.hasNext()) qFatal("Expected a value after %s", qPrintable(arg));
+ ssh_param.privateKeyFile = it.next();
+ it.remove();
+ ssh_param.pwd = "";
+ ssh_param.authType = Core::SshConnectionParameters::AuthByKey;
+ } else if ( !arg.compare("-autsshport", Qt::CaseInsensitive) ) {
+ it.remove();
+ if (!it.hasNext()) qFatal("Expected a value after %s", qPrintable(arg));
+ bool ok;
+ ssh_param.port = it.next().toUShort( &ok );
+ if (!ok)
+ qFatal("%s is not a valid port specifier", qPrintable(it.value()));
+ it.remove();
+ } else if ( !arg.compare("-autport", Qt::CaseInsensitive) ) {
+ it.remove();
+ if (!it.hasNext()) qFatal("Expected a value after %s", qPrintable(arg));
+ bool ok;
+ m_aut_port = it.next().toUShort( &ok );
+ if (!ok)
+ qFatal("%s is not a valid port specifier", qPrintable(it.value()));
+ it.remove();
+ } else if ( !arg.compare("-keepaut", Qt::CaseInsensitive) ) {
+ it.remove();
+ m_keep_aut = true;
+ } else if ( !arg.compare("-silentaut", Qt::CaseInsensitive) ) {
+ it.remove();
+ m_silent_aut = true;
+ } else if ( !arg.compare("-noaut", Qt::CaseInsensitive) ) {
+ it.remove();
+ m_no_aut = true;
+ } else if ( !arg.compare("-auto", Qt::CaseInsensitive) ) {
+ it.remove();
+ m_auto_mode = true;
+ } else if ( !arg.compare("-force-manual", Qt::CaseInsensitive) ) {
+ it.remove();
+ m_run_as_manual_test = true;
+ } else if ( !arg.compare("-env", Qt::CaseInsensitive) ) {
+ it.remove();
+ if (!it.hasNext()) qFatal("Expected a value after %s", qPrintable(arg));
+ m_env << it.next();
+ it.remove();
+ } else if ( !arg.compare("-targetid", Qt::CaseInsensitive) ) {
+ it.remove();
+ if (!it.hasNext()) qFatal("Expected a value after %s", qPrintable(arg));
+ m_targetID = it.next();
+ it.remove();
+ } else if ( !arg.compare("-demomode", Qt::CaseInsensitive) ) {
+ it.remove();
+ m_demo_mode = true;
+ } else if ( !arg.compare("-verbose-pref", Qt::CaseInsensitive) ) {
+ it.remove();
+ m_verbose_perf = true;
+ } else if ( !arg.compare("-v", Qt::CaseInsensitive) ) {
+ it.remove();
+ m_verbose = true;
+ }
+ }
+ QAbstractTest::processCommandLine(args);
+
+ if (m_auto_mode && learnMode() != LearnNone) {
+ qWarning("Can't learn in auto mode; learn options ignored.");
+ }
+ if (!m_auto_mode && learnMode() == LearnNone) {
+ setLearnMode(LearnNew);
+ }
+}
+
+/*!
+ \internal
+ Starts event recording. While event recording is busy a dialog will be visible that shows all recorded events and also
+ enables the user to stop the recording session. \a file and \a line specifies the location in a source file where event
+ recording commenced.
+
+ If \a manualSteps are specified the dialog will have an additional field showing all the manual steps.
+
+ Don't use this, use prompt() instead.
+*/
+bool QSystemTest::recordEvents( const QString &manualSteps, bool gui )
+{
+ if (m_auto_mode && gui) {
+ skip("Can't record events in auto mode.", SkipSingle );
+ return false;
+ }
+
+ m_recorded_events_edit = 0;
+ m_recorded_code = QString();
+ if (!queryPassed( "OK", "", QTestMessage("startEventRecording"))) return false;
+ if (gui) {
+ if (QTestIDE::instance()->isConnected()) {
+ QTestIDE::instance()->eventRecordingStarted(currentFile(), currentLine(), manualSteps);
+ m_recording_events = true;
+ while (!QTestIDE::instance()->mustStopEventRecording()) {
+ wait( 50 );
+ }
+
+ m_recording_events = false;
+ if (QTestIDE::instance()->eventRecordingAborted()) {
+ skip("Event recording aborted.", SkipSingle );
+ }
+ } else {
+ QDialog recordWindow;
+ Ui::RecordDialog ui;
+ ui.setupUi(&recordWindow);
+
+ if (!manualSteps.isEmpty()) {
+ ui.steps_view->setPlainText( manualSteps );
+ } else {
+ ui.steps_view->hide();
+ ui.steps_label->hide();
+ }
+
+ m_recorded_events_edit = ui.codeEdit;
+
+ connect( ui.abort_button, SIGNAL(clicked()), &recordWindow, SLOT(close()) );
+ connect( ui.abort_button, SIGNAL(clicked()), this, SLOT(abortPrompt()) );
+ abort_prompt = false;
+
+ m_recording_events = true;
+ recordWindow.exec();
+ m_recording_events = false;
+ if (abort_prompt) {
+ return false;
+ }
+
+ }
+ return queryPassed( "OK", "", QTestMessage("stopEventRecording"));
+ }
+ return true;
+}
+
+/*!
+ \internal
+ Stops recording events, returning the generated code from event recording.
+ Used internally for testing event recording.
+*/
+QString QSystemTest::stopRecordingEvents()
+{
+ queryPassed( "OK", "", QTestMessage("stopEventRecording"));
+ return m_recorded_code;
+}
+
+/*!
+ Displays a dialog with the \a manualSteps and Pass/Fail/Record buttons, then waits for a response from
+ the user.
+ \list
+ \o If Pass is clicked, the test will continue.
+ \o If Fail is clicked, a failure will be generated and the current testcase will abort.
+ \o If Record is clicked, event recording will start and recorded events can be appended to the testcase
+ once recording is finished.
+ \endlist
+
+ This function is intended to be used for verification of testcases that cannot be fully automated.
+ Testcases containing this function will be skipped if the test is run with the \c -auto option (i.e.
+ when tests are executed in a CI system).
+
+ Example1:
+ \code
+ prompt( "Did something totally amazing just happen?" );
+ \endcode
+
+ When the test is run with \c -force-manual the \a manualSteps are accumulated and the actual prompt dialog is
+ not shown until the end of the test function, i.e.
+ \list
+ \o all automated commands are converted into documented manual steps
+ \o all \a manualSteps are added to this list
+ \o and the complete set of steps is shown in one go
+ \endlist
+
+ For example:
+ \code
+ startApplication("foobar");
+ prompt( "Do something that is difficult to automate" );
+ compare( currentTitle(), "blah" );
+ \endcode
+
+ When the above example is executed in normal (automated) test execution mode 'startApplication' will
+ start application foobar, then show a dialog with the text "Do something that is difficult to automate"
+ and wait until the user clicks on Pass or Fail. If the user clicked on Fail the test will be terminated
+ and if the user clicked on Pass the execution will continue with the next step, i.e. an automatic
+ comparison of the current title against the constant 'blah'.
+
+ If however the test is executed with \c -force-manual all text will be converted into manual steps and
+ nothing will be executed automatically, but instead one prompt dialog will be shown at the end of the
+ test function with the following steps:
+ \code
+ 1. start application 'foobar'
+ 2. Do something that is difficult to automate
+ 3. Verify that the current title is equal to 'blah'
+ \endcode
+
+ \sa manualTest
+*/
+void QSystemTest::prompt( const QString &manualSteps )
+{
+ if (manualSteps.contains(QRegExp("<.*>.*</.*>")))
+ {
+ // Contains HTML, don't assume it's a series of steps
+ showPromptDialog(manualSteps);
+ return;
+ }
+
+ QStringList list = manualSteps.split("\n", QString::SkipEmptyParts);
+ foreach( QString cmd, list)
+ manualTest(cmd);
+
+ if (runAsManualTest()) {
+ // just add the manualSteps to whatever we already have, and show prompt at the end.
+ return;
+ }
+
+ if (m_auto_mode) {
+ skip("Manual test skipped", SkipSingle);
+ return;
+ }
+
+ showPromptDialog();
+}
+
+/*! \internal */
+void QSystemTest::abortPrompt()
+{
+ abort_prompt = true;
+}
+
+/*!
+ \internal
+ This function is called when the test IDE wants to abort the current test.
+*/
+void QSystemTest::abortTest()
+{
+#ifndef Q_OS_SYMBIAN
+ ::raise(SIGINT);
+#endif
+}
+
+//#ifndef QTCREATOR_QTEST
+/*!
+ \internal
+ Print any special usage information which should be shown when test is launched
+ with -help.
+*/
+void QSystemTest::printUsage() const
+{
+ QAbstractTest::printUsage();
+ qWarning(
+ " System test options:\n"
+ " -authost <host> : Specify the IP address or host name of the machine on which the AUT is running.\n"
+ " By default, the system test will connect to %s. \n"
+ " -autport <port> : Use QTUITEST_DEFAULT_OPTIONS environment variable to specify autport option and value.\n"
+ " Specify the port on which the AUT is listening for a system test connection.\n"
+ " Defaults to %d. \n"
+ " -autsshport <port>: Specify the port to use when ssh'ing to authost.\n"
+ " Defaults to %d. \n"
+ " -username : Specify the user name when ssh'ing to authost.\n"
+ " -pwd : Specify the password when ssh'ing to authost.\n"
+ " -private-key : Specify the ssh private key when ssh'ing to authost.\n"
+ " -keepaut : Leave the AUT running after the system test finishes.\n"
+ " By default, if the system test launches the AUT, it will kill the AUT when testing\n"
+ " completes.\n"
+ " -silentaut : Hide the output of the AUT. By default, if the system test launches the AUT, the AUT's\n"
+ " standard output and standard error will be mixed with that of the test itself.\n"
+ "\n"
+ " -auto : Run in fully-automated mode. Any tests which require the presence of a manual\n"
+ " tester will be skipped.\n"
+ "\n"
+ " -force-manual : Runs all system tests as manual tests. Test cases that contain automated step are translated\n"
+ " into a set of documented steps.\n"
+ "\n"
+ " -verbose-perf : Output all performance messages from the AUT to the console. This may be used to run\n"
+ " a post-processing script on performance logs.\n"
+ "\n"
+ " -remote <host:port> : Specify the host and port of a QtUiTest-compatible test IDE, used for manual\n"
+ " verification steps. The default is to not use an IDE.\n"
+ " -env VAR=VALUE : Specify additional environment variables to be applied to tested \n"
+ " applications. For example, pass -env DISPLAY=:123 to run tested \n"
+ " applications on a different X server.\n"
+ " -targetID : Specify the target identifier for system under test, defaults to \'default\' or $QTUITEST_TARGETID if set\n"
+ , DEFAULT_AUT_HOST, DEFAULT_AUTSSH_PORT, DEFAULT_AUT_PORT
+ );
+}
+//#endif
+
+#ifndef Q_QDOC
+/*
+ Fail returning a bool is for internal use only.
+ Hide from documentation.
+*/
+/*!
+ \internal
+ Cause a test failure with the given \a message.
+ Returns true if the test should continue (e.g., because the failure was expected), false otherwise.
+*/
+bool QSystemTest::fail(QString const &message)
+{
+ if (m_expect_app_close &&
+ (message.startsWith("ERROR: Connection lost") ||
+ message.startsWith("ERROR: no data in reply to") ||
+ message.startsWith("reply was missing return value") ||
+ message.startsWith("ERROR_NO_CONNECTION")) ) {
+ return true;
+ }
+
+ static bool saving_screen = false;
+ static bool saving_info = false;
+ static QString saving_screen_failed;
+ static QString saving_info_failed;
+ /* Prevent recursive failure on saving screen/widget info */
+ if (saving_screen) {
+ saving_screen_failed = "Failed to save failure screenshot: " + message;
+ return true;
+ }
+ if (saving_info) {
+ saving_info_failed = "Failed to save widget info on failure: " + message;
+ return true;
+ }
+
+ bool ret = QTest::compare_helper( false, qPrintable(message), qPrintable(currentFile()), currentLine() );
+ if (!ret) {
+ saving_screen = true;
+ saving_screen_failed = QString();
+
+ QString config = configurationIdentifier();
+ saveScreen("failure_" + config);
+
+ saving_screen = false;
+
+ if (saving_screen_failed.isEmpty()) {
+ // If we saved it, let the IDE know.
+ QFileInfo info(currentDataPath() + "/failure_" + config + ".png");
+ if (info.exists()) {
+ QTestIDE::instance()->failureScreenshot(info.canonicalFilePath(), currentFile(), currentLine(), QTest::currentTestFunction());
+ }
+ }
+
+ saving_info = true;
+ saving_info_failed = QString();
+
+ QString info = activeWidgetInfo();
+
+ saving_info = false;
+ if (!info.isEmpty()) {
+ QFile f(currentDataPath() + "/failure_" + config + ".txt");
+ if (!f.exists() && f.open(QIODevice::WriteOnly)) {
+ QTextStream(&f)
+ << message << "\n"
+ << "Location: " << currentFile() << ":" << currentLine() << "\n"
+ << (saving_screen_failed.isEmpty()
+ ? "Also see failure_" + config + ".png for a screenshot."
+ : saving_screen_failed)
+ << "\n"
+ << (saving_info_failed.isEmpty()
+ ? info
+ : saving_info_failed)
+ << "\n";
+ }
+ }
+ }
+ return ret;
+}
+#endif
+
+/*!
+
+ Cause a test to skip with the given \a message and \a mode.
+
+*/
+void QSystemTest::skip(const QString& message, SkipMode mode)
+{
+ m_skip_current_function = true;
+
+ // If there is no test data for this function, always use
+ // SkipAll to avoid reporting both SKIP and PASS result
+ if (currentDataTag().isEmpty())
+ mode = SkipAll;
+
+ QTest::qSkip(qPrintable(message), QTest::SkipMode(mode), qPrintable(currentFile()), currentLine());
+}
+
+/*!
+ \internal
+ Returns true if the last executed query has failed.
+ If \a message is not null, a failure message is returned in \a message.
+ If \a sent is not null, the message sent from the system test which
+ caused the failure is returned in \a sent.
+*/
+bool QSystemTest::queryFailed( QTestMessage *message, QTestMessage *sent )
+{
+ if (message != 0)
+ *message = m_error_msg;
+ if (sent != 0)
+ *sent = m_error_msg_sent;
+ return query_failed;
+}
+
+/*!
+ \internal
+ Informs the testframework that error situations need to be reported as warnings.
+*/
+void QSystemTest::enableQueryWarnings( bool enable, const QString &file, int line )
+{
+ setLocation( file, line );
+
+ if (!enable) {
+ m_error_msg = QTestMessage();
+ query_failed = false;
+ }
+ query_warning_mode = enable;
+}
+
+/*!
+ \internal
+ Saves the \a file and \a line information for internal usage. Whenever an error situation occurs this data is appended to the error message to improve traceability of the error.
+
+ It is usually not necessery to call this function directly.
+*/
+void QSystemTest::setLocation( const QString &file, int line )
+{
+ m_loc_fname = file;
+ m_loc_line = line;
+}
+
+/*!
+ \internal
+ Returns the current filename, i.e. the file that was being executed when the error situation occurred.
+*/
+QString QSystemTest::currentFile()
+{
+ return m_loc_fname;
+}
+
+/*!
+ \internal
+ Returns the current line number, i.e. the line number that presumably caused the error situation.
+*/
+int QSystemTest::currentLine()
+{
+ return m_loc_line;
+}
+
+/*!
+ \internal
+ Saves the error string \a errString for future processing. If warning mode is enabled the error is written directly to std out as a warning.
+ Base implementation returns false; subclasses may return true to indicate that the error is not considered "fatal".
+*/
+bool QSystemTest::setQueryError( const QString &errString )
+{
+ query_failed = true;
+ m_error_msg["status"] = errString;
+ m_error_msg_sent = m_last_msg_sent;
+ if (query_warning_mode)
+ qWarning( errString.toLatin1() );
+ return false; // query is NOT successfull
+}
+
+/*!
+ \internal
+ Saves the error specified in the test \a message for future processing. If warning mode is enabled the error is written directly to std out as a warning.
+ Base implementation returns false; subclasses may return true to indicate that the error is not considered "fatal".
+*/
+bool QSystemTest::setQueryError( const QTestMessage &message )
+{
+ query_failed = true;
+ m_error_msg = message;
+ m_error_msg_sent = m_last_msg_sent;
+ if (query_warning_mode)
+ qWarning( QString("%1 %2").arg(message.event()).arg(message.toString()).toLatin1().constData() );
+ return false; // query is NOT successfull
+}
+
+//#ifndef QTCREATOR_QTEST
+/*!
+ \internal
+ Launch AUT and run the test.
+*/
+int QSystemTest::runTest(const QString &fname, const QStringList &args,
+ const QStringList &environment)
+{
+ // Try to launch the aut script, and if successful, start executing the test.
+ // QTest::qExec will also start the application event loop, and will not return until the tests have finished.
+
+ return QAbstractTest::runTest(fname, args, environment);
+}
+//#endif
+
+/*!
+ \internal
+ Attempts to launch the AUT (Application Under Test), and returns whether it was successful.
+ The \c -remote command line argument is appended to the script with the IP set to the address of the
+ local machine. This is to allow the test system to connect to the machine the test is running on.
+
+ Any scripts used should support this.
+*/
+bool QSystemTest::connectToAut(int timeout)
+{
+ if (!m_test_app)
+ m_test_app = new QSystemTestMaster( this );
+
+ QTime t;
+ t.start();
+ while (t.elapsed() < timeout && !isConnected()) {
+ m_test_app->connect( ssh_param.host, m_aut_port );
+ m_test_app->waitForConnected(2000);
+ }
+
+ if (!m_test_app->isConnected()) {
+ qLog(QtUitest) << qPrintable(QString("'%1' while trying to connect to test app on %2:%3. ").arg(m_test_app->errorStr()).arg(ssh_param.host).arg(m_aut_port)) ;
+ return false;
+ }
+
+ // Don't try to reconnect if connection is lost ... it's pointless
+ m_test_app->enableReconnect(false, 0);
+
+ if (m_demo_mode) setDemoMode(true);
+
+ return true;
+}
+
+/*!
+ \internal
+*/
+void QSystemTest::disconnectFromAut()
+{
+ m_test_app->disconnect();
+}
+
+/*!
+ Returns the username that is running the test on the desktop machine.
+*/
+QString QSystemTest::userName()
+{
+ QString user_name;
+
+#if defined Q_OS_TEMP
+ user_name = "WinCE";
+#elif defined Q_OS_WIN32
+ user_name = ::getenv("USERNAME");
+#elif defined Q_OS_UNIX
+ user_name = ::getenv("USER");
+ if (user_name == "")
+ user_name = ::getenv( "LOGNAME" );
+#elif defined Q_OS_MAC
+ user_name = ::getenv();
+#endif
+
+ return user_name.toLower();
+}
+
+/*!
+ Runs \a application with arguments \a args on the local machine,
+ with \a input given as standard input. The combined stderr and stdout text will be returned.
+
+ If the process fails to run or returns a non-zero exit code, the current test fails.
+*/
+QString QSystemTest::runProcess( const QString &application, const QStringList &args, const QString &input )
+{
+ QString output;
+ QProcess p;
+ p.setReadChannelMode(QProcess::MergedChannels);
+ p.setReadChannel(QProcess::StandardOutput);
+ p.start(application, args);
+ if (p.waitForStarted()) {
+ if (!input.isEmpty()) {
+ p.write( input.toLatin1() );
+ }
+ p.closeWriteChannel();
+ while (p.state() == QProcess::Running) {
+ while (p.canReadLine()) {
+ output += p.readLine();
+ }
+ wait(10); //important
+ }
+ while (p.canReadLine()) {
+ output += p.readLine();
+ }
+ if (p.exitStatus() != QProcess::NormalExit) {
+ setQueryError("Process didn't exit normally\n" + output);
+ return QString();
+ }
+ int ec = p.exitCode();
+ if (0 != ec) {
+ QString err_str = QString("Process exited with exit code: %1\n%2").arg(ec).arg(output);
+ setQueryError(err_str);
+ return QString();
+ }
+ return output;
+ }
+ setQueryError("Process didn't start");
+ return output;
+}
+
+/******************************************************************************
+* DOCUMENTATION FOR FUNCTIONS IN builtins.js
+******************************************************************************/
+
+/*!
+ Compares the \a actual string with the \a expected string and reports a fail
+ with a nicely formatted error message in case the strings are not equal.
+
+ Example:
+ \code
+ var my_variable1 = "Test";
+ var my_variable2 = "Test2";
+ compare( my_variable1, "Test" ); // passes
+ compare( my_variable2, "Test" ); // will fail the test, and test execution will stop at this line
+ \endcode
+
+ \sa verify()
+*/
+#ifdef Q_QDOC
+void QSystemTest::compare( const QString &actual, const QString &expected )
+{
+ // This code is implemented in the QtUiTestrunner bindings
+}
+#endif
+
+/*!
+ Aborts the test with a failure message if \a statement is false.
+ The failure messages returned by compare() are generally speaking better readable (more informative) and is preferred when working with strings.
+
+ Example:
+ \code
+ var my_variable1 = "Test";
+ var my_variable2 = "Test2";
+ verify( my_variable1 == "Test" ); // passes
+ verify( my_variable2 == "Test" ); // will fail the test, and test execution will stop at this line
+ \endcode
+
+ \sa compare()
+*/
+#ifdef Q_QDOC
+void QSystemTest::verify( bool statement )
+{
+ // This code is implemented in the QtUiTestrunner bindings
+}
+#endif
+
+/*!
+ \fn QSystemTest::waitFor(Number timeout, Number intervals, String message)
+
+ Denotes the start of a block of code which should be repeatedly executed
+ until it returns true. If the block of code doesn't return true within
+ \a timeout milliseconds, the current test fails with the given \a message.
+
+ \a intervals is the maximum amount of times the block of code should be
+ executed; i.e., the code will be executed every \a timeout / \a intervals
+ milliseconds.
+
+ Example:
+ \code
+ waitFor() {
+ return getList().length > 0;
+ }
+ \endcode
+*/
+
+
+/*!
+ \fn QSystemTest::verify(Boolean condition, String message)
+
+ Verifies that \a condition is true. If \a condition is not true, the
+ current test fails. If \a message is given, it will be appended to
+ the failure message.
+
+ Example:
+ \code
+ select("Frank", "Contacts");
+ waitForTitle("Details: Frank");
+ var details = getText();
+ // Verify that Frank's phone number is shown somewhere
+ verify( details.contains("12345") );
+ // Same, but with more details in error message
+ verify( details.contains("12345"), "Frank's phone number is missing!" );
+ \endcode
+*/
+
+/*!
+ \fn QSystemTest::compare(Variant actual, Variant expected)
+
+ Verifies that \a actual is equal to \a expected. If this is not the case,
+ the current test fails.
+
+ Note that the order of \a actual and \a expected is significant, as it
+ affects the test failure message.
+*/
+
+/*!
+ \fn QSystemTest::fail(String message)
+
+ Immediately fail the current test with the specified \a message.
+*/
+
+/*!
+ Returns the signature of the tabbar widget. If multiple tabbars exist, \a index can be used to
+ distinguish between them (sorted in order of position from top left of screen).
+
+ Example:
+ \code
+ select( "Personal", tabBar() ); // select the tab with text 'Personal' from the tab bar.
+ print( tabBar() ); // to print the signature of the tabbar widget.
+ \endcode
+
+ The test will fail if no visible tabbar is found.
+*/
+#ifdef Q_QDOC
+void QSystemTest::tabBar( int index )
+{
+ // This code is implemented in the QtUiTestrunner bindings
+}
+#endif
+
+/*!
+ \internal
+ Returns an identifier for the current runtime configuration.
+ Includes mousePreferred(), screen size and theme.
+*/
+QString QSystemTest::configurationIdentifier() const
+{
+ return m_config_id;
+}
+
+/*!
+ \internal
+*/
+void QSystemTest::setConfigurationIdentifier(QString const& config)
+{
+ m_config_id = config;
+}
+
+/*!
+ \internal
+*/
+bool QSystemTest::verbose() const
+{
+ return m_verbose;
+}
+
+/*!
+ \internal
+*/
+bool QSystemTest::doQuery(const QTestMessage& message, const QString& queryPath, QTestMessage* reply, int timeout, const QStringList& pass, const QStringList& fail)
+{ return queryPassed(pass, fail, message, queryPath, reply, timeout); }
+
+/*!
+ \internal
+ Prints \a value.
+*/
+void QSystemTest::print( QVariant const& value )
+{
+ QDebug(QtDebugMsg) << qPrintable(value.toString());
+}
+
+/*! \internal
+ Enables/disables waits and animations for demo purposes.
+*/
+void QSystemTest::setDemoMode( bool enabled )
+{
+ QTestMessage queryMsg("enableDemoMode");
+ queryMsg["enable"] = enabled;
+ queryPassed( "OK", "", queryMsg );
+}
+
+/*!
+ \internal
+*/
+bool QSystemTest::demoMode() const
+{
+ return m_demo_mode;
+}
+
+/*!
+ \internal
+*/
+QString QSystemTest::autHost() const
+{
+ return ssh_param.host;
+}
+
+/*!
+ \internal
+*/
+int QSystemTest::autPort() const
+{
+ return m_aut_port;
+}
+
+/*!
+ Uses the \a reason to mark the current testfunction as expected to fail.
+*/
+void QSystemTest::expectFail( const QString &reason )
+{
+ QEXPECT_FAIL(currentDataTag().toLatin1().constData(), qstrdup(reason.toLatin1().constData()), Abort);
+}
+
+/*!
+ Returns the translation for \a text, from the application's installed translation files. \a context is
+ typically a class name. If no translation is found, the \a text is returned unchanged.
+
+ \a comment is a disambiguating comment, for when the same sourceText is used in different roles
+ within the same context. By default, it is null. \a n is used in conjunction with \c %n to support
+ plural forms. See QObject::tr() for details.
+
+ When developing test cases that use translations, it will be necessary to refer to the translator
+ message files (\c .ts files), either directly or through Qt Linguist, to determine which
+ translations are available, and the appropriate \a context to use.
+
+ In some cases, translated phrases will contain argument placeholders (\c %1, etc) which will need
+ to be expanded with the correct values.
+
+ Example:
+ \code
+ // Label translation
+ print( translate("QFileDialog", "File &name:") );
+
+ // Replace argument placeholders
+ compare( message, translate("QHttp", "Host %1 not found").replace("%1", hostname) );
+ \endcode
+
+ \sa QCoreApplication::translate(), getLocale()
+*/
+QString QSystemTest::translate(const QString &context, const QString &text, const QString &comment, int n)
+{
+ QTestMessage message("translate");
+ message["context"] = context;
+ message["text"] = text;
+ message["comment"] = comment;
+ message["number"] = n;
+ QTestMessage reply;
+
+ if (!queryPassed( "OK", "", BT(message), "", &reply )) return false;
+ return reply["translate"].toString();
+}
+
+/*!
+ Returns the system locale.
+
+ \sa QLocale::system(), translate()
+*/
+QLocale QSystemTest::getLocale()
+{
+ QTestMessage message("getLocale");
+ QTestMessage reply;
+
+ if (!queryPassed( "OK", "", BT(message), "", &reply )) return QLocale();
+ return reply["getLocale"].toLocale();
+}
+
+/*!
+ \internal
+*/
+void QSystemTest::qtuitest_pre_test_function()
+{
+ m_query_count = 0;
+ m_skip_current_function = false;
+}
+
+/*!
+ \internal
+*/
+void QSystemTest::qtuitest_post_test_function()
+{
+ if (!m_manual_commands.isEmpty() && !QTest::currentTestFailed()) {
+ showPromptDialog();
+ }
+
+ if (failEmptyTest() && m_query_count == 0 && !m_skip_current_function && !QTest::currentTestFailed()) {
+ fail("Nothing tested");
+ }
+}
+
+/*!
+ \internal
+*/
+bool QSystemTest::runAsManualTest(void)
+{
+ return m_run_as_manual_test;
+}
+
+/*!
+ \internal
+ Pass any configuration values to the system under test
+ */
+void QSystemTest::configTarget()
+{
+ if (m_run_as_manual_test)
+ return;
+
+ // set the target identifier
+ if (!m_targetID.isEmpty())
+ setTargetIdentifier(m_targetID);
+}
+
+/*! \typedef StringArray
+ \relates QSystemTest
+
+ An \l Array object in which every element is a \l String.
+*/
+
+/*! \typedef QVariantArray
+ \relates QSystemTest
+
+ An \l Array object in which every element is a \l QVariant.
+*/
+
+/*! \typedef Function
+ \relates QSystemTest
+
+ The Function type as documented in ECMA-262, section 15.3.
+*/
+
+/*! \typedef Array
+ \relates QSystemTest
+
+ The Array type as documented in ECMA-262, section 15.4.
+
+ The following extensions are provided in QtUiTest.
+
+ \table
+ \row \o \tt{\l Boolean Array.prototype.contains(value)}
+ \o Returns true if the array contains the given value.
+ \endtable
+*/
+
+/*! \typedef String
+ \relates QSystemTest
+
+ The String type as documented in ECMA-262, section 15.5.
+
+ The following extensions are provided in QtUiTest.
+
+ \table
+ \row \o \tt{ \l Boolean String.prototype.contains(String text)}
+ \o Returns true if the string contains the given text.
+ \row \o \tt{ \l Boolean String.prototype.contains(\l QRegExp regex)}
+ \o Returns true if the string is matched by the given regular expression.
+ \row \o \tt{ \l Boolean String.prototype.startsWith(String text)}
+ \o Returns true if the string starts with the given text.
+ \row \o \tt{ String String.prototype.left(\l Number n)}
+ \o Returns the first n characters of the string.
+ \row \o \tt{ String String.prototype.right(\l Number n)}
+ \o Returns the last n characters of the string.
+ \endtable
+*/
+
+
+/*! \typedef Boolean
+ \relates QSystemTest
+
+ The Boolean type as documented in ECMA-262, section 15.6.
+*/
+
+/*! \typedef Number
+ \relates QSystemTest
+
+ The Number type as documented in ECMA-262, section 15.7.
+*/