summaryrefslogtreecommitdiffstats
path: root/src/remote/daemon/qtuitestslave.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/remote/daemon/qtuitestslave.cpp')
-rw-r--r--src/remote/daemon/qtuitestslave.cpp600
1 files changed, 600 insertions, 0 deletions
diff --git a/src/remote/daemon/qtuitestslave.cpp b/src/remote/daemon/qtuitestslave.cpp
new file mode 100644
index 0000000..653b003
--- /dev/null
+++ b/src/remote/daemon/qtuitestslave.cpp
@@ -0,0 +1,600 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of QtSystemTest.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "appmanager.h"
+#include "qtuitestslave.h"
+#include "qtuitestmaster_p.h"
+#include "qtuitest_debug.h"
+#include "qtuitestdsettings.h"
+
+#include <QDir>
+#include <QVariant>
+#include <QMetaProperty>
+#include <QMetaObject>
+#include <QMetaType>
+#include <QProcess>
+#include <QCoreApplication>
+#include <QSettings>
+#include <QPointer>
+
+class QtUiTestSlavePrivate : public QObject
+{
+Q_OBJECT
+public:
+ QtUiTestSlavePrivate(QtUiTestSlave *parent)
+ : p(parent)
+ {
+ AppManager::instance();
+ }
+
+ QString recursiveDelete(const QString&) const;
+
+ QtUiTestSlave *p;
+ AppManager *appManager()
+ {
+ return AppManager::instance();
+ }
+
+public slots:
+
+ QstMessage currentApplication (const QstMessage&);
+ QstMessage homeApplication (const QstMessage&);
+ QstMessage setApplication (const QstMessage&);
+ QstMessage isValidApplication (const QstMessage&);
+ QstMessage getApplications (const QstMessage&);
+
+ QstMessage runProcess (const QstMessage&);
+ QstMessage getSetting (const QstMessage&);
+ QstMessage setSetting (const QstMessage&);
+ QstMessage putFile (const QstMessage&);
+ QstMessage getFile (const QstMessage&);
+ QstMessage deletePath (const QstMessage&);
+ QstMessage getDirectoryEntries(const QstMessage&);
+ QstMessage checkOS (const QstMessage&);
+ QstMessage deviceConfig (const QstMessage&);
+ QstMessage systemTime (const QstMessage&);
+};
+
+#include "qtuitestslave.moc"
+
+QtUiTestSlave::QtUiTestSlave()
+ : QstProtocol()
+ , d(new QtUiTestSlavePrivate(this))
+{
+ setAutoUnpack(false);
+ QObject::connect(static_cast<QstProtocol*>(this), SIGNAL(connectionClosed(QstProtocol*)), this, SLOT(onConnectionClosed()));
+}
+
+QtUiTestSlave::~QtUiTestSlave()
+{
+ delete d;
+ disconnect();
+}
+
+void QtUiTestSlave::onConnected()
+{
+ QstProtocol::onConnected();
+ QstMessage msg("AUT_CONNECT");
+ msg["qtVersion"] = QT_VERSION_STR;
+ msg["autVersion"] = QTUITEST_VERSION_STR;
+ msg["autTime"] = QTime::currentTime();
+ d->appManager()->testrunnerConnected(this);
+ postMessage( msg );
+}
+
+void QtUiTestSlave::onConnectionClosed()
+{
+ qLog() << "Connection to qtuitestrunner has closed";
+ d->appManager()->testrunnerDisconnected(this);
+}
+
+QstMessage QtUiTestSlavePrivate::currentApplication(const QstMessage &message)
+{
+ Q_UNUSED(message);
+ QstMessage reply;
+
+ reply[QLatin1String("currentApplication")] = appManager()->currentApplication();
+ return reply;
+}
+
+QstMessage QtUiTestSlavePrivate::homeApplication(const QstMessage &message)
+{
+ Q_UNUSED(message);
+ QstMessage reply;
+
+ reply[QLatin1String("homeApplication")] = appManager()->homeApplication();
+ return reply;
+}
+
+QstMessage QtUiTestSlavePrivate::setApplication(const QstMessage &message)
+{
+ QstMessage reply;
+ QString appId = message.context();
+
+ if (appId.isEmpty()) {
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_MISSING_PARAMETERS"));
+ }
+
+ bool ok = appManager()->setApplication(appId);
+ if (!ok) {
+ return MESSAGE_ERROR(reply, QLatin1String("Unable to set application"));
+ }
+ return reply;
+}
+
+QstMessage QtUiTestSlavePrivate::isValidApplication(const QstMessage &message)
+{
+ QstMessage reply;
+ QString appId = message.context();
+
+ if (appId.isEmpty()) {
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_MISSING_PARAMETERS"));
+ }
+
+ bool ok = appManager()->getApplications().contains(appId);
+ reply[QLatin1String("isValidApplication")] = ok;
+ return reply;
+}
+
+QstMessage QtUiTestSlavePrivate::getApplications(const QstMessage &message)
+{
+ Q_UNUSED(message);
+ QstMessage reply;
+ QString appName = message["name"].toString();
+
+ reply[QLatin1String("getApplications")] = appManager()->getApplications(appName);
+ return reply;
+}
+
+QstMessage QtUiTestSlavePrivate::runProcess(const QstMessage &message)
+{
+ QstMessage reply;
+
+ QVariantMap cmd = message["cmd"].toMap();
+ if (cmd.isEmpty()) {
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_MISSING_PARAMETERS"));
+ }
+
+ QString program = cmd["program"].toString();
+ QStringList arguments = cmd["arguments"].toStringList();
+ int timeout = cmd["timeout"].toInt();
+ QString directory = cmd["directory"].toString();
+ QVariantMap environment = cmd["environment"].toMap();
+ QVariantMap appendEnvironment = cmd["appendEnvironment"].toMap();
+ bool detached = cmd["detached"].toBool();
+
+ QProcessEnvironment env;
+ if (!environment.isEmpty()) {
+ foreach (const QString &key, environment.keys()) {
+ env.insert(key, environment[key].toString());
+ }
+ }
+ if (!appendEnvironment.isEmpty()) {
+ if (env.isEmpty()) {
+ env = QProcessEnvironment::systemEnvironment();
+ }
+ foreach (const QString &key, appendEnvironment.keys()) {
+ env.insert(key, appendEnvironment[key].toString());
+ }
+ }
+
+ bool ok = true;
+ QString status;
+
+ if (detached) {
+ // Run command in background
+ qint64 pid = 0;
+ if (QProcess::startDetached(program, arguments, directory, &pid)) {
+ status = QLatin1String("Started");
+ reply["pid"] = pid;
+ } else {
+ status = QLatin1String("FailedToStart");
+ ok = false;
+ }
+ } else {
+ // Run command and wait for completion
+ QString stdout;
+ QString stderr;
+ int exitCode = 0;
+ QProcess *process = new QProcess(this);
+
+ if (!env.isEmpty()) {
+ process->setProcessEnvironment(env);
+ }
+ if (!directory.isEmpty()) {
+ process->setWorkingDirectory(directory);
+ }
+
+ process->start(program, arguments);
+ while (process->state() == QProcess::Starting) {
+ QCoreApplication::processEvents();
+ }
+
+ if (process->error() == QProcess::FailedToStart) {
+ status = QLatin1String("FailedToStart");
+ ok = false;
+ } else {
+ QTime t;
+ t.start();
+ while (process->state() == QProcess::Running && t.elapsed() < timeout) {
+ QCoreApplication::processEvents();
+ }
+
+ stdout += process->readAllStandardOutput();
+ stderr += process->readAllStandardError();
+ if (process->state() == QProcess::Running) {
+ status = QLatin1String("TimedOut");
+ } else if (process->exitStatus() != QProcess::NormalExit) {
+ status = QLatin1String("Crashed");
+ ok = false;
+ } else {
+ status = QLatin1String("Finished");
+ }
+ }
+
+ exitCode = process->exitCode();
+ delete process;
+
+ reply["stdout"] = stdout;
+ reply["stderr"] = stderr;
+ reply["exitCode"] = exitCode;
+ }
+ qLog() << "Command status:" << status;
+ reply["cmdStatus"] = status;
+
+ if (!ok) {
+ return MESSAGE_ERROR(reply, QString("Error: Command failed to run (%1)").arg(status));
+ }
+ return reply;
+}
+
+QstMessage QtUiTestSlavePrivate::getSetting(const QstMessage &message)
+{
+ QstMessage reply;
+ QString org = message[QLatin1String("org")].toString();
+ QString app = message[QLatin1String("app")].toString();
+ QString path = message[QLatin1String("path")].toString();
+ QString group = message[QLatin1String("group")].toString();
+ QString key = message[QLatin1String("key")].toString();
+ path = p->processEnvironment(path);
+ if (group.isEmpty() || key.isEmpty()) {
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_MISSING_PARAMETERS"));
+ }
+ if (!path.isEmpty() && (!org.isEmpty() || !app.isEmpty())) {
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_BAD_PARAMETERS"));
+ } else if (path.isEmpty() && org.isEmpty()) {
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_MISSING_PARAMETERS"));
+ }
+
+ QSettings *settings = 0;
+ if (!path.isEmpty()) settings = new QSettings(path, QSettings::NativeFormat);
+ else settings = new QSettings(org, app);
+
+ settings->beginGroup(group);
+ reply[QLatin1String("getSetting")] = settings->value(key);
+ delete settings;
+
+ return reply;
+}
+
+QstMessage QtUiTestSlavePrivate::setSetting(const QstMessage &message)
+{
+ QstMessage reply;
+ QString org = message[QLatin1String("org")].toString();
+ QString app = message[QLatin1String("app")].toString();
+ QString path = message[QLatin1String("path")].toString();
+ QString group = message[QLatin1String("group")].toString();
+ QString key = message[QLatin1String("key")].toString();
+ path = p->processEnvironment(path);
+ if (key.isEmpty()) {
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_MISSING_PARAMETERS"));
+ }
+ if (!path.isEmpty() && (!org.isEmpty() || !app.isEmpty())) {
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_BAD_PARAMETERS"));
+ } else if (path.isEmpty() && org.isEmpty()) {
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_MISSING_PARAMETERS"));
+ }
+
+ QSettings *settings = 0;
+ if (!path.isEmpty()) settings = new QSettings(path, QSettings::NativeFormat);
+ else settings = new QSettings(org, app);
+
+ if (!group.isEmpty())
+ settings->beginGroup(group);
+ settings->setValue(key, message[QLatin1String("value")]);
+ delete settings;
+
+ return reply;
+}
+
+QstMessage QtUiTestSlavePrivate::putFile(const QstMessage &message)
+{
+ QstMessage reply;
+ QString path = message[QLatin1String("path")].toString();
+ path = p->processEnvironment(path);
+ QByteArray data = message[QLatin1String("data")].toByteArray();
+
+ if (path.isEmpty())
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_MISSING_PARAMETERS"));
+
+ {
+ QFileInfo info = QFileInfo(path);
+ QDir dir = info.dir();
+ if (info.exists()) {
+ dir.remove(info.fileName());
+ } else if (!dir.exists() && !QDir(QLatin1String("/")).mkpath(dir.absolutePath())) {
+ return MESSAGE_ERROR(reply, QLatin1String("Could not create path '") + dir.absolutePath() + QLatin1String("'"));
+ }
+ }
+
+ QFile f(path);
+ if (f.open(QIODevice::WriteOnly)) {
+ if (message[QLatin1String("permissions")].isValid() && !f.setPermissions(static_cast<QFile::Permissions>(message[QLatin1String("permissions")].toInt()))) {
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_FILE_PERMISSIONS"));
+ }
+ QDataStream ds(&f);
+ if (data.constData()) {
+ quint64 bytesWritten = ds.writeRawData(data.constData(), data.size());
+ if ((qint64)bytesWritten == data.size())
+ return reply;
+ else {
+ reply[QLatin1String("warning")] = QString("Wrote %1 byte(s), expected %2").arg(bytesWritten).arg(data.size());
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_TRUNCATED"));
+ }
+ }
+ // It's OK for data to be empty, then we create an empty file.
+ return reply;
+ }
+
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_FILE_OPEN"));
+}
+
+QstMessage QtUiTestSlavePrivate::getFile(const QstMessage &message)
+{
+ QstMessage reply;
+ QString path = message[QLatin1String("path")].toString();
+ path = p->processEnvironment(path);
+
+ if (path.isEmpty())
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_MISSING_PARAMETERS"));
+
+ QFile f(path);
+ if (!f.exists()) return MESSAGE_ERROR(reply, QLatin1String("ERROR_FILE_NOT_EXIST"));
+ if (f.open(QIODevice::ReadOnly)) {
+ QByteArray data;
+ QDataStream ds(&f);
+ data.resize(f.size());
+ quint64 bytesRead = ds.readRawData(data.data(), f.size());
+ reply[QLatin1String("getFile")] = data;
+ if ((qint64)bytesRead == f.size())
+ return reply;
+ else {
+ reply[QLatin1String("warning")] = QString("Read %1 byte(s), expected %2").arg(bytesRead).arg(data.size());
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_TRUNCATED"));
+ }
+ }
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_FILE_OPEN"));
+}
+
+QstMessage QtUiTestSlavePrivate::getDirectoryEntries(const QstMessage &message)
+{
+ QstMessage reply;
+ QString path = message[QLatin1String("path")].toString();
+ path = p->processEnvironment(path);
+
+ if (path.isEmpty()) {
+ return MESSAGE_ERROR(reply, QLatin1String("ERROR_MISSING_PARAMETERS"));
+ }
+
+ QDir d(path);
+ if (!d.exists()) {
+ reply[QLatin1String("getDirectoryEntries")] = QStringList();
+ return reply;
+ }
+
+ QDir::Filters filters;
+ {
+ int filters_int = message[QLatin1String("filters")].toInt();
+ filters = QFlag(filters_int);
+ }
+
+ QStringList list = d.entryList(filters);
+ reply[QLatin1String("getDirectoryEntries")] = list;
+ return reply;
+}
+
+QstMessage QtUiTestSlavePrivate::deletePath(const QstMessage &message)
+{
+ QstMessage reply;
+ QString path = message[QLatin1String("path")].toString();
+ path = p->processEnvironment(path);
+ QString deleteResult = recursiveDelete(path);
+ if (deleteResult != QLatin1String("OK")) {
+ return MESSAGE_ERROR(reply, deleteResult);
+ }
+ return reply;
+}
+
+void QtUiTestSlave::processMessage( QstMessage *msg )
+{
+ qLog() << "QtUiTestSlave::processMessage" << msg->event();
+
+ QstMessage reply;
+ QString methodSignature = msg->event() + QLatin1String("(QstMessage)");
+
+ if (d->metaObject()->indexOfMethod(methodSignature.toLatin1().constData()) == -1) {
+ if (!d->appManager()->isConnected()) {
+ (void)MESSAGE_ERROR(reply, QLatin1String("ERROR: Not connected to test application"));
+ } else if (!d->appManager()->forwardMessage(msg, this)) {
+ (void)MESSAGE_ERROR(reply, QLatin1String("ERROR: Could not forward message '") + msg->event() + QLatin1String("'"));
+ }
+
+ } else {
+ msg->unpack();
+ if (!QMetaObject::invokeMethod(d, msg->event().toLatin1().constData(), Qt::DirectConnection,
+ Q_RETURN_ARG(QstMessage, reply),
+ Q_ARG(QstMessage, *msg))) {
+ (void)MESSAGE_ERROR(reply, QLatin1String("ERROR: Failed to invoke method '") + msg->event() + QLatin1String("'"));
+ }
+ if (reply.state() != QstMessage::MessageError) {
+ reply.setState(QstMessage::MessageSuccess);
+ }
+ }
+
+ replyMessage(msg, reply);
+}
+
+QString QtUiTestSlave::processEnvironment( const QString& in ) const
+{
+ struct SystemEnvironment {
+ static QMap<QString,QString> get() {
+ QMap<QString,QString> ret;
+ QStringList env = QProcess::systemEnvironment();
+ foreach (const QString &str, env) {
+ if (str.contains(QLatin1Char('='))) {
+ ret[str.left(str.indexOf(QLatin1Char('='))).toUpper()] = str.mid(str.indexOf(QLatin1Char('=')) + 1);
+ }
+ }
+ return ret;
+ }
+ };
+ static const QMap<QString,QString> environment( SystemEnvironment::get() );
+
+ QString out;
+ static QRegExp re("\\$[{(]?([A-Za-z0-9_]+)[})]?");
+ int offset = 0;
+ while (true) {
+ int index = re.indexIn(in, offset);
+ if (-1 == index) {
+ out += in.mid(offset);
+ break;
+ }
+ out += in.mid(offset, index - offset);
+ out += environment.value(re.cap(1).toUpper());
+ offset += re.matchedLength();
+ }
+
+ return out;
+}
+
+QString QtUiTestSlavePrivate::recursiveDelete( const QString &path ) const
+{
+ if (path.isEmpty()) return QLatin1String("ERROR_CANT_DELETE_EMPTY_PATH");
+
+ QFileInfo i(path);
+ if (!i.exists()) return QLatin1String("OK");
+ if (!i.isDir() || i.isSymLink()) {
+ if (!i.dir().remove(i.fileName())) {
+ return QLatin1String("ERROR_CANT_DELETE_FILE_") + path;
+ } else {
+ return QLatin1String("OK");
+ }
+ }
+
+ QDir dir(path);
+
+ QStringList children = dir.entryList( QDir::AllEntries | QDir::System | QDir::Hidden | QDir::NoDotAndDotDot );
+
+ QString res;
+ foreach (QString child, children) {
+ res = recursiveDelete( dir.absolutePath() + QLatin1String("/") + child );
+ if (res != QLatin1String("OK") ) return res;
+ }
+
+ QString dirName = dir.dirName();
+ dir.cdUp();
+
+ if (!dir.rmdir(dirName)) {
+ return QLatin1String("ERROR_CANT_DELETE_DIRECTORY_") + path;
+ }
+ return QLatin1String("OK");
+}
+
+QstMessage QtUiTestSlavePrivate::checkOS( const QstMessage &message )
+{
+ QstMessage reply;
+ QString os = message[QLatin1String("os")].toString().toUpper();
+
+ bool result = false;
+
+#ifdef Q_OS_UNIX
+ if (os == QLatin1String("UNIX")) result = true;
+#endif
+#ifdef Q_OS_LINUX
+ if (os == QLatin1String("LINUX")) result = true;
+#endif
+#ifdef Q_OS_MAEMO
+ if (os == QLatin1String("MAEMO")) result = true;
+#endif
+#ifdef Q_OS_MAC
+ if (os == QLatin1String("MAC")) result = true;
+#endif
+#ifdef Q_OS_WIN32
+ if (os == QLatin1String("WIN32")) result = true;
+#endif
+#ifdef Q_OS_WINCE
+ if (os == QLatin1String("WINCE")) result = true;
+#endif
+#ifdef Q_OS_SYMBIAN
+ if (os == QLatin1String("SYMBIAN")) result = true;
+#endif
+
+ reply[QLatin1String("checkOS")] = result;
+ return reply;
+}
+
+QstMessage QtUiTestSlavePrivate::deviceConfig( const QstMessage &message )
+{
+ Q_UNUSED(message);
+ QstMessage reply;
+ reply[QLatin1String("deviceConfig")] = QtUiTestDSettings::instance()->deviceConfig();
+ return reply;
+}
+
+QstMessage QtUiTestSlavePrivate::systemTime(const QstMessage &message)
+{
+ Q_UNUSED(message);
+ QstMessage reply;
+
+ reply["systemTime"] = QDateTime::currentDateTime().toString(Qt::ISODate);
+ return reply;
+}