summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordac <qt-info@nokia.com>2010-07-28 11:28:18 +1000
committerdac <qt-info@nokia.com>2010-07-28 11:28:18 +1000
commit8210814717e2950e8fb552a608ecc7e3e8e7beff (patch)
tree62084690caf92a4974f246dd54cfaa1a8b4f39b3
parent1169253f038774d4a6e2ce8b07b8ec9762ba009d (diff)
Make libqsystemtest not depend on libqtuitest. Remove duplicate code
and conditional compilation required for Creator integration. Plus other cleanups.
-rw-r--r--interpreter/main.cpp7
-rw-r--r--interpreter/qscriptsystemtest.cpp122
-rw-r--r--interpreter/qscriptsystemtest.h9
-rw-r--r--libqsystemtest/gracefulquit.cpp192
-rw-r--r--libqsystemtest/libqsystemtest.pro10
-rw-r--r--libqsystemtest/qabstracttest.cpp169
-rw-r--r--libqsystemtest/qabstracttest.h9
-rw-r--r--libqsystemtest/qsystemtest.cpp234
-rw-r--r--libqsystemtest/qsystemtest.h9
-rw-r--r--libqsystemtest/qsystemtestmaster_p.h35
-rw-r--r--libqsystemtest/qtestprotocol.cpp1182
-rw-r--r--libqsystemtest/qtestprotocol_p.h207
-rw-r--r--libqsystemtest/qtuitestglobal.h (renamed from libqsystemtest/gracefulquit.h)27
-rw-r--r--libqsystemtest/recordevent_p.h135
14 files changed, 1707 insertions, 640 deletions
diff --git a/interpreter/main.cpp b/interpreter/main.cpp
index 58fda65..3867727 100644
--- a/interpreter/main.cpp
+++ b/interpreter/main.cpp
@@ -40,9 +40,6 @@
****************************************************************************/
#include "qscriptsystemtest.h"
-#ifndef Q_OS_SYMBIAN
-# include <gracefulquit.h>
-#endif
#include <QApplication>
int main(int argc, char *argv[])
@@ -50,7 +47,6 @@ int main(int argc, char *argv[])
bool guiEnabled = true;
// If in auto mode, don't need a GUI connection.
- // This allows us to run system tests without an X server running.
for (int i = 1; i < argc; ++i) {
if (!strcasecmp(argv[i], "-auto")) {
guiEnabled = false;
@@ -58,9 +54,6 @@ int main(int argc, char *argv[])
}
QApplication app(argc, argv, guiEnabled);
-#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
- GracefulQuit::install(&app);
-#endif
QScriptSystemTest tc;
return tc.exec(argc, argv);
}
diff --git a/interpreter/qscriptsystemtest.cpp b/interpreter/qscriptsystemtest.cpp
index ad7f32b..2b6a2f4 100644
--- a/interpreter/qscriptsystemtest.cpp
+++ b/interpreter/qscriptsystemtest.cpp
@@ -490,7 +490,6 @@ bool QScriptSystemTest::setQueryError( const QString &errString )
return ret;
}
-#ifdef QTCREATOR_QTEST
int QScriptSystemTest::runTest(const QString &fname, const QStringList &parameters,
const QStringList &environment)
{
@@ -569,95 +568,6 @@ int QScriptSystemTest::runTest(const QString &fname, const QStringList &paramete
// We don't want test failures to affect the exit code.
return (retval < 0) ? retval : 0;
}
-#endif
-
-#ifndef QTCREATOR_QTEST
-int QScriptSystemTest::runTest(int argc, char *argv[])
-{
- if (argc > 1) filename = argv[1];
-
- if (filename.isEmpty()) qFatal("No test script specified.");
-
- /* Consume the first argument */
- argv[1] = argv[0];
- char **c_argv = &argv[1];
- int c_argc = argc-1;
-
- {
- QFileInfo fi(filename);
- if (!fi.exists()) qFatal("%s doesn't exist", qPrintable(filename));
- filename = fi.absoluteFilePath();
- }
-
- QFile file(filename);
- if (!file.open(QFile::ReadOnly)) qFatal("Can't open %s", qPrintable(filename));
- QTextStream stream(&file);
- QString script = stream.readAll();
-
- ScriptPreprocessor().preprocess(script);
-
- setupEnums(&m_engine);
-
- // include() imports scripts directly into the parent script.
- m_engine.globalObject().setProperty
- ("include", m_engine.newFunction(includeFunction, 1));
- m_engine.globalObject().setProperty
- ("setFlags", m_engine.newFunction(setFlags, 3));
-
-
- m_engine.globalObject().setProperty("_dateToString", m_engine.newFunction(dateToString));
- m_engine.evaluate("_old_date_toString = Date.prototype.toString;"
- "Date.prototype.toString = function() {"
- " if (arguments[0] == undefined)"
- " return _old_date_toString.apply(this, arguments);"
- " return _dateToString.apply(this, arguments);"
- "}");
-
- m_engine.globalObject().setProperty("ParentTestObject", m_engine.newQObject(this));
- m_engine.globalObject().setProperty("ParentTestMetaObject", m_engine.newQMetaObject(metaObject()));
-
- loadBuiltins(m_engine);
- importIntoGlobalNamespace(m_engine, "ParentTestObject");
-
- // Allow shebangs without giving syntax errors.
- if (script.startsWith("#!")) script.prepend("//");
- script.prepend("with(ParentTestMetaObject){");
- script.append("\n}");
-
- QtScriptTest tc(filename, script, &m_engine);
- testObject = &tc;
-
- qScriptRegisterMetaType(&m_engine, variantToScriptValue, variantFromScriptValue);
- qScriptRegisterSequenceMetaType<QList<qint64> >(&m_engine);
-
- // Only set up the test data path if not explicitly set by user
- if (!QCoreApplication::arguments().contains("-data")) {
- setupTestDataPath(qPrintable(filename));
- }
-
- enableQueryWarnings(false);
-
- // If we get here, the syntax of the script is definitely OK
- // (a syntax error causes a qFatal in the QtScriptTest ctor).
- if (m_checkOnly)
- return 0;
-
- // If an IDE is connected, set the agent to enable script debugging
- if (testIDE() && testIDE()->isConnected()) {
- m_engine.setAgent(m_agent);
- }
-
- int retval = QTest::qExec(testObject, c_argc, c_argv);
-
- testObject = 0;
-
- // After a full test run, QTestLib sometimes returns 0 or sometimes returns
- // the number of test failures, depending on how it was compiled. In both
- // cases, a negative number denotes an error.
- // We don't want test failures to affect the exit code.
- return (retval < 0) ? retval : 0;
-}
-#endif
/*!
\internal
@@ -714,22 +624,22 @@ QVariantMap QScriptSystemTest::sendRaw(const QString& event, const QScriptValue&
return ret;
}
-#ifndef QTCREATOR_QTEST
+//#ifndef QTCREATOR_QTEST
/*!
\internal
Print any special usage information which should be shown when test is launched
with -help.
*/
-void QScriptSystemTest::printUsage(int argc, char *argv[]) const
+void QScriptSystemTest::printUsage() const
{
- QSystemTest::printUsage(argc, argv);
+ QSystemTest::printUsage();
qWarning(
" Script options:\n"
" -c : Check the syntax of the test only. Returns a non-zero exit code if the test\n"
" contains any syntax errors.\n"
);
}
-#endif
+//#endif
/*!
\internal
@@ -855,27 +765,19 @@ void QScriptSystemTest::processMessage(const QTestMessage& message)
\internal
Processes the command line parameters.
*/
-void QScriptSystemTest::processCommandLine( int &argc, char *argv[] )
+void QScriptSystemTest::processCommandLine( QStringList &args )
{
- int offset = 0;
-
- for (int i=1; i<argc; ++i) {
-
- if ( !strcasecmp(argv[i], "-c") ) {
- argv[i] = 0;
- offset++;
+ QMutableStringListIterator it(args);
+
+ while (it.hasNext()) {
+ QString arg = it.next();
+ if (!arg.compare("-c", Qt::CaseInsensitive)) {
m_checkOnly = true;
-
- } else {
- if (offset > 0) {
- argv[i-offset] = argv[i];
- argv[i] = 0;
- }
+ it.remove();
}
}
- argc-=offset;
- QSystemTest::processCommandLine(argc, argv);
+ QSystemTest::processCommandLine(args);
}
void QScriptSystemTest::scriptPositionChange(qint64 scriptId, int line, int column)
diff --git a/interpreter/qscriptsystemtest.h b/interpreter/qscriptsystemtest.h
index 61d8429..9624fd0 100644
--- a/interpreter/qscriptsystemtest.h
+++ b/interpreter/qscriptsystemtest.h
@@ -65,10 +65,8 @@ public:
void scriptPositionChange(qint64, int, int);
void scriptContextChange(bool);
-#ifdef QTCREATOR_QTEST
virtual int runTest(const QString &fname, const QStringList &parameters,
const QStringList &environment);
-#endif
public slots:
virtual bool fail(QString const &message);
@@ -91,11 +89,8 @@ protected:
virtual bool setQueryError( const QTestMessage &message );
virtual bool setQueryError( const QString &errString );
-#ifndef QTCREATOR_QTEST
- virtual int runTest(int argc, char *argv[]);
- virtual void printUsage(int,char*[]) const;
-#endif
- virtual void processCommandLine(int&, char*[]);
+ virtual void printUsage() const;
+ virtual void processCommandLine(QStringList&);
virtual bool isFailExpected();
diff --git a/libqsystemtest/gracefulquit.cpp b/libqsystemtest/gracefulquit.cpp
deleted file mode 100644
index a50fb94..0000000
--- a/libqsystemtest/gracefulquit.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-/****************************************************************************
-**
-** 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 "gracefulquit.h"
-
-#ifdef Q_OS_UNIX
-
-#include <QTimer>
-#include <QSocketNotifier>
-
-#include <signal.h>
-#include <errno.h>
-#include <unistd.h>
-
-int graceful_quit_fds[2];
-sighandler_t graceful_quit_old_handlers[32];
-int graceful_quit_signal;
-
-void graceful_quit_alarm_handler(int)
-{
- ::kill(::getpid(), graceful_quit_signal);
- qFatal("Terminated.");
-}
-
-void graceful_quit_signal_handler(int sig)
-{
- graceful_quit_signal = sig;
-
- // Replace the old handlers.
- signal(SIGINT, graceful_quit_old_handlers[SIGINT]);
- signal(SIGQUIT, graceful_quit_old_handlers[SIGQUIT]);
- signal(SIGTERM, graceful_quit_old_handlers[SIGTERM]);
- signal(SIGHUP, graceful_quit_old_handlers[SIGHUP]);
-
- /*
- Write to the monitored pipe to wake up any watching socket notifiers.
- */
- char byte = 0x01;
- write(graceful_quit_fds[1], &byte, sizeof(byte));
-}
-
-/*
- This class ensures that the process really dies if it fails to gracefully
- quit (e.g., it's stuck in a nested event loop).
-*/
-class QKillTimer : public QObject
-{
-Q_OBJECT
-public:
- QKillTimer(QSocketNotifier*);
-
-private slots:
- void on_activated();
-
-private:
- QSocketNotifier* m_notifier;
-};
-
-QKillTimer::QKillTimer(QSocketNotifier* parent)
- : QObject(parent),
- m_notifier(parent)
-{
- connect(m_notifier, SIGNAL(activated(int)), this, SLOT(on_activated()));
-}
-
-void QKillTimer::on_activated()
-{
- m_notifier->setEnabled(false);
- signal(SIGALRM, graceful_quit_alarm_handler);
- ::alarm(1);
-}
-
-/*!
- \class GracefulQuit
- \inpublicgroup QtUiTestExtension
- \brief The GracefulQuit class provides a method for quitting applications
- gracefully when a terminating unix signal is received.
-*/
-
-/*!
- Installs a graceful quit handler on \a object.
-
- \a object must have a quit() slot, such as QCoreApplication.
- When a Unix signal occurs which would normally cause a termination, such
- as SIGINT or SIGTERM, the quit() slot will be invoked on \a object.
-
- Example:
- \code
- int main(int argc, char** argv)
- {
- QApplication app(argc, argv);
-
- // Ensure we shut down sanely upon receiving SIGINT etc.
- GracefulQuit::install(&app);
-
- // Do regular stuff...
- MyWidget widget;
- widget.show();
-
- // If we get a signal, all objects will be destroyed as they should be.
- return app.exec();
- }
- \endcode
-*/
-void GracefulQuit::install(QObject* object)
-{
- Q_ASSERT(object);
-
- /*
- Create a pipe for internal use.
- When the pipe is written into, that means we've received an exit
- signal.
- */
- static bool made_pipe = false;
- if (!made_pipe) {
- if (-1 == pipe(graceful_quit_fds)) {
- qWarning("GracefulQuit::install(): pipe() failed: %s", strerror(errno));
- return;
- } else {
- made_pipe = true;
-
- /*
- Install the signal handler.
- */
- graceful_quit_old_handlers[SIGINT] = signal(SIGINT, graceful_quit_signal_handler);
- graceful_quit_old_handlers[SIGQUIT] = signal(SIGQUIT, graceful_quit_signal_handler);
- graceful_quit_old_handlers[SIGTERM] = signal(SIGTERM, graceful_quit_signal_handler);
- graceful_quit_old_handlers[SIGHUP] = signal(SIGHUP, graceful_quit_signal_handler);
- }
- }
-
- /*
- Watch the readable fd. When it can be read from, it's time for us
- to quit.
- */
- static QSocketNotifier* sn = new QSocketNotifier
- (graceful_quit_fds[0], QSocketNotifier::Read, object);
-
- // Give the application 1 second to gracefully quit.
- static QKillTimer* kt = new QKillTimer(sn);
- Q_UNUSED(kt);
-
- if (!object->connect(sn, SIGNAL(activated(int)), SLOT(quit())))
- Q_ASSERT(0);
-
-}
-
-#else
-void GracefulQuit::install(QObject*)
-{}
-#endif
-
-#include "gracefulquit.moc"
-
diff --git a/libqsystemtest/libqsystemtest.pro b/libqsystemtest/libqsystemtest.pro
index f12c431..83bf8c2 100644
--- a/libqsystemtest/libqsystemtest.pro
+++ b/libqsystemtest/libqsystemtest.pro
@@ -11,14 +11,13 @@ SEMI_PRIVATE_HEADERS += \
qtestverifydlg_p.h
HEADERS +=\
- gracefulquit.h \
qabstracttest.h \
qsystemtest.h \
qtestide.h \
+ qtestprotocol.h \
testdevicecontrol.h
SOURCES +=\
- gracefulquit.cpp \
qabstracttest.cpp \
qtestide.cpp \
qtestremote.cpp \
@@ -26,6 +25,7 @@ SOURCES +=\
qsystemtest.cpp \
qsystemtest_p.cpp \
qsystemtestmaster.cpp \
+ qtestprotocol.cpp \
testdevicecontrol.cpp
HEADERS +=\
@@ -91,7 +91,7 @@ TEMPLATE=lib
TARGET=qsystemtest
TARGET=$$qtLibraryTarget($$TARGET)
HEADERS*=$$SEMI_PRIVATE_HEADERS $$PRIVATE_HEADERS
-INCLUDEPATH+=$$SRCROOT/libqtuitest
+#INCLUDEPATH+=$$SRCROOT/libqtuitest
CONFIG+=qtestlib
QT+=network script
LIBS+=-L$$BUILDROOT/lib -lBotan
@@ -105,14 +105,10 @@ unix:!symbian {
symbian {
TARGET.EPOCALLOWDLLDATA=1
TARGET.CAPABILITY += AllFiles ReadDeviceData ReadUserData SwEvent WriteUserData
- SOURCES-=gracefulquit.cpp
- HEADERS-=gracefulquit.h
LIBS+=-L$$OUT_PWD -lqtuitest -lws32 -leuser -lcone
}
win32 {
- SOURCES-=gracefulquit.cpp
- HEADERS-=gracefulquit.h
CONFIG(debug,debug|release): LIBS+=-L$$BUILDROOT/libqtuitest -lqtuitestd
CONFIG(release,debug|release):LIBS+=-L$$BUILDROOT/libqtuitest -lqtuitest
target.path=$$[QT_INSTALL_BINS]
diff --git a/libqsystemtest/qabstracttest.cpp b/libqsystemtest/qabstracttest.cpp
index 2353e6d..f441938 100644
--- a/libqsystemtest/qabstracttest.cpp
+++ b/libqsystemtest/qabstracttest.cpp
@@ -322,25 +322,22 @@ QString QAbstractTest::currentTestFunction( bool fullName ) const
int QAbstractTest::exec( int argc, char* argv[], char* filename )
{
setupTestDataPath(filename);
- QByteArray defOpt = qgetenv("QTUITEST_DEFAULT_OPTIONS");
- QList<QByteArray> defaultOptions;
+
+ QStringList options;
+ for (int i = 1; i < argc; ++i) options << argv[i];
+
+ QString defOpt = qgetenv("QTUITEST_DEFAULT_OPTIONS");
if (defOpt.length()) {
- defaultOptions = defOpt.split(' ');
+ options << defOpt.split(' ');
}
- int defArgc = defaultOptions.length();
-
- int _argc(argc+defArgc);
- char **_argv = new char*[argc+defArgc + 1];
- for (int i = 0; i < argc; ++i) _argv[i] = argv[i];
- for (int j = 0; j < defArgc; ++j) _argv[argc+j] = defaultOptions[j].data();
- processCommandLine(_argc, _argv);
- int ret = runTest(_argc, _argv);
- delete[] _argv;
- return ret;
+
+ processCommandLine(options);
+ QString script = options.first();
+ return runTest(script, options, QStringList());
}
#endif
-#ifndef QTCREATOR_QTEST
+//#ifndef QTCREATOR_QTEST
/*
\internal
Print a usage message.
@@ -348,10 +345,11 @@ int QAbstractTest::exec( int argc, char* argv[], char* filename )
Any subclass which overrides processCommandLine() should also override this
function and provide documentation for their arguments.
*/
-void QAbstractTest::printUsage( int argc, char* argv[] ) const
+void QAbstractTest::printUsage() const
{
- qWarning( " Usage:\n"
- " %s [file] [options] [testfunction[:datatag]]...\n"
+ qWarning(
+ " Usage:\n"
+ " qtuitestrunner [file] [options] [testfunction[:datatag]]...\n"
"\n"
" 'file' is a QtScript file containing the testcase.\n"
" 'testfunctions' is a list of functions to execute (separated by spaces).\n"
@@ -384,109 +382,65 @@ void QAbstractTest::printUsage( int argc, char* argv[] ) const
" data that can't be hardcoded in the testcase itself).\n"
" If -data is not specified the testcase will first look in the testdata subdirectory\n"
" of the directory containing the test source file, then in $HOME/.qtest\n"
- , (argc) ? argv[0] : "test");
+ );
}
-#endif
+//#endif
/*
\internal
Processes the command line parameters specified by \a argc, \a argv.
Subclasses may reimplement this function to handle specific arguments.
*/
-void QAbstractTest::processCommandLine( int &argc, char* argv[] )
-{
- int offset = 0;
- for (int i = 1; i < argc; ++i) {
- if ( !strcasecmp(argv[i], "-data") ) {
- argv[i] = 0;
- offset += 2;
- ++i;
- if ( i < argc ) {
- setBaseDataPath(QString(argv[i]));
- argv[i] = 0;
- }
-
- } else if ( !strcasecmp(argv[i], "-v") ||
- !strcasecmp(argv[i], "-v1") ||
- !strcasecmp(argv[i], "-v2") ) {
+void QAbstractTest::processCommandLine( QStringList &args )
+{
+ QMutableStringListIterator it(args);
+
+ while (it.hasNext()) {
+ QString arg = it.next();
+ if ( !arg.compare("-data", Qt::CaseInsensitive) ) {
+ it.remove();
+ if (!it.hasNext()) qFatal("Expected a value after %s", qPrintable(arg));
+ setBaseDataPath(it.next());
+ it.remove();
+ } else if ( !arg.compare("-v", Qt::CaseInsensitive) ) {
+ it.remove();
+ Autotest_QLog::m_enabled = true;
+ } else if ( !arg.compare("-v1", Qt::CaseInsensitive) ||
+ !arg.compare("-v2", Qt::CaseInsensitive) ) {
// Don't consume -v1 or -v2
- if (!strcasecmp(argv[i], "-v")) {
- argv[i] = 0;
- offset++;
- }
Autotest_QLog::m_enabled = true;
-
- } else if ( !strcasecmp(argv[i], "-learn") ) {
- argv[i] = 0;
- offset++;
+ } else if ( !arg.compare("-learn", Qt::CaseInsensitive) ) {
+ it.remove();
setLearnMode(LearnNew);
-
- } else if ( !strcasecmp(argv[i], "-learn-all") ) {
- argv[i] = 0;
- offset++;
+ } else if ( !arg.compare("-learn-all", Qt::CaseInsensitive) ) {
+ it.remove();
setLearnMode(LearnAll);
-
- } else if ( !strcasecmp(argv[i], "-failempty") ) {
- argv[i] = 0;
- offset++;
+ } else if ( !arg.compare("-failempty", Qt::CaseInsensitive) ) {
+ it.remove();
d->failEmptyTest = true;
-
- } else if ( !strcasecmp(argv[i], "-timed") ) {
- argv[i] = 0;
- offset++;
+ } else if ( !arg.compare("-timed", Qt::CaseInsensitive) ) {
+ it.remove();
QTimedTest::pass_through = false;
QTimedTest::print_times = true;
-
- } else if ( !strcasecmp(argv[i], "-maxtime") ) {
- argv[i] = 0;
- offset += 2;
- ++i;
- if ( i < argc ) {
- QTimedTest::pass_through = false;
- bool ok;
- timeout_thread = new FatalTimeoutThread;
- timeout_thread->setTimeout(QString::fromAscii(argv[i]).toInt(&ok));
- if (!ok) qFatal("Invalid parameter to -maxtime");
- argv[i] = 0;
- }
-
- } else if ( !strcasecmp(argv[i], "-help") ||
- !strcasecmp(argv[i], "--help") ||
- !strcasecmp(argv[i], "-h") ) {
- argv[i] = 0;
- offset++;
+ } else if ( !arg.compare("-maxtime", Qt::CaseInsensitive) ) {
+ it.remove();
+ if (!it.hasNext()) qFatal("Expected a value after %s", qPrintable(arg));
+ QTimedTest::pass_through = false;
+ bool ok;
+ timeout_thread = new FatalTimeoutThread;
+ timeout_thread->setTimeout(it.next().toInt(&ok));
+ if (!ok) qFatal("Invalid parameter to -maxtime");
+ it.remove();
+ } else if ( !arg.compare("-help", Qt::CaseInsensitive) ||
+ !arg.compare("--help", Qt::CaseInsensitive) ||
+ !arg.compare("-h", Qt::CaseInsensitive) ) {
+ it.remove();
#ifndef QTCREATOR_QTEST
- printUsage(argc-offset, argv);
+ printUsage();
exit(0);
#endif
-
- // Silently ignore a few system test specific arguments.
- // For compatibility, we'll silently ignore these so that
- // the same arguments can be specified for unit and system tests.
- } else if ( !strcasecmp(argv[i], "-autip")
- || !strcasecmp(argv[i], "-authost")
- || !strcasecmp(argv[i], "-autport")
- || !strcasecmp(argv[i], "-remote") ) {
- argv[i] = 0;
- offset += 2;
- ++i;
- argv[i] = 0;
-
- } else if ( !strcasecmp(argv[i], "-keepaut")
- || !strcasecmp(argv[i], "-silentaut")
- || !strcasecmp(argv[i], "-auto")
- || !strcasecmp(argv[i], "-demomode") ) {
- argv[i] = 0;
- offset++;
-
- } else {
- if (offset > 0) {
- argv[i-offset] = argv[i];
- argv[i] = 0;
- }
}
- }
- argc-=offset;
+ }
}
#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
@@ -501,7 +455,7 @@ void handle_segfault(int signum)
}
#endif
-# ifndef QTCREATOR_QTEST
+//# ifndef QTCREATOR_QTEST
/*
\internal
Run test with arguments \a argc, \a argv, and return an exit code.
@@ -509,16 +463,19 @@ void handle_segfault(int signum)
using QTest::qExec(). Subclasses may reimplement this function to provide
other behaviour.
*/
-int QAbstractTest::runTest(int argc, char *argv[])
+int QAbstractTest::runTest(const QString &fname, const QStringList &args,
+ const QStringList &environment)
{
+ Q_UNUSED(fname);
+ Q_UNUSED(environment);
#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
signal(SIGSEGV, handle_segfault);
if (!setjmp(segfault_jmp))
#endif
- return QTest::qExec( this, argc, argv );
+ return QTest::qExec( this, args );
return -1;
}
-#endif
+//#endif
/*!
\internal
diff --git a/libqsystemtest/qabstracttest.h b/libqsystemtest/qabstracttest.h
index 10a2a14..2a20d09 100644
--- a/libqsystemtest/qabstracttest.h
+++ b/libqsystemtest/qabstracttest.h
@@ -131,11 +131,10 @@ public slots:
#ifndef Q_QDOC
protected:
-# ifndef QTCREATOR_QTEST
- virtual int runTest(int argc, char *argv[]);
- virtual void printUsage( int argc, char *argv[] ) const;
-# endif
- virtual void processCommandLine( int &argc, char *argv[] );
+ virtual int runTest(const QString &fname, const QStringList &parameters,
+ const QStringList &environment);
+ virtual void printUsage() const;
+ virtual void processCommandLine(QStringList&);
virtual void setupTestDataPath(const char *filename);
#endif
diff --git a/libqsystemtest/qsystemtest.cpp b/libqsystemtest/qsystemtest.cpp
index e8dae1b..f6e0c36 100644
--- a/libqsystemtest/qsystemtest.cpp
+++ b/libqsystemtest/qsystemtest.cpp
@@ -42,7 +42,7 @@
#include <qsystemtest.h>
#include "qsystemtestmaster_p.h"
#include "qtuitest_config.h"
-#include "gracefulquit.h"
+//#include "gracefulquit.h"
#include "ui_recorddlg.h"
#ifdef QTCREATOR_QTEST
@@ -2786,21 +2786,17 @@ void QSystemTest::expectApplicationClose( bool value )
\internal
Processes the command line parameters.
*/
-void QSystemTest::processCommandLine( int &argc, char *argv[] )
-{
- int offset = 0;
-
- // Whenever we discover an option that 'we' understand, eat away the option (and its parameters) so that QTest doesn't get
- // confused by them.
- for (int i=1; i<argc; ++i) {
- if ( !strcasecmp(argv[i], "-remote") ) {
- argv[i] = 0;
- ++offset;
-
- if ( i+1 >= argc || !strlen(argv[i+1]) )
- qFatal("Expected a host:port argument after -remote");
-
- QString host_port( argv[i+1] );
+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);
@@ -2813,158 +2809,78 @@ void QSystemTest::processCommandLine( int &argc, char *argv[] )
if (!ok)
qFatal("'%s' is not a valid host:port argument", qPrintable(host_port));
- argv[i+1] = 0;
- ++offset;
- ++i;
-
testIDE()->openRemote( host, port );
- connect(testIDE(), SIGNAL(abort()), this, SLOT(abortTest()));
-
- } else if ( !strcasecmp(argv[i], "-autip") || !strcasecmp(argv[i], "-authost") ) {
- char *given_arg = argv[i]; // so we can output 'autip' or 'authost' in error messages
-
- argv[i] = 0;
- ++offset;
- if ( i+1 >= argc || !strlen(argv[i+1]) )
- qFatal("Expected a host specifier after %s", given_arg);
-
- ssh_param.host = argv[i+1];
- argv[i+1] = 0;
- ++offset;
- ++i;
+ connect(testIDE(), 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 ( !strcasecmp(argv[i], "-username") ) {
- char *given_arg = argv[i];
-
- argv[i] = 0;
- ++offset;
- if ( i+1 >= argc || !strlen(argv[i+1]) )
- qFatal("Expected a user name after %s", given_arg);
-
- ssh_param.uname = argv[i+1];
- argv[i+1] = 0;
- ++offset;
- ++i;
-
- } else if ( !strcasecmp(argv[i], "-pwd") ) {
- char *given_arg = argv[i];
-
- argv[i] = 0;
- ++offset;
- if ( i+1 >= argc || !strlen(argv[i+1]) )
- qFatal("Expected a password after %s", given_arg);
-
- ssh_param.pwd = argv[i+1];
+ } 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;
- argv[i+1] = 0;
- ++offset;
- ++i;
-
ssh_param.privateKeyFile = "";
-
- } else if ( !strcasecmp(argv[i], "-private-key") ) {
- char *given_arg = argv[i];
-
- argv[i] = 0;
- ++offset;
- if ( i+1 >= argc || !strlen(argv[i+1]) )
- qFatal("Expected a private key file after %s", given_arg);
-
- ssh_param.privateKeyFile = argv[i+1];
+ } 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;
- argv[i+1] = 0;
- ++offset;
- ++i;
-
- } else if ( !strcasecmp(argv[i], "-autport") ) {
- argv[i] = 0;
- ++offset;
- if ( i+1 >= argc || !strlen(argv[i+1]) )
- qFatal("Expected a port specifier after -autport");
-
+ } 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 = QString(argv[i+1]).toUShort( &ok );
+ m_aut_port = it.next().toUShort( &ok );
if (!ok)
- qFatal("%s is not a valid port specifier", argv[i+1]);
-
- argv[i+1] = 0;
- ++offset;
- ++i;
-
- } else if ( !strcasecmp(argv[i], "-keepaut") ) {
- argv[i] = 0;
- ++offset;
+ 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 ( !strcasecmp(argv[i], "-silentaut") ) {
- argv[i] = 0;
- offset++;
+ } else if ( !arg.compare("-silentaut", Qt::CaseInsensitive) ) {
+ it.remove();
m_silent_aut = true;
-
- } else if ( !strcasecmp(argv[i], "-noaut") ) {
- argv[i] = 0;
- offset++;
+ } else if ( !arg.compare("-noaut", Qt::CaseInsensitive) ) {
+ it.remove();
m_no_aut = true;
-
- } else if ( !strcasecmp(argv[i], "-auto") ) {
- argv[i] = 0;
- offset++;
+ } else if ( !arg.compare("-auto", Qt::CaseInsensitive) ) {
+ it.remove();
m_auto_mode = true;
-
- } else if ( !strcasecmp(argv[i], "-force-manual") ) {
- argv[i] = 0;
- offset++;
+ } else if ( !arg.compare("-force-manual", Qt::CaseInsensitive) ) {
+ it.remove();
m_run_as_manual_test = true;
-
- } else if ( !strcasecmp(argv[i], "-env") ) {
- argv[i] = 0;
- offset++;
-
- if ( i+1 >= argc || !strlen(argv[i+1]) )
- qFatal("Expected a string after -env");
-
- m_env << QString::fromLocal8Bit(argv[i+1]);
- argv[i+1] = 0;
- ++offset;
- ++i;
- } else if ( !strcasecmp(argv[i], "-targetID") ) {
- argv[i] = 0;
- offset++;
-
- if ( i+1 >= argc || !strlen(argv[i+1]) )
- qFatal("Expected a string after -targetID");
-
- m_targetID = QString::fromLocal8Bit(argv[i+1]);
- argv[i+1] = 0;
- ++offset;
- ++i;
- } else if ( !strcasecmp(argv[i], "-demomode") ) {
- argv[i] = 0;
- offset++;
+ } 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 ( !strcasecmp(argv[i], "-verbose-perf") ) {
- argv[i] = 0;
- offset++;
+ } else if ( !arg.compare("-verbose-pref", Qt::CaseInsensitive) ) {
+ it.remove();
m_verbose_perf = true;
-
- } else if ( !strcasecmp(argv[i], "-v") ) {
- argv[i] = 0;
- offset++;
+ } else if ( !arg.compare("-v", Qt::CaseInsensitive) ) {
+ it.remove();
m_verbose = true;
-
- } else {
- if (offset > 0) {
- argv[i-offset] = argv[i];
- argv[i] = 0;
- }
}
}
- argc-=offset;
-
- QAbstractTest::processCommandLine(argc, argv);
+ QAbstractTest::processCommandLine(args);
if (m_auto_mode && learnMode() != LearnNone) {
qWarning("Can't learn in auto mode; learn options ignored.");
@@ -3142,15 +3058,15 @@ void QSystemTest::abortTest()
#endif
}
-#ifndef QTCREATOR_QTEST
+//#ifndef QTCREATOR_QTEST
/*!
\internal
Print any special usage information which should be shown when test is launched
with -help.
*/
-void QSystemTest::printUsage(int argc, char *argv[]) const
+void QSystemTest::printUsage() const
{
- QAbstractTest::printUsage(argc, argv);
+ 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"
@@ -3182,7 +3098,7 @@ void QSystemTest::printUsage(int argc, char *argv[]) const
, DEFAULT_AUT_PORT
);
}
-#endif
+//#endif
#ifndef Q_QDOC
/*
@@ -3371,18 +3287,20 @@ bool QSystemTest::setQueryError( const QTestMessage &message )
return false; // query is NOT successfull
}
-#ifndef QTCREATOR_QTEST
+//#ifndef QTCREATOR_QTEST
/*!
\internal
Launch AUT and run the test.
*/
-int QSystemTest::runTest(int argc, char *argv[]) {
+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(argc, argv);
+ return QAbstractTest::runTest(fname, args, environment);
}
-#endif
+//#endif
/*!
\internal
diff --git a/libqsystemtest/qsystemtest.h b/libqsystemtest/qsystemtest.h
index 7201249..b2add54 100644
--- a/libqsystemtest/qsystemtest.h
+++ b/libqsystemtest/qsystemtest.h
@@ -396,11 +396,10 @@ protected slots:
void abortTest();
protected:
-#ifndef QTCREATOR_QTEST
- virtual void printUsage(int,char*[]) const;
- virtual int runTest(int,char*[]);
-#endif
- virtual void processCommandLine(int&,char*[]);
+ virtual void printUsage() const;
+ virtual int runTest(const QString &fname, const QStringList &parameters,
+ const QStringList &environment);
+ virtual void processCommandLine(QStringList&);
virtual void applicationStandardOutput(QList<QByteArray> const&);
virtual void applicationStandardError(QList<QByteArray> const&);
diff --git a/libqsystemtest/qsystemtestmaster_p.h b/libqsystemtest/qsystemtestmaster_p.h
index 57c8377..f82bbf0 100644
--- a/libqsystemtest/qsystemtestmaster_p.h
+++ b/libqsystemtest/qsystemtestmaster_p.h
@@ -71,40 +71,5 @@ private:
QSystemTest *test_case;
};
-/*
-class QTUITEST_EXPORT QSystemTestMasterServer: public QTestServerSocket
-{
- Q_OBJECT
-public:
- QSystemTestMasterServer( quint16 port, QSystemTest *testCase );
- virtual ~QSystemTestMasterServer();
-
- //static void delayConnection();
-
- bool waitForApp( const QString &appName, uint timeOut );
- QSystemTestMaster *findApp( const QString &appName );
- QSystemTestMaster *getApp( uint index );
- uint appCount() { return app_count; };
-
-protected:
- virtual void onNewConnection( int socket );
- bool appendApp( QSystemTestMaster *app );
- //static void savePortInfo( int port );
-
-protected slots:
- void connectionClosed( QTestProtocol *socket );
-
-signals:
- void newGuiClient( QSystemTestMaster *newApp );
-
-private:
- // FIXME: Should use QList here
- #define MAX_APPS 50
- QSystemTestMaster *apps[MAX_APPS];
- uint app_count;
- QSystemTest *test_case;
-};
-*/
-
#endif
diff --git a/libqsystemtest/qtestprotocol.cpp b/libqsystemtest/qtestprotocol.cpp
new file mode 100644
index 0000000..9246a28
--- /dev/null
+++ b/libqsystemtest/qtestprotocol.cpp
@@ -0,0 +1,1182 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+/*
+ This cpp file contains a number of socket related classes that are used heavily in QtUiTest.
+ The classes are documented later in the file.
+*/
+
+#include "qtestprotocol_p.h"
+
+#include <QApplication>
+#include <QString>
+#include <QTimer>
+#include <QUuid>
+#include <QFileInfo>
+#include <QDir>
+#include <QtNetwork/QTcpSocket>
+#include <QtNetwork/QTcpServer>
+#include <QtNetwork/QHostAddress>
+#include <QtNetwork/QHostInfo>
+#include <QPointer>
+
+#include <QDebug>
+#define qLog(A) if (1); else qDebug()
+
+#if defined(Q_OS_WIN32) && !defined(Q_OS_TEMP)
+# include <io.h>
+#endif
+
+static const int CONNECT_TIMEOUT = 20000;
+
+static const quint32 TEST_MSG_SIGNATURE = 0xEDBAEDBA;
+static const quint32 TEST_MSG_END = 0xEDBAABDE;
+static const quint8 TEST_MSG_VERSION = 3;
+static uint g_unique_id = 0;
+
+bool waitForSignal(QObject* object, const char* signal, int timeout)
+{
+#ifdef QTUITEST_TARGET
+ return QtUiTest::waitForSignal(object, signal, timeout);
+#else
+ QEventLoop loop;
+ QTimer dummy;
+ dummy.setInterval(1000);
+ if (!QObject::connect(object, signal, &dummy, SLOT(start())))
+ return false;
+ if (!QObject::connect(object, signal, &loop, SLOT(quit())))
+ return false;
+ QTimer::singleShot(timeout, &loop, SLOT(quit()));
+ loop.exec();
+ return dummy.isActive();
+#endif
+}
+
+void wait(int timeout)
+{
+#ifdef QTUITEST_TARGET
+ QtUiTest::wait(timeout);
+#else
+ QEventLoop loop;
+ QTimer::singleShot(timeout, &loop, SLOT(quit()));
+ loop.exec();
+#endif
+}
+
+// ********************************************************************************
+// ****************************** QTestMessage ************************************
+// ********************************************************************************
+
+/*
+ \class QTestMessage QTestMessage.h
+ \inpublicgroup QtUiTestModule
+
+ \brief The QTestMessage class can be used for exchanging messages between separate
+ processes. The class is never used directly but instead is used by QTestProtocol.
+
+ The class basically wraps a number of strings (i.e. 'event' and 'message') and binary data
+ (i.e. a bytearray or a file) into a datastream that is sent to a peer using a socket
+ connection. On the receiving side a QTestMessage instance is decoding the datastream
+ and performs a number of sanity checks to make it a bit more robost.
+
+ A TCP connection 'should' be reliable but in exceptional cases bytes may be lost. This
+ would result in the connection becoming useless because all future data reception would
+ be out of sync. To solve this a 'resync' function is implemented that can re-sync the datastream
+ by throwing away bytes until the data seems in sync again. The obvious downside is that
+ at least one message will be lost.
+
+ Message format is as follows:
+
+ Field: Length: Remarks:
+
+ Start signature 4 Fixed value - 0xEDBAEDBA
+ Protocol version 1 Fixed value - 3
+ Message number 2
+ Event length 4 Length of following string
+ Event string Event length QString value
+ Message length 4 Length of following string
+ Messaga string Message length QString value
+ Data length 4 Length of following binary data
+ File data Data length Binary data
+ End signature 4 Fixed value - 0xEDBAABDE
+*/
+
+/*
+ \internal
+
+ Constructs a default (empty) message.
+*/
+
+QTestMessage::QTestMessage(QString const &event, QVariantMap const &map)
+ : m_phase(0)
+ , m_msg_id(0)
+ , m_event(event)
+ , m_map(map)
+{
+}
+
+QTestMessage::QTestMessage(QString const &event, const QTestMessage &other )
+ : m_phase(0)
+ , m_msg_id(other.m_msg_id)
+ , m_map(other.m_map)
+{
+ m_event = event;
+}
+
+QTestMessage::QTestMessage(QString const &event, QString const &queryApp, QString const &queryPath )
+ : m_phase(0)
+ , m_msg_id(0)
+ , m_event(event)
+ , m_map(QVariantMap())
+{
+ m_map["queryapp"] = queryApp;
+ m_map["querypath"] = queryPath;
+}
+
+/*
+ \internal
+
+ Copy constructor.
+*/
+
+QTestMessage::QTestMessage(const QTestMessage &other)
+ : m_phase(0)
+ , m_msg_id(other.m_msg_id)
+ , m_event(other.m_event)
+ , m_map(other.m_map)
+{
+}
+
+/*
+ \internal
+
+ Destroys the message.
+*/
+
+QTestMessage::~QTestMessage()
+{
+}
+
+/*
+ \internal
+
+ Assignment operator.
+*/
+
+QTestMessage& QTestMessage::operator=(const QTestMessage &other)
+{
+ m_msg_id = other.m_msg_id;
+ m_event = other.m_event;
+ m_map = other.m_map;
+
+ return *this;
+}
+
+QVariant &QTestMessage::operator[](QString const &key)
+{
+ return m_map[key.toLower()];
+}
+
+QVariant const QTestMessage::operator[](QString const &key) const
+{
+ return m_map[key.toLower()];
+}
+
+bool QTestMessage::contains(QString const &key) const
+{
+ return m_map.contains(key.toLower());
+}
+
+QList<QString> QTestMessage::keys() const
+{
+ return m_map.keys();
+}
+
+QString QTestMessage::toString() const
+{
+ QString ret;
+ foreach(QString k, m_map.keys()) {
+ if (!m_map[k].isValid()) continue;
+ ret += k + ": ";
+ if (m_map[k].canConvert<QStringList>()) ret += "'" + m_map[k].toStringList().join("','") + "'";
+ else if (m_map[k].canConvert<QString>()) ret += "'" + m_map[k].toString() + "'";
+ else ret += "(data)";
+ ret += "\n";
+ }
+ if (ret.endsWith("\n")) ret.chop(1);
+ return ret;
+}
+
+QVariantMap QTestMessage::toMap() const
+{
+ return m_map;
+}
+
+/*
+ \internal
+ Returns the event that was received.
+*/
+
+QString QTestMessage::event() const
+{
+ return m_event;
+}
+
+/*
+ \internal
+ Returns the message number.
+*/
+
+quint16 QTestMessage::msgId() const
+{
+ return m_msg_id;
+}
+
+bool QTestMessage::statusOK() const
+{
+ return m_map.contains("status") && m_map["status"].toString() == "OK";
+}
+
+bool QTestMessage::isNull() const
+{
+ return m_map.isEmpty();
+}
+
+
+// ********************************************************************************
+// ************************* QTestServerSocket ************************************
+// ********************************************************************************
+
+/* !
+ \class QTestServerSocket qtestserversocket.h
+ \inpublicgroup QtUiTestModule
+
+ \brief The QTestServerSocket class provides a TCP-based server.
+
+ This class is a convenience class for accepting incoming TCP
+ connections. You can specify the port or have QTestServerSocket pick
+ one, and listen on just one address or on all the machine's
+ addresses.
+
+ Using the API is very simple: subclass QTestServerSocket, call the
+ constructor of your choice, and implement onNewConnection() to
+ handle new incoming connections. There is nothing more to do.
+
+ (Note that due to lack of support in the underlying APIs,
+ QTestServerSocket cannot accept or reject connections conditionally.)
+
+ \sa QTcpSocket, QTcpServer, QHostAddress, QSocketNotifier
+*/
+
+
+/* !
+ Creates a server socket object, that will serve the given \a port
+ on all the addresses of this host. If \a port is 0, QTestServerSocket
+ will pick a suitable port in a system-dependent manner. Use \a
+ backlog to specify how many pending connections the server can
+ have.
+
+ \warning On Tru64 Unix systems a value of 0 for \a backlog means
+ that you don't accept any connections at all; you should specify a
+ value larger than 0.
+*/
+
+QTestServerSocket::QTestServerSocket( quint16 port, int backlog )
+ : QTcpServer()
+{
+ setMaxPendingConnections( backlog );
+ listen( QHostAddress::Any, port );
+
+ if (this->serverPort() == 0) {
+ qWarning( QString("ERROR: port '%1' is already in use, application is aborted.").arg(port).toAscii() );
+ QApplication::exit(777);
+ }
+}
+
+/* !
+ Destroys the socket.
+
+ This causes any backlogged connections (connections that have
+ reached the host, but not yet been completely set up
+ to be severed.
+
+ Existing connections continue to exist; this only affects the
+ acceptance of new connections.
+*/
+QTestServerSocket::~QTestServerSocket()
+{
+}
+
+/* !
+ Returns true if the construction succeeded; otherwise returns false.
+*/
+bool QTestServerSocket::ok() const
+{
+ return serverPort() > 0;
+}
+
+/* !
+ Returns the port number on which this server socket listens. This
+ is always non-zero; if you specify 0 in the constructor,
+ QTestServerSocket will pick a non-zero port itself. ok() must be true
+ before calling this function.
+
+ \sa address()
+*/
+quint16 QTestServerSocket::port() const
+{
+ return serverPort();
+}
+
+/* !
+ Returns the address on which this object listens, or 0.0.0.0 if
+ this object listens on more than one address. ok() must be true
+ before calling this function.
+
+ \sa port()
+*/
+QString QTestServerSocket::address() const
+{
+ return serverAddress().toString();
+}
+
+void QTestServerSocket::incomingConnection( int socket )
+{
+ onNewConnection( socket );
+}
+
+// ********************************************************************************
+// *************************** QTestProtocol ************************************
+// ********************************************************************************
+
+/*
+ \class QTestProtocol qtestprotocol.h
+ \inpublicgroup QtUiTestModule
+
+ \brief The QTestProtocol class can be used for exchanging messages between separate
+ processes.
+
+ It is intended to be fast but at the same time ultra reliable and robust communication protocol.
+
+ The main function that is used on the sending side is:
+ \code
+ myConnection.postMessage( "My-Event", "My-Message", ptr_to_my_data );
+ \endcode
+
+ On the receiving side the main function is a re-implemented 'processMessage':
+ \code
+ void MyTestConnection::processMessage( QTestMessage *msg )
+ {
+ if (msg->event() == "My-Event") {
+ print( msg->message() );
+ }
+ }
+ \endcode
+*/
+
+#include <stdio.h>
+
+QTestProtocol::QTestProtocol()
+ : QTcpSocket()
+ , tx_msg_id(1)
+ , host()
+ , port(0)
+ , onData_busy(false)
+ , enable_reconnect(false)
+ , reconnect_interval(10000)
+ , connect_timer()
+ , last_data_received(false)
+ , connection_valid(false)
+ , ping_enabled(false)
+ , ping_interval(10000)
+ , ping_timer()
+ , ping_count(0)
+ , ping_timeout_warning_issued(false)
+ , last_send_cmd("")
+ , unique_id()
+ , debug_on(false)
+{
+ static int id1 = qRegisterMetaType<QTestMessage>(); Q_UNUSED(id1);
+ static int id2 = qRegisterMetaType<QTestMessage*>(); Q_UNUSED(id2);
+ static int id3 = qRegisterMetaType<const QTestMessage*>(); Q_UNUSED(id3);
+
+ unique_id = QString("%1").arg(++g_unique_id);
+ if (debug_on) {
+ qDebug() << QString("%1 QTestProtocol::QTestProtocol()").arg(uniqueId()).toLatin1();
+ }
+ cur_message = 0;
+ rx_busy = false;
+
+ QObject::connect( &connect_timer, SIGNAL(timeout()), this, SLOT(connectTimeout()), Qt::DirectConnection );
+
+ QObject::connect( this,SIGNAL(connected()),this,SLOT(onSocketConnected()), Qt::DirectConnection );
+ QObject::connect( this,SIGNAL(disconnected()),this,SLOT(onSocketClosed()), Qt::DirectConnection );
+ QObject::connect( this,SIGNAL(readyRead()),this,SLOT(onData()), Qt::DirectConnection );
+
+ // initialize. Any time is better than no time.
+ rx_timer.start();
+}
+
+/*!
+ Destructs the instance of QTestProtocol.
+*/
+
+QTestProtocol::~QTestProtocol()
+{
+ if (debug_on) {
+ qDebug() << QString("%1 QTestProtocol::~QTestProtocol()").arg(uniqueId()).toLatin1();
+ }
+ enableReconnect( false, 0 );
+
+ // we can't send any more messages so disable pinging
+ enablePing( false );
+
+ // anything that is still in the tx buffer gets lost
+ abort();
+ close();
+
+ while (send_msg_replies.count() > 0)
+ delete send_msg_replies.takeFirst();
+ while (unhandled_msg.count() > 0)
+ delete unhandled_msg.takeFirst();
+}
+
+void QTestProtocol::setSocket( int socket )
+{
+ if (debug_on) {
+ qDebug() << ( QString("%1 QTestProtocol::setSocket(socket=%2)").
+ arg(uniqueId()).
+ arg(socket).toLatin1());
+ }
+ setSocketDescriptor( socket );
+
+ rx_timer.start();
+ testConnection();
+}
+
+void QTestProtocol::enableReconnect( bool enable, uint reconnectInterval )
+{
+ if (debug_on) {
+ qDebug() << ( QString("%1 QTestProtocol::enableReconnect( enable=%2, interval=%3)").
+ arg(uniqueId()).
+ arg(enable).
+ arg(reconnectInterval).toLatin1());
+ }
+ enable_reconnect = enable;
+ reconnect_interval = reconnectInterval;
+}
+
+/*!
+ Opens a socket connection to the specified \a hostname and \a port.
+
+ After a connection is successfully opened the instance will listen for and process
+ incoming commands received from the remote host.
+*/
+void QTestProtocol::connect( const QString& hostname, int port )
+{
+ if (state() == ConnectedState) {
+ if (hostname == this->host && port == this->port)
+ return;
+ disconnect();
+ }
+
+ if (debug_on) {
+ qDebug() << ( QString("%1 QTestProtocol::connect(%2:%3)").
+ arg(uniqueId()).
+ arg(hostname).
+ arg(port).toLatin1());
+ }
+
+ rx_timer.start();
+
+ this->host = hostname;
+ this->port = port;
+
+ reconnect();
+}
+
+void QTestProtocol::disconnect( bool disableReconnect )
+{
+ if (state() == ConnectedState) {
+ if (debug_on) {
+ qDebug() << ( QString("%1 QTestProtocol::disconnect(disableReconnect=%2)").
+ arg(uniqueId()).
+ arg(disableReconnect).toLatin1());
+ }
+ // be polite and tell the other side we are closing
+ postMessage( QTestMessage("QTEST_CLOSING_CONNECTION") );
+
+ // we are closing ourselves, so we don't want to reconnect
+ if (disableReconnect) enable_reconnect = false;
+
+ onSocketClosed();
+ }
+}
+
+bool QTestProtocol::isConnected()
+{
+ return ((state() == ConnectedState) && connection_valid);
+}
+
+bool QTestProtocol::waitForConnected( int timeout )
+{
+ QTime t;
+ t.start();
+ bool ok = false;
+ QTime t2;
+ t2.start();
+ while (t.elapsed() < timeout && !ok) {
+ ok = QTcpSocket::waitForConnected(timeout);
+ if (!ok) {
+ wait(100);
+ if (t2.elapsed() > 500) {
+ t2.start();
+ reconnect();
+ }
+ }
+ }
+
+ if (ok) {
+ if (debug_on) {
+ qDebug() << QString("%1 QTestProtocol::waitForConnected(%2) ...")
+ .arg(uniqueId()).arg(timeout).toLatin1();
+ }
+ t2.start();
+ while (t.elapsed() < timeout && !isConnected()) {
+ wait(10);
+ if (!isConnected() && t2.elapsed() > 500) {
+ postMessage( QTestMessage("QTEST_NEW_CONNECTION") );
+ t2.start();
+ }
+ }
+ ok = isConnected();
+ } else {
+ qDebug() << errorStr();
+ }
+
+ if (debug_on) {
+ qDebug() << QString("%1 QTestProtocol::waitForConnected() ... %2 (%3 ms)").arg(uniqueId()).arg(ok ? "OK" : "FAILED" ).arg(t.elapsed()).toLatin1();
+ }
+ return ok;
+}
+
+/*!
+ \internal
+ Posts (e.g. non blocking) an \a event, \a message and contents of \a fileName to the remote host.
+*/
+
+uint QTestProtocol::postMessage(QTestMessage const &message )
+{
+ if (debug_on && message.event() != "PING" && message.event() != "PONG") {
+ qDebug() << ( QString("%1 QTestProtocol::postMessage(%2)").
+ arg(uniqueId()).
+ arg(message.event()).toLatin1());
+ }
+ if (state() != ConnectedState)
+ return 0;
+ QTestMessage msg(message);
+ msg.m_msg_id = tx_msg_id++;
+ send( msg );
+ return msg.m_msg_id;
+}
+
+void QTestProtocol::onReplyReceived( QTestMessage* /*reply*/ )
+{
+}
+
+/*!
+ \internal
+ Sends an \a event, \a message and \a data to the remote host and waits for up to
+ \a timeout milliseconds for a reply. If a reply is received, the reply's message
+ string is placed in \a reply.
+*/
+bool QTestProtocol::sendMessage( QTestMessage const &message, QTestMessage &reply, int timeout )
+{
+ QTestMessage msg(message);
+ QPointer<QTestProtocol> safeThis(this);
+ bool safeDebugOn(debug_on);
+ QString safeUniqueId(uniqueId());
+
+ last_send_cmd = message.event();
+
+ if (state() == ConnectingState) {
+ wait(4000);
+ }
+
+ if (state() == ConnectedState) {
+ msg.m_msg_id = tx_msg_id++;
+
+ if (debug_on) {
+ qDebug() << ( QString("%1 QTestProtocol::sendMessage(%2) msgid=%3)").
+ arg(uniqueId()).
+ arg(msg.event()).
+ arg(msg.msgId()).
+ toLatin1());
+ }
+
+ send( msg );
+
+// QElapsedTimer t1;
+// QElapsedTimer t2;
+ QTime t1;
+ t1.start();
+ QTime t2;
+ t2.start();
+ bool first_time = true;
+ while ( (state() == ConnectedState) &&
+ (timeout < 0 || t1.elapsed()/*msecsSinceReference()*/ < timeout) ) {
+
+ if (debug_on) {
+ if (first_time || t2.elapsed()/*msecsSinceReference()*/ > 1000) {
+ qDebug() << ( QString("%1 QTestProtocol::sendMessage(%2) ... waiting for reply").
+ arg(uniqueId()).
+ arg(message.event()).toLatin1());
+ t2.start();
+ first_time = false;
+ }
+ }
+
+ waitForSignal(this, SIGNAL(replyReceived()), 500);
+
+ if (!safeThis) {
+ if (message["expectClose"].isValid()) {
+ return true;
+ }
+ if (safeDebugOn) {
+ qDebug() << ( QString("%1 QTestProtocol::sendMessage(%2) ... object deleted unexpectedly").
+ arg(safeUniqueId).
+ arg(message.event()).toLatin1());
+ }
+ reply["status"] = "ERROR: Connection was terminated unexpectedly. This may be caused by an application crash.";
+ reply["_q_inResponseTo"] = QString("%1\n%2").arg(message.event()).arg(message.toString());
+ return false;
+ } else {
+ if (send_msg_replies.count() > 0) {
+ if (debug_on) {
+ qDebug() << ( QString("%1 QTestProtocol::sendMessage(%2) ... check replies").
+ arg(uniqueId()).
+ arg(message.event()).toLatin1());
+ }
+ for (int i=0; i<send_msg_replies.size(); i++) {
+ QTestMessage * possible_reply = send_msg_replies.at(i);
+ if (possible_reply && possible_reply->m_msg_id == msg.m_msg_id) {
+
+ reply = *possible_reply;
+ delete send_msg_replies.takeAt( i );
+
+ if (debug_on) {
+ qDebug() << ( QString("%1 QTestProtocol::sendMessage(%2) ... reply received").
+ arg(uniqueId()).
+ arg(message.event()).toLatin1());
+ }
+
+ onReplyReceived(&reply);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ if (state() != ConnectedState) {
+ reply["status"] = "ERROR: Connection lost. This is likely caused by a crash in the Application Under Test.";
+ reply["_q_inResponseTo"] = QString("%1\n%2").arg(message.event()).arg(message.toString());
+ } else {
+ qDebug() << "ERROR-REPLY-TIMEOUT: " << t1.elapsed()/*msecsSinceReference()*/ << t2.elapsed()/*msecsSinceReference()*/;
+ reply["status"] = "ERROR_REPLY_TIMEOUT";
+ reply["_q_inResponseTo"] = QString("%1\n%2").arg(message.event()).arg(message.toString());
+ }
+ reply["location"] = QString("%1:%2").arg(__FILE__).arg(__LINE__);
+ } else {
+ reply["status"] = "ERROR_NO_CONNECTION";
+ reply["_q_inResponseTo"] = QString("%1\n%2").arg(message.event()).arg(message.toString());
+ reply["location"] = QString("%1:%2").arg(__FILE__).arg(__LINE__);
+ }
+
+ if (debug_on) {
+ qDebug() << ( QString("%1 QTestProtocol::sendMessage(%2) ... done. Status: %3").
+ arg(uniqueId()).
+ arg(message.event()).arg(reply["status"].toString()).toLatin1());
+ }
+
+ return false;
+}
+
+/*!
+ Send the string \a result as a reply to \a originalMsg.
+*/
+
+void QTestProtocol::replyMessage( QTestMessage *originalMsg, QTestMessage const &message )
+{
+ if (debug_on) {
+ qDebug() << ( QString("%1 QTestProtocol::replyMessage(%2)").
+ arg(uniqueId()).
+ arg(originalMsg->event()).toLatin1());
+ }
+
+ QTestMessage msg(message);
+ msg.m_msg_id = originalMsg->msgId();
+ msg.m_event = "@REPLY@";
+ send( msg );
+}
+
+bool QTestProtocol::lastDataReceived()
+{
+ return last_data_received;
+}
+
+QString QTestProtocol::errorStr()
+{
+ QString S = "Connection error: ";
+ switch (error()) {
+ case ConnectionRefusedError: S += "A connection attempt was rejected by the peer"; break;
+ case HostNotFoundError: S += "Host not found"; break;
+ case RemoteHostClosedError: S += "RemoteHostClosedError"; break;
+ case SocketAccessError: S += "SocketAccessError"; break;
+ case SocketResourceError: S += "SocketResourceError"; break;
+ case SocketTimeoutError: S += "SocketTimeoutError"; break;
+ case DatagramTooLargeError: S += "DatagramTooLargeError"; break;
+ case NetworkError: S += "NetworkError"; break;
+ case AddressInUseError: S += "AddressInUseError"; break;
+ case SocketAddressNotAvailableError: S += "SocketAddressNotAvailableError"; break;
+ case UnsupportedSocketOperationError: S += "UnsupportedSocketOperationError"; break;
+ case UnknownSocketError: S += "UnknownSocketError"; break;
+ default: S += " Unknown error";
+ }
+
+ return S;
+}
+
+void QTestProtocol::onConnectionFailed( const QString &reason )
+{
+ emit connectionFailed( this, reason );
+}
+
+void QTestProtocol::testConnection()
+{
+ if (debug_on) {
+ qDebug() << QString("%1 QTestProtocol::testConnection()").arg(uniqueId()).toLatin1();
+ }
+
+ while (send_msg_replies.count() > 0)
+ delete send_msg_replies.takeFirst();
+
+ enablePing( true );
+ postMessage( QTestMessage("QTEST_NEW_CONNECTION") );
+}
+
+void QTestProtocol::send( QTestMessage const &message )
+{
+ QByteArray data;
+ if (!message.m_map.isEmpty()) {
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s << message.m_map;
+ }
+
+ QDataStream tmp(this);
+ sendPreamble( &tmp, message.msgId(), message.event() );
+
+ quint32 len = data.count();
+ tmp << len; // phase 2
+ if (len > 0) {
+ tmp.writeRawData( data.data(), (int)len ); // phase 3
+ }
+
+ tmp << TEST_MSG_END; // phase 4
+
+ flush(); // Force socket to send data now
+}
+
+void QTestProtocol::sendPreamble( QDataStream *ds, quint16 msgId, const QString &event )
+{
+ quint32 len;
+ *ds << TEST_MSG_SIGNATURE; // phase 0
+ *ds << TEST_MSG_VERSION;
+ *ds << msgId;
+
+ len = (event.length() *2) + 4;
+ *ds << len;
+ *ds << event; // phase 1
+}
+
+bool QTestProtocol::receive( QTestMessage *msg, bool &syncError )
+{
+ syncError = false;
+
+ QDataStream stream(this);
+
+ quint8 rx_version;
+ if (msg->m_phase == uint(0)) {
+ msg->m_len = 0;
+ quint32 sig;
+ if (bytesAvailable() >= sizeof( sig ) + sizeof( rx_version ) + sizeof( msg->m_msg_id ) + sizeof( msg->m_len )) {
+ stream >> sig;
+ if (sig != TEST_MSG_SIGNATURE) {
+ qWarning( QString("QTestMessage::receive(), Invalid start signature (0x%1)").arg(sig,8,16).toLatin1() );
+ syncError = true;
+ return false;
+ } else {
+ stream >> rx_version; // FIXME: something should be done to verify the version.
+ stream >> msg->m_msg_id;
+ stream >> msg->m_len;
+ msg->m_phase++;
+ }
+ }
+ }
+
+ if (msg->m_phase == uint(1)) {
+ if (bytesAvailable() >= msg->m_len) {
+ stream >> msg->m_event;
+ msg->m_phase++;
+ }
+ }
+
+ if (msg->m_phase == uint(2)) {
+ if (bytesAvailable() >= sizeof( msg->m_len )) {
+ stream >> msg->m_len;
+ msg->m_phase++;
+ }
+ }
+
+ if (msg->m_phase == uint(3)) {
+ if (msg->m_len > 0) {
+ QByteArray buf;
+ quint32 len = msg->m_len;
+ uint bytes_available = bytesAvailable();
+ if (bytes_available < len)
+ len = bytes_available;
+ buf.resize( len );
+ stream.readRawData( buf.data(), len );
+
+ static QMap<QTestMessage*, QByteArray> data;
+ data[msg].append(buf);
+
+ msg->m_len -= len;
+ if (msg->m_len == 0) {
+ QDataStream s(&data[msg], QIODevice::ReadOnly);
+ s >> msg->m_map;
+ data.remove(msg);
+ msg->m_phase++;
+ // received OK
+ } else {
+ // waiting for more data
+ return false;
+ }
+ } else {
+ msg->m_phase++;
+ }
+ }
+
+ if (msg->m_phase == uint(4)) {
+ quint32 id2;
+ if (bytesAvailable() >= sizeof( id2 )) {
+ stream >> id2;
+ msg->m_phase = 0;
+ if (id2 != TEST_MSG_END) {
+ qWarning( QString("QTestMessage::receive(), Invalid end signature (0x%2)").arg(id2,8,16).toLatin1() );
+ syncError = true;
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool QTestProtocol::rxBusy()
+{
+ return rx_busy;
+}
+
+/*!
+ Reads the remote control connection and responds to received commands.
+*/
+
+void QTestProtocol::onData()
+{
+ if (onData_busy) return;
+ onData_busy = true;
+
+ int sync_error_count = 0;
+ bool msg_received = true;
+ while (msg_received && bytesAvailable() > 0) {
+ msg_received = false;
+ // set the time to now :-)
+ rx_timer.start();
+ ping_timeout_warning_issued = false;
+
+ bool sync_error;
+ if (cur_message == 0)
+ cur_message = new QTestMessage();
+
+ if (receive( cur_message, sync_error )) {
+ msg_received = true;
+ QString last_event = cur_message->event();
+
+ if (debug_on) {
+ qDebug() << ( QString("%1 QTestProtocol::onData(%2) msgid = %3").
+ arg(uniqueId()).
+ arg(last_event).
+ arg(cur_message->m_msg_id).
+ toLatin1());
+ }
+
+ // We received a full message
+ if (last_event == "@REPLY@") {
+ send_msg_replies.append( cur_message ); // add the reply to a list
+ int id = cur_message->m_msg_id;
+ QTestMessage *received_message(cur_message);
+ cur_message = 0; // and make sure we create a new one
+ emit replyReceived( id, received_message );
+ } else if (last_event == "QTEST_NEW_CONNECTION") {
+ postMessage( QTestMessage("QTEST_ACK_CONNECTION") ); // Acknowledge the other side we can receive data
+ } else if (last_event == "QTEST_ACK_CONNECTION") {
+ connection_valid = true; // we don't assume we have a connection until both sides have actually received data from the other side
+ onConnected();
+ connect_timer.stop();
+ } else if (last_event == "QTEST_CLOSING_CONNECTION") {
+ last_data_received = true;
+ QTimer::singleShot( 0, this, SLOT(onSocketClosed()));
+ } else if (last_event == "PONG") {
+ // Do nothing
+ } else if (last_event == "TEST") {
+ if (!debug_on) {
+ // don't show the same information twice
+ qDebug() << QString("%1 Test message received").arg(uniqueId()).toLatin1();
+ }
+ } else if (last_event == "PING") {
+ postMessage( QTestMessage("PONG") );
+ } else {
+ unhandled_msg.append( cur_message ); // add the msg to a list
+ cur_message = 0;
+ QTimer::singleShot(0,this,SLOT(processMessages()));
+ }
+
+ delete cur_message;
+ cur_message = 0;
+ } else {
+ // We didn't receive a full message
+ if (sync_error) {
+ sync_error_count++;
+ if (sync_error_count > 10)
+ return;
+ // receiving garbage messages - nothing can be done but closing the connection and try again.
+ delete cur_message;
+ cur_message = 0;
+ disconnect(!enable_reconnect);
+ reconnect();
+ }
+ // else we are waiting on more fragments to arrive
+ }
+ }
+ onData_busy = false;
+}
+
+void QTestProtocol::processMessages()
+{
+ while (!rx_busy && unhandled_msg.count() > 0) {
+ QTestMessage *tmp = unhandled_msg.takeFirst();
+ if (tmp) {
+ rx_busy = true;
+ processMessage( tmp );
+ delete tmp;
+ rx_busy = false;
+ }
+ }
+}
+
+/*!
+ Signals the instance that the other side has closed the connection.
+*/
+void QTestProtocol::onSocketClosed()
+{
+ if (debug_on) {
+ qDebug() << QString("%1 QTestProtocol::onSocketClosed()").arg(uniqueId()).toLatin1();
+ }
+
+ // we can't send any more messages so disable pinging
+ enablePing( false );
+
+ // anything that is still in the tx buffer gets lost
+ abort();
+
+ close();
+
+ connection_valid = false;
+
+ // if the close was spontaneous and we want to keep the connection alive, we try to reconnect
+ if (enable_reconnect) {
+ if (debug_on) {
+ qDebug() <<
+ QString("%1 QTestProtocol::onSocketClosed() singleshot reconnect in .5 seconds").arg(uniqueId()).toLatin1();
+ }
+ QTimer::singleShot(500,this,SLOT(reconnect()));
+ }
+
+ // tell the world we are closed
+ QTimer::singleShot(0, this, SLOT(emitConnectionClosed()));
+}
+
+/*!
+ Signals the instance that a connection is established with a remote control host.
+*/
+void QTestProtocol::onSocketConnected()
+{
+ connect_timer.stop();
+ if (debug_on) {
+ qDebug() << QString("%1 QTestProtocol::onSocketConnected()").arg(uniqueId()).toLatin1();
+ }
+ testConnection();
+}
+
+void QTestProtocol::reconnect()
+{
+ if (host != "" && state() != ConnectedState) {
+ if (debug_on) {
+ qDebug() << QString("%1 QTestProtocol::reconnect()").arg(uniqueId()).toLatin1();
+ }
+
+ connect_timer.stop();
+ connect_timer.start( CONNECT_TIMEOUT );
+
+ // if we are trying to connect to the local machine, always use 127.0.0.1
+ // (and avoid the need for dns)
+ QString hostname = QHostInfo::localHostName().toUpper();
+ if (hostname == host.toUpper() || hostname.startsWith( host.toUpper() + "." ))
+ host = "127.0.0.1";
+
+ close();
+ connectToHost( host, port );
+ } else {
+ if (host == "") {
+ qWarning( "QTestProtocol::reconnect() FAILED, no host specified" );
+ enable_reconnect = false;
+ }
+ }
+}
+
+void QTestProtocol::connectTimeout()
+{
+ if (debug_on) {
+ qDebug() << QString("%1 QTestProtocol::connectTimeout()").arg(uniqueId()).toLatin1();
+ }
+
+ connect_timer.stop();
+ if (enable_reconnect) {
+ reconnect();
+ } else {
+ onConnectionFailed( errorStr() );
+ }
+}
+
+void QTestProtocol::pingTimeout()
+{
+ if (!ping_enabled) return;
+
+ if (state() == ConnectedState) {
+// int elapsed = rx_timer.msecsSinceReference();
+ int elapsed = rx_timer.elapsed();
+ if (state() == ClosingState) {
+ if (elapsed > 30000) { // no activity for x seconds when we are closing?
+ enablePing( false );
+ if (enable_reconnect) {
+ disconnect();
+ // close has reset the enable_reconnect value, so enable it again
+ enableReconnect( true, reconnect_interval );
+ reconnect();
+ } else {
+ disconnect();
+ }
+ } else if (elapsed > 2000 ) {
+ postMessage( QTestMessage("PING") );
+ }
+ } else {
+ if (elapsed > 10000) {
+ postMessage( QTestMessage("PING") );
+ if (elapsed > 300000) { // no activity for 5 minutes in 'normal' cases??
+ if (!ping_timeout_warning_issued)
+ qWarning( QString("%1 QTestProtocol::pingTimeout() WARNING: Did not receive a msg for %2 ms").arg(uniqueId()).arg(elapsed).toLatin1() );
+ ping_timeout_warning_issued = true;
+ }
+ }
+ }
+ } else {
+ if (enable_reconnect) {
+ // Connection seems to be closed, try to reconnect
+ disconnect();
+
+ // disconnect has reset the enable_reconnect value, so enable it again
+ enableReconnect( true, reconnect_interval );
+ reconnect();
+ }
+ }
+}
+
+void QTestProtocol::emitConnectionClosed()
+{
+ if (debug_on) {
+ qDebug() << ( QString("%1 QTestProtocol::emitConnectionClosed()").arg(uniqueId()).toLatin1());
+ }
+
+ emit replyReceived(); // force sendMessage to quit
+ emit connectionClosed( this );
+}
+
+void QTestProtocol::enablePing( bool enable )
+{
+ if (ping_enabled != enable) {
+ ping_timer.stop();
+ ping_enabled = enable;
+ if (enable) {
+ QObject::connect( &ping_timer, SIGNAL(timeout()), this, SLOT(pingTimeout()), Qt::DirectConnection );
+ ping_timer.start( ping_interval );
+ } else {
+ QObject::disconnect( &ping_timer, SIGNAL(timeout()), this, SLOT(pingTimeout()) );
+ }
+ }
+}
+
+QString QTestProtocol::uniqueId()
+{
+ return QString("%1 %2").arg(unique_id).arg(qApp->applicationName());
+}
+
+void QTestProtocol::enableDebug( bool debugOn )
+{
+ debug_on = debugOn;
+ qDebug() << QString("Debugging is switched %1 for Test Protocol %2").arg(debugOn ? "ON" : "OFF").arg(uniqueId()).toLatin1() ;
+}
+
+void QTestProtocol::disableDebug()
+{
+ debug_on = false;
+ qDebug() << QString("Debugging is switched %1 for Test Protocol %2").arg(debug_on ? "ON" : "OFF").arg(uniqueId()).toLatin1() ;
+}
+
diff --git a/libqsystemtest/qtestprotocol_p.h b/libqsystemtest/qtestprotocol_p.h
new file mode 100644
index 0000000..d2c2dc3
--- /dev/null
+++ b/libqsystemtest/qtestprotocol_p.h
@@ -0,0 +1,207 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef QTESTPROTOCOL_P_H
+#define QTESTPROTOCOL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the QtUiTest API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QTimer>
+#include <QTime>
+#include <QObject>
+#include <QString>
+#include <QFile>
+#include <QVariant>
+#include <QMap>
+#include <QtNetwork/QTcpSocket>
+#include <QtNetwork/QTcpServer>
+#include <QtCore/QElapsedTimer>
+
+#define REMOTE_CONNECT_ERROR 99
+
+class QTestMessage
+{
+public:
+ QTestMessage(QString const &event = QString(), QVariantMap const &map = QVariantMap() );
+ QTestMessage(QString const &event, const QTestMessage &other );
+ QTestMessage(QString const &event, QString const &queryApp, QString const &queryPath = QString() );
+ QTestMessage(const QTestMessage &other);
+ virtual ~QTestMessage();
+
+ QTestMessage& operator=(const QTestMessage &other);
+
+ QString event() const;
+ quint16 msgId() const;
+
+ QVariant const operator[](QString const &key) const;
+ QVariant &operator[](QString const &key);
+ bool contains(QString const &key) const;
+ QList<QString> keys() const;
+
+ QString toString() const;
+ QVariantMap toMap() const;
+
+ bool statusOK() const;
+ bool isNull() const;
+
+protected:
+ uint m_phase;
+ quint32 m_len;
+
+ quint16 m_msg_id;
+ QString m_event;
+
+ QVariantMap m_map;
+
+ friend class QTestProtocol;
+};
+Q_DECLARE_METATYPE( QTestMessage )
+Q_DECLARE_METATYPE( QTestMessage* )
+Q_DECLARE_METATYPE( const QTestMessage* )
+
+class QTestServerSocket : public QTcpServer
+{
+ Q_OBJECT
+public:
+ QTestServerSocket( quint16 port, int backlog = 1 );
+ virtual ~QTestServerSocket();
+
+ bool ok() const;
+ quint16 port() const;
+ QString address() const;
+
+ virtual void onNewConnection( int socket ) = 0;
+
+private:
+ virtual void incomingConnection( int socket );
+};
+
+class QTestProtocol : public QTcpSocket
+{
+ Q_OBJECT
+public:
+
+ QTestProtocol();
+ virtual ~QTestProtocol();
+
+ virtual void setSocket( int socket );
+
+ void enableReconnect( bool enable, uint reconnectInterval = 5000 );
+
+ void connect( const QString& hostname, int port );
+ void disconnect( bool disableReconnect = true );
+ bool isConnected();
+ virtual bool waitForConnected( int timeout = 10000 );
+
+ virtual uint postMessage( QTestMessage const &message );
+
+ virtual bool sendMessage( QTestMessage const &message, QTestMessage &reply, int timeout );
+ virtual void replyMessage( QTestMessage *originalMsg, QTestMessage const &message );
+
+ bool lastDataReceived();
+ bool rxBusy();
+ virtual void onReplyReceived( QTestMessage *reply );
+
+ QString errorStr();
+
+ virtual void onConnectionFailed( const QString &reason );
+ virtual void onConnected() {}; // re-implement in derived class
+
+ QString uniqueId();
+ void enableDebug( bool debugOn );
+
+public slots:
+ void reconnect();
+ void disableDebug();
+
+protected:
+ virtual void processMessage( QTestMessage *msg ) = 0;
+
+ void send( QTestMessage const &message );
+ static void sendPreamble( QDataStream *ds, quint16 msgId, const QString &event );
+
+ bool receive( QTestMessage *msg, bool &syncError );
+
+signals:
+ void connectionClosed( QTestProtocol *socket );
+ void connectionFailed( QTestProtocol *socket, const QString &reason );
+ void replyReceived(int id = -1, QTestMessage const *message = 0);
+ void replyConfirmed();
+
+protected slots:
+ void onData();
+ void onSocketConnected();
+ void onSocketClosed();
+ void connectTimeout();
+ void pingTimeout();
+ void emitConnectionClosed();
+ void processMessages();
+ void testConnection();
+
+private:
+ void enablePing( bool enable );
+
+ quint16 tx_msg_id;
+ QString host;
+ int port;
+ bool onData_busy;
+ bool enable_reconnect;
+ uint reconnect_interval;
+ QTimer connect_timer;
+// QElapsedTimer rx_timer;
+ QTime rx_timer;
+ bool last_data_received;
+ bool rx_busy;
+ bool connection_valid;
+
+ QList<QTestMessage*> send_msg_replies;
+ QList<QTestMessage*> unhandled_msg;
+ QTestMessage *cur_message;
+
+ bool ping_enabled;
+ uint ping_interval;
+ QTimer ping_timer;
+ uint ping_count;
+ bool ping_timeout_warning_issued;
+ QString last_send_cmd;
+ QString unique_id;
+ bool debug_on;
+};
+
+#endif
+
diff --git a/libqsystemtest/gracefulquit.h b/libqsystemtest/qtuitestglobal.h
index 25eeb04..497a886 100644
--- a/libqsystemtest/gracefulquit.h
+++ b/libqsystemtest/qtuitestglobal.h
@@ -39,15 +39,26 @@
**
****************************************************************************/
-#ifndef GRACEFULQUIT_P_H
-#define GRACEFULQUIT_P_H
-#include <qtuitestglobal.h>
+#ifndef QTUITEST_GLOBAL_H
+#define QTUITEST_GLOBAL_H
-class QObject;
-struct QSYSTEMTEST_EXPORT GracefulQuit
-{
- static void install(QObject*);
-};
+#include <QtGlobal>
+
+#if defined(Q_OS_WIN32) || defined(Q_OS_SYMBIAN)
+# if defined(QSYSTEMTEST_TARGET)
+# define QSYSTEMTEST_EXPORT Q_DECL_EXPORT
+# else
+# define QSYSTEMTEST_EXPORT Q_DECL_IMPORT
+# endif
+# if defined(QTUITESTRUNNER_TARGET)
+# define QTUITESTRUNNER_EXPORT Q_DECL_EXPORT
+# else
+# define QTUITESTRUNNER_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define QSYSTEMTEST_EXPORT Q_DECL_EXPORT
+# define QTUITESTRUNNER_EXPORT Q_DECL_EXPORT
+#endif
#endif
diff --git a/libqsystemtest/recordevent_p.h b/libqsystemtest/recordevent_p.h
new file mode 100644
index 0000000..f8ad42a
--- /dev/null
+++ b/libqsystemtest/recordevent_p.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef RECORDEVENT_P_H
+#define RECORDEVENT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the QtUiTest API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QString>
+#include <QVariant>
+
+struct RecordEvent {
+ enum Type {
+ GotFocus,
+ Entered,
+ Selected,
+ Activated,
+ CheckStateChanged,
+ TitleChanged,
+ MessageBoxShown
+ };
+ Type type;
+ QString widget;
+ QString focusWidget;
+ QVariant data;
+};
+
+#define Q_DECLARE_METATYPE_STREAM(TYPE) \
+ QT_BEGIN_NAMESPACE \
+ template <> \
+ struct QMetaTypeId< TYPE > \
+ { \
+ enum { Defined = 1 }; \
+ static int qt_metatype_id() \
+ { \
+ static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
+ if (!metatype_id) { \
+ metatype_id = qRegisterMetaType< TYPE >(#TYPE, \
+ reinterpret_cast< TYPE *>(quintptr(-1))); \
+ qRegisterMetaTypeStreamOperators< TYPE >(#TYPE); \
+ } \
+ return metatype_id; \
+ } \
+ }; \
+ QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE_STREAM(RecordEvent);
+Q_DECLARE_METATYPE_STREAM(QList<RecordEvent>);
+
+inline bool operator==(RecordEvent const& a, RecordEvent const& b)
+{ return a.type == b.type && a.widget == b.widget && a.focusWidget == b.focusWidget && a.data == b.data; }
+
+inline bool operator!=(RecordEvent const& a, RecordEvent const& b)
+{ return !(a == b); }
+
+inline QDataStream &operator<<(QDataStream &out, const RecordEvent &re)
+{ return (out << static_cast<int>(re.type) << re.widget << re.focusWidget << re.data); }
+
+inline QDataStream &operator>>(QDataStream &in, RecordEvent &re)
+{
+ int reType;
+ QDataStream &ret = (in >> reType >> re.widget >> re.focusWidget >> re.data);
+ re.type = static_cast<RecordEvent::Type>(reType);
+ return ret;
+}
+
+inline QDataStream &operator<<(QDataStream &out, const QList<RecordEvent> &l)
+{
+ out << l.count();
+ foreach (RecordEvent re, l) {
+ out << re;
+ }
+ return out;
+}
+inline QDataStream &operator>>(QDataStream &in, QList<RecordEvent> &l)
+{
+ int count = 0;
+ in >> count;
+ RecordEvent re;
+ for (int i = 0; i < count; ++i) {
+ in >> re;
+ l << re;
+ }
+ return in;
+}
+
+#endif
+