diff options
Diffstat (limited to 'src/qtestlib/wince')
29 files changed, 4864 insertions, 0 deletions
diff --git a/src/qtestlib/wince/cetcpsync/cetcpsync.pro b/src/qtestlib/wince/cetcpsync/cetcpsync.pro new file mode 100644 index 000000000..d1d7c999a --- /dev/null +++ b/src/qtestlib/wince/cetcpsync/cetcpsync.pro @@ -0,0 +1,22 @@ +TARGET = cetcpsync +DESTDIR = ../../../../bin +CONFIG += console +CONFIG -= app_bundle +QT += network +QT -= gui +TEMPLATE = app + +build_all:!build_pass { + CONFIG -= build_all + CONFIG += release +} + +INCLUDEPATH += ../cetcpsyncserver + +SOURCES += main.cpp \ + remoteconnection.cpp \ + qtcesterconnection.cpp + +HEADERS += \ + remoteconnection.h \ + qtcesterconnection.h diff --git a/src/qtestlib/wince/cetcpsync/main.cpp b/src/qtestlib/wince/cetcpsync/main.cpp new file mode 100644 index 000000000..4d209e356 --- /dev/null +++ b/src/qtestlib/wince/cetcpsync/main.cpp @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <iostream> +#include "qtcesterconnection.h" + +using namespace std; + +static void showUsage() +{ + cout << "cetcpsync is meant to be used by cetest internally." << endl + << "For usage instructions remoteconnection.h could be useful." << endl; +} + +const int debugLevel = 0; +void debugOutput(const QString& text, int level) +{ + if (level <= debugLevel) + cout << qPrintable(text) << endl; +} + +class Exception +{ +public: + Exception(const QString& msg = QString()) + : m_message(msg) + {} + + QString message() { return m_message; } + +protected: + QString m_message; +}; + +class TooFewParametersException : public Exception +{ +public: + TooFewParametersException(const QLatin1String& cmd, int expectedParameterCount) + { + m_message = QLatin1String("Command ") + cmd + QLatin1String(" needs at least "); + m_message.append(QString::number(expectedParameterCount)); + m_message.append(QLatin1String(" parameters.")); + } +}; + +static void fileTimeFromString(FILETIME& ft, const QString& str) +{ + int idx = str.indexOf("*"); + if (idx <= 0) + return; + ft.dwLowDateTime = str.left(idx).toULong(); + ft.dwHighDateTime = str.mid(idx+1).toULong(); +} + +static QString fileTimeToString(FILETIME& ft) +{ + return QString::number(ft.dwLowDateTime) + "*" + QString::number(ft.dwHighDateTime); +} + +static int execCommand(const QLatin1String& cmd, int argc, char* argv[]) +{ + int retval = 0; + bool success = true; + QtCesterConnection connection; + if (cmd == "copyFileToDevice") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyFileToDevice(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "copyDirectoryToDevice") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyDirectoryToDevice(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "copyFileFromDevice") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyFileFromDevice(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "copyDirectoryFromDevice") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyDirectoryFromDevice(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "timeStampForLocalFileTime") { + if (argc < 1) + throw TooFewParametersException(cmd, 1); + FILETIME ft; + fileTimeFromString(ft, argv[0]); + success = connection.timeStampForLocalFileTime(&ft); + if (success) + cout << qPrintable(fileTimeToString(ft)); + } else if (cmd == "fileCreationTime") { + if (argc < 1) + throw TooFewParametersException(cmd, 1); + FILETIME ft; + success = connection.fileCreationTime(argv[0], &ft); + if (success) + cout << qPrintable(fileTimeToString(ft)); + } else if (cmd == "copyFile") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyFile(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "copyDirectory") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyDirectory(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "deleteFile") { + if (argc < 1) + throw TooFewParametersException(cmd, 1); + success = connection.deleteFile(argv[0]); + } else if (cmd == "deleteDirectory") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.deleteDirectory(argv[0], argv[1] == "true", argv[2] == "true"); + } else if (cmd == "moveFile") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.moveFile(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "moveDirectory") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.moveDirectory(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "createDirectory") { + if (argc < 2) + throw TooFewParametersException(cmd, 2); + success = connection.createDirectory(argv[0], argv[1] == "true"); + } else if (cmd == "execute") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + int timeout = QString(argv[2]).toInt(); + success = connection.execute(argv[0], argv[1], timeout, &retval); + } else if (cmd == "noop") { + // do nothing :) + success = true; + } else { + throw Exception("unknown command"); + } + + return success ? retval : 1; +} + +int main(int argc, char *argv[]) +{ + if (argc <= 1) { + showUsage(); + return 0; + } + + QLatin1String param(argv[1]); + int result = 1; + try { + result = execCommand(param, argc - 2, argv + 2); + } catch (Exception e) { + cerr << "Error: " << qPrintable(e.message()); + } + return result; +} diff --git a/src/qtestlib/wince/cetcpsync/qtcesterconnection.cpp b/src/qtestlib/wince/cetcpsync/qtcesterconnection.cpp new file mode 100644 index 000000000..5d358e80b --- /dev/null +++ b/src/qtestlib/wince/cetcpsync/qtcesterconnection.cpp @@ -0,0 +1,552 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qtcesterconnection.h" +#include <transfer_global.h> +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtNetwork/QTcpSocket> +#include <QtNetwork/QHostAddress> + +extern void debugOutput(const QString& text, int level); + +#pragma warning(disable:4996) + +#define END_ERROR(s, a) \ + if(a) qDebug() << a; \ + _freeSocket(s); \ + return false; + +QtCesterConnection::QtCesterConnection() + : AbstractRemoteConnection() +{ +} + +QtCesterConnection::~QtCesterConnection() +{ +} + +bool QtCesterConnection::connect(QVariantList&) +{ + // We connect with each command, so this is always true + // The command itself will fail then + connected = true; + return true; +} + +void QtCesterConnection::disconnect() +{ + connected = false; +} + +bool QtCesterConnection::isConnected() const +{ + return connected; +} + +bool QtCesterConnection::copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists) +{ + debugOutput( qPrintable(QString::fromLatin1("Copy File: %1 -> %2").arg(localSource).arg(deviceDest)),0); + QFile localFile(localSource); + QFileInfo info(localSource); + if (!localFile.exists() || !localFile.open(QIODevice::ReadOnly)) { + qDebug() << "Could not open File!"; + return false; + } + + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_CREATE_FILE)) { + END_ERROR(socket, "Could not initialized command"); + } + + CreateFileOptions option; + strcpy(option.fileName, qPrintable(deviceDest)); +#ifdef Q_OS_WIN + // Copy FileTime for update verification + FILETIME creationTime, accessTime, writeTime; + HANDLE localHandle = CreateFile(localSource.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + if (localHandle != INVALID_HANDLE_VALUE) { + if (GetFileTime(localHandle, &creationTime, &accessTime, &writeTime)) { + LocalFileTimeToFileTime(&writeTime, &writeTime); + option.fileTime = writeTime; + } + CloseHandle(localHandle); + } + DWORD attributes = GetFileAttributes(localSource.utf16()); + if (attributes != -1 ) + option.fileAttributes = attributes; +#endif + option.fileSize = info.size(); + option.overwriteExisting = !failIfExists; + + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send options..."); + } + + if (!_checkResult(socket)) { + END_ERROR(socket, "Server did not accept configuration"); + } + + int bytesWritten = 0; + const int bufferSize = 1024; + QByteArray data; + while (bytesWritten < option.fileSize) { + data = localFile.read(bufferSize); + bytesWritten += data.size(); +#ifdef Q_OS_WIN + wprintf( L"%s -> %s (%d / %d) %d %%\r", localSource.utf16() , deviceDest.utf16(), + bytesWritten , option.fileSize, (100*bytesWritten)/option.fileSize ); +#endif + if (!_sendData(socket, data.constData(), data.size())) { + END_ERROR(socket, "Error during file transfer"); + } + if (!_checkResult(socket)) { + END_ERROR(socket, "Got some strange result"); + } + } +#ifdef Q_OS_WIN + wprintf( L"\n"); // We should jump to next line... +#endif + if (bytesWritten != option.fileSize) { + END_ERROR(socket, "Did not send sufficient data"); + } + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive) +{ + QTcpSocket* socket = NULL; + QFileInfo info(localSource); + if (!info.exists() || !info.isDir()) { + END_ERROR(socket, "Input directory invalid"); + } + + createDirectory(deviceDest, true); + QDir dir(localSource); + QFileInfoList list = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + foreach(QFileInfo item, list) { + QString targetName = deviceDest + QLatin1String("\\") + item.fileName(); + if (item.isDir()) { + if (recursive) { + if (!copyDirectoryToDevice(item.absoluteFilePath() , targetName, recursive)) + return false; + } + } else { + if (!copyFileToDevice(item.absoluteFilePath(), targetName)) + return false; + } + } + return true; +} + +bool QtCesterConnection::copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists) +{ + QFile targetFile(localDest); + QTcpSocket* socket = 0; + if (targetFile.exists() && failIfExists) { + END_ERROR(socket, "Local file not supposed to be overwritten"); + } + + if (!targetFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + END_ERROR(socket, "Could not open local file for writing"); + } + + if (!_initCommand(socket, COMMAND_READ_FILE)) { + END_ERROR(socket, "Could not establish connection"); + } + + ReadFileOptions option; + strcpy(option.fileName, qPrintable(deviceSource)); + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send options"); + } + + QByteArray data; + if (!_receiveData(socket, data)) { + END_ERROR(socket, "Did not receive any data"); + } + + ReadFileReply* reply = (ReadFileReply*) data.data(); + if (!reply->fileValid) { + END_ERROR(socket, "Requested file invalid"); + } + + int fileSize = reply->fileSize; + int currentSize = 0; + // ### TODO: make a little bit more error-prone + do { + _sendData(socket, COMMAND_SUCCESS, strlen(COMMAND_SUCCESS)); + _receiveData(socket, data); + currentSize += data.size(); + targetFile.write(data); + } while(currentSize < fileSize); + + _freeSocket(socket); + targetFile.close(); + return true; +} + +bool QtCesterConnection::copyDirectoryFromDevice(const QString& /*deviceSource*/ + , const QString& /*localDest*/ + , bool /*recursive*/) +{ + qDebug() << "To be implemented!! Should not be needed for autotest system"; + exit(-1); + return false; +} + +bool QtCesterConnection::copyFile(const QString &srcFile, const QString &destFile, bool failIfExists) +{ + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_COPY_FILE)) { + END_ERROR(socket, "Could not establish connection for copy"); + } + + CopyFileOptions option; + strcpy(option.from, qPrintable(srcFile)); + strcpy(option.to, qPrintable(destFile)); + option.overwriteExisting = !failIfExists; + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send copy options"); + } + + if (!_checkResult(socket)) { + END_ERROR(socket, "Copy failed"); + } + + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::copyDirectory(const QString &srcDirectory, const QString &destDirectory, + bool recursive) +{ + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_COPY_DIRECTORY)) { + END_ERROR(socket, "Could not establish connection for dir copy"); + } + + CopyDirectoryOptions option; + strcpy(option.from, qPrintable(srcDirectory)); + strcpy(option.to, qPrintable(destDirectory)); + option.recursive = recursive; + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send dir copy options"); + } + + if (!_checkResult(socket)) { + END_ERROR(socket, "Dir Copy failed"); + } + + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::deleteFile(const QString &fileName) +{ + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_DELETE_FILE)) { + END_ERROR(socket, "Could not establish connection for file deletion"); + } + + DeleteFileOptions option; + strcpy(option.fileName, qPrintable(fileName)); + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send file options"); + } + + if (!_checkResult(socket)) { + //END_ERROR(socket, "File Deletion failed"); + // This is actually not an error so ignore it. + } + + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::deleteDirectory(const QString &directory, bool recursive, bool failIfContentExists) +{ + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_DELETE_DIRECTORY)) { + END_ERROR(socket, "Could not establish connection for dir deletion"); + } + + DeleteDirectoryOptions option; + strcpy(option.dirName, qPrintable(directory)); + option.recursive = recursive; + option.failIfContentExists = failIfContentExists; + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send dir options"); + } + + if (!_checkResult(socket)) { + // we do not write an error as this will fail a lot on recursive. + END_ERROR(socket, 0); + } + + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::execute(QString program, + QString arguments, + int timeout, + int *returnValue) +{ + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_EXECUTE)) { + END_ERROR(socket, "Could not establish connection for dir deletion"); + } + + ExecuteOptions options; + strcpy(options.appName, qPrintable(program)); + QStringList argList = arguments.split(QLatin1Char(' ')); + options.argumentsCount = qMin(argList.size(), MAX_ARGUMENTS); + options.waitForFinished = true; + options.timeout = timeout; + if (!_sendData(socket, (char*) &options, sizeof(options))) { + END_ERROR(socket, "Could not send dir options"); + } + if (!_checkResult(socket)) { + END_ERROR(socket, "Did not receive an answer"); + } + + for (int i=0; i < options.argumentsCount; ++i) { + char someData[MAX_NAME_LENGTH]; + strcpy(someData, qPrintable(argList[i])); + if (!_sendData(socket, someData, MAX_NAME_LENGTH)) { + END_ERROR(socket, "Could not send argument"); + } + if (!_checkResult(socket)) { + END_ERROR(socket, "Failure in argument send"); + } + } + + // trigger the startup + if (!_sendData(socket, COMMAND_SUCCESS, strlen(COMMAND_SUCCESS))) { + END_ERROR(socket, "Could not trigger startup"); + } + + const int waitTime = 60 * 60 * 1000; + if (!socket->waitForReadyRead(waitTime)) { + END_ERROR(socket, "Process timed out"); + } + + QByteArray result = socket->readAll(); + if (result != COMMAND_SUCCESS) { + if (returnValue) + *returnValue = -1; // just some at least + END_ERROR(socket, "Application did not start or returned error"); + } + + if (returnValue) + *returnValue = 0; + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::createDirectory(const QString &path, bool deleteBefore) +{ + if (deleteBefore) + deleteDirectory(path, true, true); + + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_CREATE_DIRECTORY)) { + END_ERROR(socket, "Could not establish connection for dir creation"); + } + + CreateDirectoryOptions option; + strcpy(option.dirName, qPrintable(path)); + option.recursively = true; + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send dir options"); + } + + if (!_checkResult(socket)) { + END_ERROR(socket, "Dir creation failed"); + } + + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::timeStampForLocalFileTime(FILETIME* fTime) const +{ + if (!fTime) + return false; + + FILETIME copyTime = *fTime; + LocalFileTimeToFileTime(©Time, ©Time); + + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_TIME_STAMP)) { + END_ERROR(socket, "Could not establish time stamp connection"); + } + + if (!_sendData(socket, (char*) ©Time, sizeof(copyTime))) { + END_ERROR(socket, "Could not send stamp time"); + } + + QByteArray data; + if (!_receiveData(socket, data)) { + END_ERROR(socket, "Did not receive time stamp or connection interrupted"); + } + + copyTime = *((FILETIME*)data.data()); + if (copyTime.dwLowDateTime == -1 && copyTime.dwHighDateTime == -1) { + END_ERROR(socket, "remote Time stamp failed!"); + } + + *fTime = copyTime; + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::fileCreationTime(const QString &fileName, FILETIME* deviceCreationTime) const +{ + if (!deviceCreationTime) + return false; + + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_FILE_TIME)) { + END_ERROR(socket, "Could not establish connection for file time access"); + } + + FileTimeOptions option; + strcpy(option.fileName, qPrintable(fileName)); + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send file time name"); + } + + QByteArray data; + if (!_receiveData(socket, data)) { + END_ERROR(socket, "File Time request failed"); + } + + FILETIME* resultTime = (FILETIME*) data.data(); + if (resultTime->dwLowDateTime == -1 && resultTime->dwHighDateTime == -1) { + END_ERROR(socket, 0); + debugOutput("Could not access file time", 0); + } + + *deviceCreationTime = *resultTime; + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::_createSocket(QTcpSocket*& result) const +{ + QTcpSocket* sock = new QTcpSocket(); + QByteArray ipAddress = qgetenv("DEVICE_IP"); + if (ipAddress.isEmpty()) { + qWarning("Error: You need to have DEVICE_IP set"); + exit(0); + } + sock->connectToHost(QHostAddress(QString(ipAddress)), 12145); + + if (!sock->waitForConnected()) { + qDebug() << "connection timeout..."; + result = NULL; + return false; + } + result = sock; + return true; +} + +void QtCesterConnection::_freeSocket(QTcpSocket*& sock) const +{ + if (!sock) + return; + if (sock->state() == QAbstractSocket::ConnectedState) { + sock->disconnectFromHost(); + // seems like no need to wait + //sock->waitForDisconnected(); + } + delete sock; + sock = NULL; +#ifdef Q_OS_WIN + Sleep(100); +#endif +} + +bool QtCesterConnection::_initCommand(QTcpSocket*& sock, const char* command) const +{ + QTcpSocket* socket = NULL; + if (!_createSocket(socket)) { + END_ERROR(socket, "Could not connect to server"); + } + + if (!_sendData(socket, command, strlen(command)) || + !_checkResult(socket)) { + END_ERROR(socket, "Cound not send command"); + } + sock = socket; + return true; +} + +bool QtCesterConnection::_sendData(QTcpSocket*& sock, const char* data, int dataSize) const +{ + int amount = sock->write(data, dataSize); + if (amount != dataSize) { + fprintf(stderr, "*******COULD NOT SEND ENOUGH DATA*************\n"); + } + return sock->waitForBytesWritten(); +} + +bool QtCesterConnection::_receiveData(QTcpSocket*& sock, QByteArray& data) const +{ + if (!sock->waitForReadyRead()) { + qDebug() << "did not receive any data"; + return false; + } + data = sock->readAll(); + return true; +} + +bool QtCesterConnection::_checkResult(QTcpSocket*& sock) const +{ + QByteArray response; + if (!_receiveData(sock, response) || response != COMMAND_SUCCESS) + return false; + return true; +} + diff --git a/src/qtestlib/wince/cetcpsync/qtcesterconnection.h b/src/qtestlib/wince/cetcpsync/qtcesterconnection.h new file mode 100644 index 000000000..c33935d90 --- /dev/null +++ b/src/qtestlib/wince/cetcpsync/qtcesterconnection.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef ACTIVESYNC_REMOTECONNECTION_H +#define ACTIVESYNC_REMOTECONNECTION_H + +#include "remoteconnection.h" + +class QTcpSocket; + +class QtCesterConnection : public AbstractRemoteConnection +{ +public: + QtCesterConnection(); + virtual ~QtCesterConnection(); + + bool connect(QVariantList &list = QVariantList()); + void disconnect(); + bool isConnected() const; + + // These functions are designed for transfer between desktop and device + // Caution: deviceDest path has to be device specific (eg. no drive letters for CE) + bool copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists = false); + bool copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive = true); + bool copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists = false); + bool copyDirectoryFromDevice(const QString &deviceSource, const QString &localDest, bool recursive = true); + + bool timeStampForLocalFileTime(FILETIME*) const; + bool fileCreationTime(const QString &fileName, FILETIME*) const; + + // These functions only work on files existing on the device + bool copyFile(const QString&, const QString&, bool failIfExists = false); + bool copyDirectory(const QString&, const QString&, bool recursive = true); + bool deleteFile(const QString&); + bool deleteDirectory(const QString&, bool recursive = true, bool failIfContentExists = false); + bool createDirectory(const QString&, bool deleteBefore=false); + + bool execute(QString program, QString arguments = QString(), int timeout = -1, int *returnValue = NULL); +private: + bool _createSocket(QTcpSocket*&) const; + void _freeSocket(QTcpSocket*&) const; + bool _initCommand(QTcpSocket*&, const char*) const; + bool _sendData(QTcpSocket*&, const char* data, int dataSize) const; + bool _receiveData(QTcpSocket*&, QByteArray&) const; + bool _checkResult(QTcpSocket*&) const; + bool connected; +}; + +#endif diff --git a/src/qtestlib/wince/cetcpsync/remoteconnection.cpp b/src/qtestlib/wince/cetcpsync/remoteconnection.cpp new file mode 100644 index 000000000..01a37201e --- /dev/null +++ b/src/qtestlib/wince/cetcpsync/remoteconnection.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "remoteconnection.h" + +AbstractRemoteConnection::AbstractRemoteConnection() +{ +} + +AbstractRemoteConnection::~AbstractRemoteConnection() +{ +} + +// Slow but should be ok... +bool AbstractRemoteConnection::moveFile(const QString &src, const QString &dest, bool FailIfExists) +{ + bool result = copyFile(src, dest, FailIfExists); + deleteFile(src); + return result; +} + +// Slow but should be ok... +bool AbstractRemoteConnection::moveDirectory(const QString &src, const QString &dest, bool recursive) +{ + bool result = copyDirectory(src, dest, true); + deleteDirectory(src, recursive); + return result; +} diff --git a/src/qtestlib/wince/cetcpsync/remoteconnection.h b/src/qtestlib/wince/cetcpsync/remoteconnection.h new file mode 100644 index 000000000..bf0fcce17 --- /dev/null +++ b/src/qtestlib/wince/cetcpsync/remoteconnection.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef REMOTECONNECTION_H +#define REMOTECONNECTION_H + +#include <QtCore/QString> +#include <QtCore/QVariant> +#include <windows.h> +class AbstractRemoteConnection +{ +public: + AbstractRemoteConnection(); + virtual ~AbstractRemoteConnection(); + + virtual bool connect(QVariantList&) = 0; + virtual void disconnect() = 0; + virtual bool isConnected() const = 0; + + // These functions are designed for transfer between desktop and device + // Caution: deviceDest path has to be device specific (eg. no drive letters for CE) + virtual bool copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists = false) = 0; + virtual bool copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive = true) = 0; + virtual bool copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists = false) = 0; + virtual bool copyDirectoryFromDevice(const QString &deviceSource, const QString &localDest, bool recursive = true) = 0; + + // For "intelligent deployment" we need to investigate on filetimes on the device + virtual bool timeStampForLocalFileTime(FILETIME*) const = 0; + virtual bool fileCreationTime(const QString &fileName, FILETIME*) const = 0; + + // These functions only work on files existing on the device + virtual bool copyFile(const QString&, const QString&, bool failIfExists = false) = 0; + virtual bool copyDirectory(const QString&, const QString&, bool recursive = true) = 0; + virtual bool deleteFile(const QString&) = 0; + virtual bool deleteDirectory(const QString&, bool recursive = true, bool failIfContentExists = false) = 0; + bool moveFile(const QString&, const QString&, bool FailIfExists = false); + bool moveDirectory(const QString&, const QString&, bool recursive = true); + + virtual bool createDirectory(const QString&, bool deleteBefore=false) = 0; + + virtual bool execute(QString program, QString arguments = QString(), int timeout = -1, int *returnValue = NULL) = 0; +}; + +#endif diff --git a/src/qtestlib/wince/cetcpsyncserver/cetcpsyncserver.pro b/src/qtestlib/wince/cetcpsyncserver/cetcpsyncserver.pro new file mode 100644 index 000000000..bd01d2dab --- /dev/null +++ b/src/qtestlib/wince/cetcpsyncserver/cetcpsyncserver.pro @@ -0,0 +1,17 @@ +TEMPLATE = app +TARGET = cetcpsyncsvr +DEPENDPATH += . +QT -= gui +QT += network + +CONFIG += console + +HEADERS += \ + connectionmanager.h \ + commands.h \ + transfer_global.h + +SOURCES += \ + connectionmanager.cpp \ + commands.cpp \ + main.cpp diff --git a/src/qtestlib/wince/cetcpsyncserver/commands.cpp b/src/qtestlib/wince/cetcpsyncserver/commands.cpp new file mode 100644 index 000000000..aaf5431d5 --- /dev/null +++ b/src/qtestlib/wince/cetcpsyncserver/commands.cpp @@ -0,0 +1,686 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commands.h" +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QProcess> + +#ifdef Q_OS_WINCE +#include <windows.h> +#endif + +///////////////////////////////////////////////////// +// Abstract Command Implementation // +///////////////////////////////////////////////////// +AbstractCommand::AbstractCommand() +: m_socket(0) +{ +} + +AbstractCommand::~AbstractCommand() +{ +} + +void AbstractCommand::reportSuccess() +{ + m_socket->write(COMMAND_SUCCESS, strlen(COMMAND_SUCCESS)); + m_socket->waitForBytesWritten(); +} + +void AbstractCommand::reportError() +{ + m_socket->write(COMMAND_ERROR, strlen(COMMAND_ERROR)); + m_socket->waitForBytesWritten(); +} + +void AbstractCommand::dataReceived(QByteArray&) +{ + debugOutput(1, "AbstractCommand::dataReceived NOT SUPPOSED TO BE HERE"); +} + +void AbstractCommand::commandFinished() +{ + debugOutput(1, "AbstractCommand::commandFinished()NOT SUPPOSED TO BE HERE"); +} + +void AbstractCommand::setSocket(QTcpSocket* socket) +{ + debugOutput(0, "AbstractCommand::setSocket()"); + Q_ASSERT(socket); + m_socket = socket; + connect(m_socket, SIGNAL(readyRead()), this, SLOT(_readData())); + reportSuccess(); +} + +QTcpSocket* AbstractCommand::socket() +{ + return m_socket; +} + +void AbstractCommand::_readData() +{ + QByteArray arr = m_socket->readAll(); + dataReceived(arr); +} + +void AbstractCommand::_disconnect() +{ +} + +///////////////////////////////////////////////////// +// Create File Command Implementation // +///////////////////////////////////////////////////// +CreateFileCommand::CreateFileCommand() +: m_dataCount(0) +{ + debugOutput(0, "CreateFileCommand::CreateFileCommand"); + m_options.fileSize= -1; +} + +CreateFileCommand::~CreateFileCommand() +{ + debugOutput(0, "CreateFileCommand::~CreateFileCommand"); + if (m_file.isOpen()) { + fprintf(stderr, "****************FILE IS STILL OPENED AND HAVENT FINISHED WRITING**********************\n"); + fprintf(stderr, "Current: %d Expected: %d\n", m_dataCount , m_options.fileSize); + m_file.close(); + } +} + +void CreateFileCommand::dataReceived(QByteArray &data) +{ + bool successful = true; + // If we haven't received the options yet + if (m_options.fileSize == -1) { + CreateFileOptions* opt = (CreateFileOptions*) data.data(); + memcpy(&m_options , opt , sizeof(CreateFileOptions)); + + if (QFileInfo(QString::fromLatin1(m_options.fileName)).exists()) { + if (m_options.overwriteExisting) { +#ifdef Q_OS_WINCE + SetFileAttributes(QFileInfo(m_options.fileName).absoluteFilePath().utf16(), FILE_ATTRIBUTE_NORMAL); +#endif + QFile::remove(m_options.fileName); + } else + successful = false; + } + m_file.setFileName(QString::fromLatin1(m_options.fileName)); + if (!m_file.open(QIODevice::WriteOnly)) + successful = false; + else + debugOutput(3, QString::fromLatin1("Creating file: %1").arg(m_options.fileName)); + } else { // write buffer on disc + if (!m_file.isOpen()) + return; + m_file.write(data); + m_dataCount += data.size(); + if (m_dataCount >= m_options.fileSize) { + // We do not care about more data than announced + m_file.close(); + } + } + + if (successful) + reportSuccess(); + else + reportError(); +} + +void CreateFileCommand::commandFinished() +{ + debugOutput(0, "CreateFileCommand::commandFinished"); +#ifdef Q_OS_WIN + // We need to set the file attributes for intelligent time comparisons + QString tmpFile = QString::fromLatin1(m_options.fileName); + HANDLE handle = CreateFile(tmpFile.utf16(), GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (handle != INVALID_HANDLE_VALUE) { + SetFileTime(handle, &(m_options.fileTime), NULL, NULL); + CloseHandle(handle); + } + SetFileAttributes(tmpFile.utf16(), m_options.fileAttributes); +#endif +} + +///////////////////////////////////////////////////// +// Create Directory Command Implementation // +///////////////////////////////////////////////////// +CreateDirectoryCommand::CreateDirectoryCommand() + : AbstractCommand() +{ + debugOutput(0, "CreateDirectoryCommand::CreateDirectoryCommand"); +} + +CreateDirectoryCommand::~CreateDirectoryCommand() +{ + debugOutput(0, "CreateDirectoryCommand::~CreateDirectoryCommand()"); +} + +void CreateDirectoryCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "CreateDirectoryCommand::dataReceived()"); + CreateDirectoryOptions* options = (CreateDirectoryOptions*) data.data(); + debugOutput(3, QString::fromLatin1("Creating directory: %1").arg(options->dirName)); + bool success = true; + QDir dir; + if (options->recursively) + success = dir.mkpath(options->dirName); + else + success = dir.mkdir(options->dirName); + + if (success) + reportSuccess(); + else + reportError(); +} + +void CreateDirectoryCommand::commandFinished() +{ + debugOutput(0, "CreateDirectoryCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Copy File Command Implementation // +///////////////////////////////////////////////////// +CopyFileCommand::CopyFileCommand() + : AbstractCommand() +{ + debugOutput(0, "CopyFileCommand::CopyFileCommand()"); +} + +CopyFileCommand::~CopyFileCommand() +{ + debugOutput(0, "CopyFileCommand::~CopyFileCommand()"); +} + +void CopyFileCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "CopyFileCommand::dataReceived()"); + CopyFileOptions* options = (CopyFileOptions*) data.data(); + debugOutput(3, QString::fromLatin1("Copy File: %1 -> %2").arg(options->from).arg(options->to)); + bool success = true; + if (QFileInfo(options->to).exists()) { + if (options->overwriteExisting) + QFile::remove(options->to); + else + success = false; + } + if (success) + if (!QFile::copy(options->from , options->to)) + success = false; + + if (success) + reportSuccess(); + else + reportError(); +} + +void CopyFileCommand::commandFinished() +{ + debugOutput(0, "CopyFileCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Copy Directory Command Implementation // +///////////////////////////////////////////////////// +CopyDirectoryCommand::CopyDirectoryCommand() + : AbstractCommand() +{ + debugOutput(0, "CopyDirectoryCommand::CopyDirectoryCommand()"); +} + +CopyDirectoryCommand::~CopyDirectoryCommand() +{ + debugOutput(0, "CopyDirectoryCommand::~CopyDirectoryCommand()"); +} + +void CopyDirectoryCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "CopyDirectoryCommand::dataReceived()"); + CopyDirectoryOptions* options = (CopyDirectoryOptions*) data.data(); + debugOutput(3, QString::fromLatin1("Copy Directory: %1 %2").arg(options->from).arg(options->to)); + if (copyDir(QLatin1String(options->from) , QLatin1String(options->to) , options->recursive)) + reportSuccess(); + else + reportError(); +} + +void CopyDirectoryCommand::commandFinished() +{ + debugOutput(0, "CopyDirectoryCommand::commandFinished()"); +} + +bool CopyDirectoryCommand::copyDir(const QString &from, const QString &to, bool recursive) +{ + QDir().mkpath(to); + QDir sourceDir(from); + QDir destDir(to); + QStringList entries = sourceDir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + foreach (QString item , entries) { + QString itemFrom = sourceDir.absoluteFilePath(item); + QString itemTo = destDir.absoluteFilePath(item); + if (QFileInfo(item).isDir()) { + if (recursive && !copyDir(itemFrom, itemTo, recursive)) + return false; + } else { + if (!QFile::copy(itemFrom, itemTo)) + return false; + } + } + return true; +} + +///////////////////////////////////////////////////// +// Delete File Command Implementation // +///////////////////////////////////////////////////// +DeleteFileCommand::DeleteFileCommand() + : AbstractCommand() +{ + debugOutput(0, "DeleteFileCommand::DeleteFileCommand()"); +} + +DeleteFileCommand::~DeleteFileCommand() +{ + debugOutput(0, "DeleteFileCommand::~DeleteFileCommand()"); +} + +void DeleteFileCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "DeleteFileCommand::dataReceived()"); + DeleteFileOptions* options = (DeleteFileOptions*) data.data(); + debugOutput(3, QString::fromLatin1("Delete File: %1").arg(options->fileName)); + bool success = true; + QFile file(options->fileName); + if (file.exists()) { +#ifdef Q_OS_WINCE + SetFileAttributes(QFileInfo(options->fileName).absoluteFilePath().utf16(), FILE_ATTRIBUTE_NORMAL); +#endif + success = file.remove(); + } else + success = false; + + if (success) + reportSuccess(); + else + reportError(); +} + +void DeleteFileCommand::commandFinished() +{ + debugOutput(0, "DeleteFileCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Delete Directory Command Implementation // +///////////////////////////////////////////////////// +DeleteDirectoryCommand::DeleteDirectoryCommand() + : AbstractCommand() +{ + debugOutput(0, "DeleteDirectoryCommand::DeleteDirectoryCommand()"); +} + +DeleteDirectoryCommand::~DeleteDirectoryCommand() +{ + debugOutput(0, "DeleteDirectoryCommand::~DeleteDirectoryCommand()"); +} + +void DeleteDirectoryCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "DeleteDirectoryCommand::dataReceived()"); + DeleteDirectoryOptions* options = (DeleteDirectoryOptions*) data.data(); + debugOutput(3, QString::fromLatin1("Delete directory: %1").arg(options->dirName)); + if (deleteDirectory(QLatin1String(options->dirName), options->recursive, options->failIfContentExists)) + reportSuccess(); + else + reportError(); +} + +void DeleteDirectoryCommand::commandFinished() +{ + debugOutput(0, "DeleteDirectoryCommand::commandFinished()"); +} + +bool DeleteDirectoryCommand::deleteDirectory(const QString &dirName, bool recursive, bool failIfContentExists) +{ + QDir dir(dirName); + if (!dir.exists()) + return false; + + QStringList itemList = dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + if (itemList.size() > 0 && failIfContentExists) + return false; + + foreach (QString item, itemList) { + QString itemName = dir.absoluteFilePath(item); + if (QFileInfo(itemName).isDir()) { + if (recursive && !deleteDirectory(itemName, recursive, failIfContentExists)) + return false; + } else { + if (!dir.remove(item)) + return false; + } + } + QString lastName = dir.dirName(); + dir.cdUp(); + dir.rmpath(lastName); + return true; +} + +///////////////////////////////////////////////////// +// Execute Command Implementation // +///////////////////////////////////////////////////// +ExecuteCommand::ExecuteCommand() + : AbstractCommand() + , m_argumentCount(0) + , m_timeout(-1) +{ + debugOutput(0, "ExecuteCommand::ExecuteCommand()"); +} + +ExecuteCommand::~ExecuteCommand() +{ + debugOutput(0, "ExecuteCommand::~ExecuteCommand()"); +} + +void ExecuteCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "ExecuteCommand::dataReceived()"); + + if (m_argumentCount == 0) { + ExecuteOptions* options = (ExecuteOptions*) data.data(); + if (!QFileInfo(options->appName).exists()) { + debugOutput(1, "Error execute: application does not exist"); + reportError(); + return; + } + + m_program = QLatin1String(options->appName); + m_argumentCount = options->argumentsCount; + m_waitFinished = options->waitForFinished; + m_timeout = options->timeout; + if (m_argumentCount == 0) + m_argumentCount = -1; // to trigger startup on next receive + reportSuccess(); + } else if (m_arguments.size() < m_argumentCount) { + m_arguments += data; + reportSuccess(); + } else { // do the execution + if (data == COMMAND_SUCCESS) + _doExecute(); + } +} + +void ExecuteCommand::_doExecute() +{ + debugOutput(0, "ExecuteCommand::_doExecute()"); + debugOutput(3, QString::fromLatin1("Execute: %1 %2").arg(m_program).arg(m_arguments.join(" "))); + if (m_waitFinished) { + QProcess process; + process.start(m_program, m_arguments); + if (process.waitForFinished(m_timeout) == false || process.exitCode() < 0) + reportError(); + else + reportSuccess(); + } else { + if (QProcess::startDetached(m_program, m_arguments)) + reportSuccess(); + else + reportError(); + } +} +void ExecuteCommand::commandFinished() +{ + debugOutput(0,"ExecuteCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Read File Implementation // +///////////////////////////////////////////////////// +ReadFileCommand::ReadFileCommand() + : AbstractCommand() + , m_currentPos(0) +{ + debugOutput(0, "ReadFileCommand::ReadFileCommand()"); + m_fileName.clear(); +} + +ReadFileCommand::~ReadFileCommand() +{ + debugOutput(0, "ReadFileCommand::~ReadFileCommand()"); + if (m_file.isOpen()) + m_file.close(); +} + +void ReadFileCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "ReadFileCommand::dataReceived()"); + if (m_fileName.isEmpty()) { + ReadFileOptions* option = (ReadFileOptions*) data.data(); + m_fileName = QLatin1String(option->fileName); + QFileInfo info(m_fileName); + m_file.setFileName(m_fileName); + ReadFileReply reply; + if (!info.exists() || !info.isFile() || !m_file.open(QIODevice::ReadOnly)) + reply.fileValid = false; + else + reply.fileValid = true; + reply.fileSize = info.size(); + m_fileSize = reply.fileSize; + socket()->write((char*) &reply, sizeof(reply)); + debugOutput(3, QString::fromLatin1("Reading file: %1").arg(m_fileName)); + } else { + QTcpSocket* sock = socket(); // design failure??? + if (data != COMMAND_SUCCESS || m_currentPos >= m_fileSize) { + sock->disconnectFromHost(); + return; + } + const int bufferSize = 1024; + QByteArray buffer = m_file.read(bufferSize); + m_currentPos += buffer.size(); + sock->write(buffer); + sock->waitForBytesWritten(); + } +} + +void ReadFileCommand::commandFinished() +{ + debugOutput(0, "ReadFileCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Read Directory Implementation // +///////////////////////////////////////////////////// +ReadDirectoryCommand::ReadDirectoryCommand() + : AbstractCommand() + , m_iterator(0) +{ + debugOutput(0, "ReadDirectoryCommand::ReadDirectoryCommand"); + m_dirName.clear(); +} + +ReadDirectoryCommand::~ReadDirectoryCommand() +{ + debugOutput(0, "ReadDirectoryCommand::~ReadDirectoryCommand()"); + delete m_iterator; +} + +void ReadDirectoryCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "ReadDirectoryCommand::dataReceived()"); + QTcpSocket* sock = socket(); + if (m_dirName.isEmpty()) { + ReadDirectoryOptions* option = (ReadDirectoryOptions*) data.data(); + QFileInfo info(QLatin1String(option->dirName)); + debugOutput(3, QString::fromLatin1("Reading Directory entries: %1").arg(option->dirName)); + ReadDirectoryReply reply; + if (!info.exists() || !info.isDir()) { + reply.itemCount = -1; + reply.entryValid = false; + } else { + m_dirName = QLatin1String(option->dirName); + m_dir.setPath(m_dirName); + m_iterator = new QDirIterator(m_dir); + reply.itemCount = m_dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).size(); + reply.entryValid = true; + } + sock->write((char*) &reply, sizeof(reply)); + sock->waitForBytesWritten(); + } else { + if (data != COMMAND_SUCCESS) { + qDebug() << "Something went wrong in the meantime"; + return; + } + ReadDirectoryItem reply; + if (m_iterator->hasNext()) { + m_iterator->next(); + QFileInfo info = m_iterator->fileInfo(); + strcpy(reply.name, qPrintable(info.absoluteFilePath())); + reply.isDirectory = info.isDir(); + if (!reply.isDirectory) + reply.size = info.size(); + } + reply.hasMore = m_iterator->hasNext(); + sock->write((char*) &reply, sizeof(reply)); + sock->waitForBytesWritten(); + } +} + +void ReadDirectoryCommand::commandFinished() +{ + debugOutput(0, "ReadDirectoryCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// File Time Implementation // +///////////////////////////////////////////////////// +FileTimeCommand::FileTimeCommand() + : AbstractCommand() +{ + debugOutput(0, "FileTimeCommand::FileTimeCommand()"); +} + +FileTimeCommand::~FileTimeCommand() +{ + debugOutput(0, "FileTimeCommand::~FileTimeCommand()"); +} + +void FileTimeCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "FileTimeCommand::dataReceived()"); + FileTimeOptions* option = (FileTimeOptions*) data.data(); + + FILETIME resultTime; + resultTime.dwLowDateTime = -1; + resultTime.dwHighDateTime = -1; + +#ifdef Q_OS_WIN + QString fileName = QLatin1String(option->fileName); + HANDLE deviceHandle = CreateFile(fileName.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + debugOutput(3, QString::fromLatin1("Asking FileTime: %1").arg(fileName)); + if (deviceHandle != INVALID_HANDLE_VALUE) { + FILETIME deviceCreationTime; + if (GetFileTime(deviceHandle, &deviceCreationTime, NULL, NULL)) { + resultTime = deviceCreationTime; + } + CloseHandle(deviceHandle); + } +#endif + QTcpSocket* sock = socket(); + sock->write((char*) &resultTime, sizeof(resultTime)); + sock->waitForBytesWritten(); +} + +void FileTimeCommand::commandFinished() +{ + debugOutput(0, "FileTimeCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Time Stamp Implementation // +///////////////////////////////////////////////////// +TimeStampCommand::TimeStampCommand() + : AbstractCommand() +{ + debugOutput(0, "TimeStampCommand::TimeStampCommand()"); +} + +TimeStampCommand::~TimeStampCommand() +{ + debugOutput(0, "TimeStampCommand::~TimeStampCommand()"); +} + +void TimeStampCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "TimeStampCommand::dataReceived()"); + FILETIME resultTime; + resultTime.dwLowDateTime = -1; + resultTime.dwHighDateTime = -1; + +#ifdef Q_OS_WIN + FILETIME stampTime = *((FILETIME*)data.data()); + + QString tmpFile = QString::fromLatin1("\\qt_tmp_ftime_convert"); + HANDLE remoteHandle = CreateFile(tmpFile.utf16(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (remoteHandle != INVALID_HANDLE_VALUE) { + if (!SetFileTime(remoteHandle, &stampTime, NULL, NULL)) { + CloseHandle(remoteHandle); + } else { + CloseHandle(remoteHandle); + remoteHandle = CreateFile(tmpFile.utf16(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (remoteHandle != INVALID_HANDLE_VALUE) { + if (GetFileTime(remoteHandle, &stampTime, NULL, NULL)) + resultTime = stampTime; + CloseHandle(remoteHandle); + DeleteFile(tmpFile.utf16()); + } + } + } + debugOutput(3, QString::fromLatin1("Asking TimeStamp")); +#endif + QTcpSocket* sock = socket(); + sock->write((char*) &resultTime, sizeof(resultTime)); + sock->waitForBytesWritten(); +} + +void TimeStampCommand::commandFinished() +{ + debugOutput(0, "TimeStampCommand::commandFinished()"); +} diff --git a/src/qtestlib/wince/cetcpsyncserver/commands.h b/src/qtestlib/wince/cetcpsyncserver/commands.h new file mode 100644 index 000000000..b880dcd1d --- /dev/null +++ b/src/qtestlib/wince/cetcpsyncserver/commands.h @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef COMMANDS_INCL +#define COMMANDS_INCL + +#include "transfer_global.h" + +#include <QtNetwork/QTcpSocket> +#include <QtCore/QString> +#include <QtCore/QFile> +#include <QtCore/QDir> +#include <QtCore/QDirIterator> +#include <windows.h> + +// debug output +#define DEBUG_LEVEL 2 +inline void debugOutput(int level, const char* text) +{ + if (level >= DEBUG_LEVEL) + qDebug() << text; +} + +inline void debugOutput(int level, const QString &text) +{ + if (level >= DEBUG_LEVEL) + qDebug() << text; +} +// Basic abtract command class +class AbstractCommand : public QObject +{ + Q_OBJECT +public: + AbstractCommand(); + virtual ~AbstractCommand(); + + void setSocket(QTcpSocket*); + QTcpSocket* socket(); + + void reportSuccess(); + void reportError(); + +public slots: + virtual void dataReceived(QByteArray&); + virtual void commandFinished(); + +private slots: + void _readData(); + void _disconnect(); + +private: + QTcpSocket* m_socket; +}; + +// File Creation class +class CreateFileCommand : public AbstractCommand +{ + Q_OBJECT +public: + CreateFileCommand(); + ~CreateFileCommand(); + +public slots: + void dataReceived(QByteArray&); + void commandFinished(); + +private: + CreateFileOptions m_options; + QFile m_file; + int m_dataCount; +}; + +inline AbstractCommand* instCreateFile() { return new CreateFileCommand(); } + +// Directory Creation class +class CreateDirectoryCommand : public AbstractCommand +{ + Q_OBJECT +public: + CreateDirectoryCommand(); + ~CreateDirectoryCommand(); + +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +}; +inline AbstractCommand* instCreateDirectory() { return new CreateDirectoryCommand(); } + +// File copy class +class CopyFileCommand : public AbstractCommand +{ + Q_OBJECT +public: + CopyFileCommand(); + ~CopyFileCommand(); + +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +}; +inline AbstractCommand* instCopyFile() { return new CopyFileCommand(); } + +// Copy directory class +class CopyDirectoryCommand : public AbstractCommand +{ + Q_OBJECT +public: + CopyDirectoryCommand(); + ~CopyDirectoryCommand(); + +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +private: + bool copyDir(const QString &from, const QString &to, bool recursive); +}; +inline AbstractCommand* instCopyDirectory() { return new CopyDirectoryCommand(); } + +// Delete File class +class DeleteFileCommand : public AbstractCommand +{ + Q_OBJECT +public: + DeleteFileCommand(); + ~DeleteFileCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +}; +inline AbstractCommand* instDeleteFile() { return new DeleteFileCommand(); } + +// Delete Directory class +class DeleteDirectoryCommand : public AbstractCommand +{ + Q_OBJECT +public: + DeleteDirectoryCommand(); + ~DeleteDirectoryCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +private: + bool deleteDirectory(const QString &dirName, bool recursive, bool failIfContentExists); +}; +inline AbstractCommand* instDeleteDirectory() { return new DeleteDirectoryCommand(); } + +// Execute application class +class ExecuteCommand : public AbstractCommand +{ + Q_OBJECT +public: + ExecuteCommand(); + ~ExecuteCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +private: + void _doExecute(); + QString m_program; + QStringList m_arguments; + int m_argumentCount; + bool m_waitFinished; + int m_timeout; +}; +inline AbstractCommand* instExecution() { return new ExecuteCommand(); } + +// Read File class +class ReadFileCommand : public AbstractCommand +{ + Q_OBJECT +public: + ReadFileCommand(); + ~ReadFileCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +private: + QString m_fileName; + QFile m_file; + qint64 m_currentPos; + qint64 m_fileSize; +}; +inline AbstractCommand* instReadFile() { return new ReadFileCommand(); } + +// Read Directory class +class ReadDirectoryCommand : public AbstractCommand +{ + Q_OBJECT +public: + ReadDirectoryCommand(); + ~ReadDirectoryCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +private: + QString m_dirName; + QDir m_dir; + QDirIterator* m_iterator; +}; +inline AbstractCommand* instReadDirectory() { return new ReadDirectoryCommand(); } + +// Read File Time class +class FileTimeCommand : public AbstractCommand +{ + Q_OBJECT +public: + FileTimeCommand(); + ~FileTimeCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +}; +inline AbstractCommand* instFileTime() { return new FileTimeCommand(); } + +// Time stamp class +class TimeStampCommand : public AbstractCommand +{ + Q_OBJECT +public: + TimeStampCommand(); + ~TimeStampCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +}; +inline AbstractCommand* instTimeStamp() { return new TimeStampCommand(); } + +// Access part +typedef AbstractCommand* (*instantiator)(); + +struct CommandInfo +{ + CommandInfo(const QString &name, instantiator func) : commandName(name) , commandFunc(func) { } + QString commandName; + instantiator commandFunc; +}; + +inline QList<CommandInfo> availableCommands() +{ + QList<CommandInfo> list; + list.append(CommandInfo(QLatin1String(COMMAND_CREATE_FILE), instCreateFile)); + list.append(CommandInfo(QLatin1String(COMMAND_CREATE_DIRECTORY), instCreateDirectory)); + list.append(CommandInfo(QLatin1String(COMMAND_COPY_FILE), instCopyFile)); + list.append(CommandInfo(QLatin1String(COMMAND_COPY_DIRECTORY), instCopyDirectory)); + list.append(CommandInfo(QLatin1String(COMMAND_DELETE_FILE), instDeleteFile)); + list.append(CommandInfo(QLatin1String(COMMAND_DELETE_DIRECTORY), instDeleteDirectory)); + list.append(CommandInfo(QLatin1String(COMMAND_EXECUTE), instExecution)); + list.append(CommandInfo(QLatin1String(COMMAND_READ_FILE), instReadFile)); + list.append(CommandInfo(QLatin1String(COMMAND_READ_DIRECTORY), instReadDirectory)); + list.append(CommandInfo(QLatin1String(COMMAND_FILE_TIME), instFileTime)); + list.append(CommandInfo(QLatin1String(COMMAND_TIME_STAMP), instTimeStamp)); + return list; +} + +#endif diff --git a/src/qtestlib/wince/cetcpsyncserver/connectionmanager.cpp b/src/qtestlib/wince/cetcpsyncserver/connectionmanager.cpp new file mode 100644 index 000000000..1d5600d1b --- /dev/null +++ b/src/qtestlib/wince/cetcpsyncserver/connectionmanager.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "connectionmanager.h" +#include "commands.h" +#include <QtCore/QDebug> + +ConnectionManager::ConnectionManager() + : QObject() + , m_server(0) +{ + debugOutput(0, "ConnectionManager::ConnectionManager()"); +} + +ConnectionManager::~ConnectionManager() +{ + debugOutput(0, "ConnectionManager::~ConnectionManager()"); + cleanUp(); +} + +bool ConnectionManager::init() +{ + debugOutput(0, "ConnectionManager::init()"); + debugOutput(3, "Initializing server..."); + cleanUp(); + m_server = new QTcpServer(this); + connect(m_server, SIGNAL(newConnection()), this, SLOT(newConnection())); + bool result = m_server->listen(QHostAddress::Any, SERVER_PORT); + if (!result) + debugOutput(3, QString::fromLatin1(" Error: Server start failed:") + m_server->errorString()); + debugOutput(3, " Waiting for action"); + return result; +} + +void ConnectionManager::cleanUp() +{ + debugOutput(0, "ConnectionManager::cleanUp()"); + + if (m_server) { + debugOutput(1, "Removing server instance..."); + disconnect(m_server, SIGNAL(newConnection()), this, SLOT(newConnection())); + delete m_server; + m_server = 0; + } +} + +void ConnectionManager::newConnection() +{ + debugOutput(0, "ConnectionManager::newConnection()"); + + QTcpSocket* connection = m_server->nextPendingConnection(); + if (!connection) { + debugOutput(3, "Received connection has empty socket"); + return; + } + debugOutput(0, QString::fromLatin1(" received a connection: %1").arg((int) connection)); + new Connection(connection); +} + +Connection::Connection(QTcpSocket *socket) + : QObject() + , m_connection(socket) + , m_command(0) +{ + connect(m_connection, SIGNAL(readyRead()), this, SLOT(receiveCommand())); + connect(m_connection, SIGNAL(disconnected()), this, SLOT(closedConnection())); +} + +Connection::~Connection() +{ + if (m_command) { + m_command->commandFinished(); + delete m_command; + m_command = 0; + } + delete m_connection; +} + +void Connection::receiveCommand() +{ + QByteArray arr = m_connection->readAll(); + debugOutput(1, QString::fromLatin1("Command received: ") + (arr)); + QList<CommandInfo> commands = availableCommands(); + for(QList<CommandInfo>::iterator it = commands.begin(); it != commands.end(); ++it) { + if (it->commandName == QString::fromLatin1(arr)) { + debugOutput(1, "Found command in list"); + disconnect(m_connection, SIGNAL(readyRead()), this, SLOT(receiveCommand())); + AbstractCommand* command = (*it).commandFunc(); + command->setSocket(m_connection); + m_command = command; + return; + } + } + debugOutput(2, QString::fromLatin1("Unknown command received: ") + (arr)); +} + +void Connection::closedConnection() +{ + debugOutput(0, "connection being closed..."); + this->deleteLater(); +} diff --git a/src/qtestlib/wince/cetcpsyncserver/connectionmanager.h b/src/qtestlib/wince/cetcpsyncserver/connectionmanager.h new file mode 100644 index 000000000..e9f725cc8 --- /dev/null +++ b/src/qtestlib/wince/cetcpsyncserver/connectionmanager.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CONNECTION_MANAGER_INCL +#define CONNECTION_MANAGER_INCL + +#include "transfer_global.h" +#include "commands.h" + +#include <QtNetwork/QTcpServer> +#include <QtNetwork/QTcpSocket> + +class Connection : public QObject +{ + Q_OBJECT +public: + Connection(QTcpSocket* socket); + ~Connection(); + +public slots: + void receiveCommand(); + void closedConnection(); + +private: + QTcpSocket* m_connection; + AbstractCommand* m_command; +}; + +class ConnectionManager : public QObject +{ + Q_OBJECT +public: + ConnectionManager(); + ~ConnectionManager(); + + bool init(); + +public slots: + void cleanUp(); + void newConnection(); + +private: + QTcpServer* m_server; +}; + +#endif diff --git a/src/qtestlib/wince/cetcpsyncserver/main.cpp b/src/qtestlib/wince/cetcpsyncserver/main.cpp new file mode 100644 index 000000000..eccd86839 --- /dev/null +++ b/src/qtestlib/wince/cetcpsyncserver/main.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "connectionmanager.h" + +#include <QtCore> +#include <QtNetwork> + +void messageOutput(QtMsgType type, const char *msg) +{ + switch(type) { + case QtDebugMsg: fprintf(stderr, "Debug: %s\n", msg); break; + case QtWarningMsg: fprintf(stderr, "Warning: %s\n", msg); break; + default: fprintf(stderr, "Some Msg: %s\n", msg); break; + } +} + +int main(int argc, char **argv) +{ + qInstallMsgHandler(messageOutput); + + QCoreApplication app(argc, argv); + ConnectionManager manager; + manager.init(); + return app.exec(); +} diff --git a/src/qtestlib/wince/cetcpsyncserver/transfer_global.h b/src/qtestlib/wince/cetcpsyncserver/transfer_global.h new file mode 100644 index 000000000..a2c029753 --- /dev/null +++ b/src/qtestlib/wince/cetcpsyncserver/transfer_global.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef TRANSFER_GLOBAL_H +#define TRANSFER_GLOBAL_H + +#include <QtCore/qglobal.h> +#ifdef Q_OS_WIN +#include <windows.h> +#endif + +#define SERVER_PORT 12145 + +#define MAX_NAME_LENGTH 512 +#define MAX_ARGUMENTS 10 + +// Defines for commands sent/received +#define COMMAND_CREATE_FILE "CREATEFILE" +#define COMMAND_CREATE_DIRECTORY "CREATEDIR" +#define COMMAND_COPY_FILE "COPYFILE" +#define COMMAND_COPY_DIRECTORY "COPYDIR" +#define COMMAND_DELETE_FILE "DELETEFILE" +#define COMMAND_DELETE_DIRECTORY "DELETEDIR" +#define COMMAND_EXECUTE "EXECUTE" +#define COMMAND_QUIT_SERVER "QUIT" +#define COMMAND_FILE_TIME "FILETIME" +#define COMMAND_TIME_STAMP "TIMESTAMP" + +// Report back commands +#define COMMAND_SUCCESS "SUCCESS" +#define COMMAND_ERROR "ERROR" + +// Defines for commands that send data back to requester +#define COMMAND_READ_FILE "READFILE" +#define COMMAND_READ_DIRECTORY "READDIR" + +#include <QtCore/qglobal.h> +// Option-Structures for commands + +struct CreateFileOptions +{ + char fileName[MAX_NAME_LENGTH]; +#ifdef Q_OS_WIN + FILETIME fileTime; + DWORD fileAttributes; +#endif + int fileSize; + bool overwriteExisting; +}; + +struct CreateDirectoryOptions +{ + char dirName[MAX_NAME_LENGTH]; + bool recursively; // in case of \foo\bar create \foo if it does not exist +}; + +struct CopyFileOptions +{ + char from[MAX_NAME_LENGTH]; + char to[MAX_NAME_LENGTH]; + bool overwriteExisting; +}; + +struct CopyDirectoryOptions +{ + char from[MAX_NAME_LENGTH]; + char to[MAX_NAME_LENGTH]; + bool recursive; +}; + +struct DeleteFileOptions +{ + char fileName[MAX_NAME_LENGTH]; +}; + +struct DeleteDirectoryOptions +{ + char dirName[MAX_NAME_LENGTH]; + bool recursive; + bool failIfContentExists; +}; + +struct ExecuteOptions +{ + char appName[MAX_NAME_LENGTH]; + int argumentsCount; + bool waitForFinished; + int timeout; +}; + +struct ReadFileOptions +{ + char fileName[MAX_NAME_LENGTH]; +}; + +struct ReadFileReply +{ + qint64 fileSize; + bool fileValid; +}; + +struct ReadDirectoryOptions +{ + char dirName[MAX_NAME_LENGTH]; +}; + +struct ReadDirectoryItem +{ + char name[MAX_NAME_LENGTH]; + qint64 size; + bool isDirectory; + bool hasMore; +}; + +#define FileTimeOptions ReadFileOptions + +struct ReadDirectoryReply +{ + bool entryValid; + int itemCount; // might change during iteration +}; +#endif diff --git a/src/qtestlib/wince/cetest/activesyncconnection.cpp b/src/qtestlib/wince/cetest/activesyncconnection.cpp new file mode 100644 index 000000000..c336c273b --- /dev/null +++ b/src/qtestlib/wince/cetest/activesyncconnection.cpp @@ -0,0 +1,640 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "activesyncconnection.h" +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo> +#include <rapi.h> + +extern void debugOutput(const QString& text, int level); + +ActiveSyncConnection::ActiveSyncConnection() + : AbstractRemoteConnection() + , connected(false) +{ +} + +ActiveSyncConnection::~ActiveSyncConnection() +{ + if (isConnected()) + disconnect(); +} + +bool ActiveSyncConnection::connect(QVariantList&) +{ + if (connected) + return true; + connected = false; + RAPIINIT init; + init.cbSize = sizeof(init); + if (CeRapiInitEx(&init) != S_OK) + return connected; + + DWORD res; + res = WaitForMultipleObjects(1,&(init.heRapiInit),true, 5000); + if ((res == -1) || (res == WAIT_TIMEOUT) || (init.hrRapiInit != S_OK)) + return connected; + + connected = true; + return connected; +} + +void ActiveSyncConnection::disconnect() +{ + connected = false; + CeRapiUninit(); +} + +bool ActiveSyncConnection::isConnected() const +{ + return connected; +} + +bool ActiveSyncConnection::copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists) +{ + if (failIfExists) { + CE_FIND_DATA search; + HANDLE searchHandle = CeFindFirstFile(deviceDest.utf16(), &search); + if (searchHandle != INVALID_HANDLE_VALUE) { + CeFindClose(searchHandle); + return false; + } + } + + QFile file(localSource); + if (!file.exists()) + return false; + if (!file.open(QIODevice::ReadOnly)) { + debugOutput(QString::fromLatin1(" Could not open source file"),2); + if (file.size() == 0) { + // Create an empy file + deleteFile(deviceDest); + HANDLE deviceHandle = CeCreateFile(deviceDest.utf16(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (deviceHandle != INVALID_HANDLE_VALUE) { + CeCloseHandle(deviceHandle); + return true; + } + } else { + qWarning("Could not open %s: %s", qPrintable(localSource), qPrintable(file.errorString())); + } + return false; + } + + deleteFile(deviceDest); + HANDLE deviceHandle = CeCreateFile(deviceDest.utf16(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (deviceHandle == INVALID_HANDLE_VALUE) { + qWarning("Could not create %s: %s", qPrintable(deviceDest), strwinerror(CeGetLastError()).constData()); + return false; + } + + DWORD written = 0; + int currentPos = 0; + int size = file.size(); + DWORD toWrite = 0; + const int bufferSize = 65000; + QByteArray data; + data.reserve(bufferSize); + while (currentPos < size) { + data = file.read(bufferSize); + if (data.size() <= 0) { + wprintf( L"Error while reading file!\n"); + return false; + } + if (size - currentPos > bufferSize ) + toWrite = bufferSize; + else + toWrite = size - currentPos; + if (toWrite == 0) + break; + if (!CeWriteFile(deviceHandle, data.data() , toWrite, &written, NULL)) { + qWarning("Could not write to %s: %s", qPrintable(deviceDest), strwinerror(CeGetLastError()).constData()); + return false; + } + currentPos += written; + data.clear(); + wprintf( L"%s -> %s (%d / %d) %d %%\r", localSource.utf16() , deviceDest.utf16(), currentPos , size, (100*currentPos)/size ); + } + wprintf(L"\n"); + + // Copy FileTime for update verification + FILETIME creationTime, accessTime, writeTime; + HANDLE localHandle = CreateFile(localSource.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + if (localHandle != INVALID_HANDLE_VALUE) { + if (GetFileTime(localHandle, &creationTime, &accessTime, &writeTime)) { + LocalFileTimeToFileTime(&writeTime, &writeTime); + if (!CeSetFileTime(deviceHandle, &writeTime, NULL, NULL)) { + debugOutput(QString::fromLatin1(" Could not write time values"), 0); + } + } + CloseHandle(localHandle); + } + CeCloseHandle(deviceHandle); + + DWORD attributes = GetFileAttributes(localSource.utf16()); + if (attributes != -1 ) + CeSetFileAttributes(deviceDest.utf16(), attributes); + return true; +} + +bool ActiveSyncConnection::copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive) +{ + QDir dir(localSource); + if (!dir.exists()) + return false; + + deleteDirectory(deviceDest, recursive); + CeCreateDirectory(deviceDest.utf16(), NULL); + foreach(QString entry, dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot)) { + QString source = localSource + "\\" + entry; + QString target = deviceDest + "\\" + entry; + QFileInfo info(source); + if (info.isDir()) { + if (recursive) { + if (!copyDirectoryToDevice(source, target, recursive)) + return false; + } + } else { + if (!copyFileToDevice(source, target)) + return false; + } + } + return true; +} + +bool ActiveSyncConnection::copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists) +{ + QFile target(localDest); + if (failIfExists && target.exists()) { + debugOutput(QString::fromLatin1(" Not allowed to overwrite file"), 2); + return false; + } + + if (target.exists()) + target.remove(); + + HANDLE deviceHandle = CeCreateFile(deviceSource.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + if (deviceHandle == INVALID_HANDLE_VALUE) { + debugOutput(QString::fromLatin1(" Could not open file on device"), 2); + return false; + } + + DWORD fileSize = CeGetFileSize( deviceHandle, NULL ); + if (fileSize == -1) { + debugOutput(QString::fromLatin1(" Could not stat filesize of remote file"), 2); + CeCloseHandle(deviceHandle); + return false; + } + + if (!target.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + debugOutput(QString::fromLatin1(" Could not open local file for writing"), 2); + CeCloseHandle(deviceHandle); + return false; + } + + int bufferSize = 65000; + char *buffer = (char*) malloc(bufferSize); + DWORD bufferRead = 0; + DWORD bufferWritten = 0; + bool readUntilEnd = false; + while(CeReadFile(deviceHandle, buffer, bufferSize, &bufferRead, NULL)) { + if (bufferRead == 0) { + readUntilEnd = true; + break; + } + target.write(buffer, bufferRead); + bufferWritten += bufferRead; + wprintf(L"%s -> %s (%d / %d) %d %%\r", deviceSource.utf16(), localDest.utf16(), bufferWritten, fileSize, (100*bufferWritten)/fileSize); + } + wprintf(L"\n"); + + if (!readUntilEnd) { + debugOutput(QString::fromLatin1(" an error occurred during copy"), 2); + return false; + } + + CeCloseHandle(deviceHandle); + return true; +} + +bool ActiveSyncConnection::copyDirectoryFromDevice(const QString &deviceSource, const QString &localDest, bool recursive) +{ + if (!QDir(localDest).exists() && !QDir(localDest).mkpath(QDir(localDest).absolutePath())) { + debugOutput(QString::fromLatin1(" Could not create local path"), 2); + } + + QString searchArg = deviceSource + "\\*"; + CE_FIND_DATA data; + HANDLE searchHandle = CeFindFirstFile(searchArg.utf16(), &data); + if (searchHandle == INVALID_HANDLE_VALUE) { + // We return true because we might be in a recursive call + // where nothing is to copy and the copy process + // might still be correct + return true; + } + + do { + QString srcFile = deviceSource + "\\" + QString::fromWCharArray(data.cFileName); + QString destFile = localDest + "\\" + QString::fromWCharArray(data.cFileName); + if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + if (recursive && !copyDirectoryFromDevice(srcFile, destFile, recursive)) { + wprintf(L"Copy of subdirectory(%s) failed\n", srcFile.utf16()); + return false; + } + } else { + copyFileFromDevice(srcFile, destFile, false); + } + } while(CeFindNextFile(searchHandle, &data)); + CeFindClose(searchHandle); + return true; +} + +bool ActiveSyncConnection::copyFile(const QString &srcFile, const QString &destFile, bool failIfExists) +{ + return CeCopyFile(QDir::toNativeSeparators(srcFile).utf16(), + QDir::toNativeSeparators(destFile).utf16(), failIfExists); +} + +bool ActiveSyncConnection::copyDirectory(const QString &srcDirectory, const QString &destDirectory, + bool recursive) +{ + CeCreateDirectory(destDirectory.utf16(), NULL); + QString searchArg = srcDirectory + "\\*"; + CE_FIND_DATA data; + HANDLE searchHandle = CeFindFirstFile(searchArg.utf16(), &data); + if (searchHandle == INVALID_HANDLE_VALUE) { + // We return true because we might be in a recursive call + // where nothing is to copy and the copy process + // might still be correct + return true; + } + + do { + QString srcFile = srcDirectory + "\\" + QString::fromWCharArray(data.cFileName); + QString destFile = destDirectory + "\\" + QString::fromWCharArray(data.cFileName); + if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + if (recursive && !copyDirectory(srcFile, destFile, recursive)) { + wprintf(L"Copy of subdirectory(%s) failed\n", srcFile.utf16()); + return false; + } + } else { + debugOutput(QString::fromLatin1("Copy %1 -> %2\n").arg(srcFile).arg(destFile), 0); + CeCopyFile(srcFile.utf16(), destFile.utf16(), false); + } + } while(CeFindNextFile(searchHandle, &data)); + CeFindClose(searchHandle); + return true; +} + +bool ActiveSyncConnection::deleteFile(const QString &fileName) +{ + CeSetFileAttributes(fileName.utf16(), FILE_ATTRIBUTE_NORMAL); + return CeDeleteFile(fileName.utf16()); +} + +bool ActiveSyncConnection::deleteDirectory(const QString &directory, bool recursive, bool failIfContentExists) +{ + HANDLE hFind; + CE_FIND_DATA FindFileData; + QString FileName = directory + "\\*"; + hFind = CeFindFirstFile(FileName.utf16(), &FindFileData); + if( hFind == INVALID_HANDLE_VALUE ) + return CeRemoveDirectory(directory.utf16()); + + if (failIfContentExists) + return false; + + do { + QString FileName = directory + "\\" + QString::fromWCharArray(FindFileData.cFileName); + if((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + if (recursive) + if (!deleteDirectory(FileName, recursive, failIfContentExists)) + return false; + } else { + if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + CeSetFileAttributes(FileName.utf16(), FILE_ATTRIBUTE_NORMAL); + if( !CeDeleteFile(FileName.utf16()) ) + break; + } + } while(CeFindNextFile(hFind,&FindFileData)); + CeFindClose(hFind); + + return CeRemoveDirectory(directory.utf16()); +} + +bool ActiveSyncConnection::execute(QString program, QString arguments, int timeout, int *returnValue) +{ + if (!isConnected()) { + qWarning("Cannot execute, connect to device first!"); + return false; + } + + PROCESS_INFORMATION* pid = new PROCESS_INFORMATION; + bool result = false; + if (timeout != 0) { + // If we want to wait, we have to use CeRapiInvoke, as CeCreateProcess has no way to wait + // until the process ends. The lib must have been build and also deployed already. + if (!isConnected() && !connect()) + return false; + + QString dllLocation = "\\Windows\\QtRemote.dll"; + QString functionName = "qRemoteLaunch"; + + DWORD outputSize; + BYTE* output; + IRAPIStream *stream; + int returned = 0; + DWORD error = 0; + HRESULT res = CeRapiInvoke(dllLocation.utf16(), functionName.utf16(), 0, 0, &outputSize, &output, &stream, 0); + if (S_OK != res) { + DWORD ce_error = CeGetLastError(); + if (S_OK != ce_error) { + qWarning("Error invoking %s on %s: %s", qPrintable(functionName), + qPrintable(dllLocation), strwinerror(ce_error).constData()); + } else { + qWarning("Error: %s on %s unexpectedly returned %d", qPrintable(functionName), + qPrintable(dllLocation), res); + } + } else { + DWORD written; + int strSize = program.length(); + if (S_OK != stream->Write(&strSize, sizeof(strSize), &written)) { + qWarning(" Could not write appSize to process"); + return false; + } + if (S_OK != stream->Write(program.utf16(), program.length()*sizeof(wchar_t), &written)) { + qWarning(" Could not write appName to process"); + return false; + } + strSize = arguments.length(); + if (S_OK != stream->Write(&strSize, sizeof(strSize), &written)) { + qWarning(" Could not write argumentSize to process"); + return false; + } + if (S_OK != stream->Write(arguments.utf16(), arguments.length()*sizeof(wchar_t), &written)) { + qWarning(" Could not write arguments to process"); + return false; + } + if (S_OK != stream->Write(&timeout, sizeof(timeout), &written)) { + qWarning(" Could not write waiting option to process"); + return false; + } + + if (S_OK != stream->Read(&returned, sizeof(returned), &written)) { + qWarning(" Could not access return value of process"); + } + if (S_OK != stream->Read(&error, sizeof(error), &written)) { + qWarning(" Could not access error code"); + } + + if (error) { + qWarning("Error on target: %s", strwinerror(error).constData()); + result = false; + } + else { + result = true; + } + } + + if (returnValue) + *returnValue = returned; + } else { + // We do not need to invoke another lib etc, if we are not interested in results anyway... + result = CeCreateProcess(program.utf16(), arguments.utf16(), 0, 0, false, 0, 0, 0, 0, pid); + } + return result; +} + +bool ActiveSyncConnection::setDeviceAwake(bool activate, int *returnValue) +{ + if (!isConnected()) { + qWarning("Cannot execute, connect to device first!"); + return false; + } + bool result = false; + + // If we want to wait, we have to use CeRapiInvoke, as CeCreateProcess has no way to wait + // until the process ends. The lib must have been build and also deployed already. + if (!isConnected() && !connect()) + return false; + + HRESULT res = S_OK; + + //SYSTEM_POWER_STATUS_EX systemPowerState; + + //res = CeGetSystemPowerStatusEx(&systemPowerState, true); + + QString dllLocation = "\\Windows\\QtRemote.dll"; + QString functionName = "qRemoteToggleUnattendedPowerMode"; + + DWORD outputSize; + BYTE* output; + IRAPIStream *stream; + int returned = 0; + int toggle = int(activate); + + res = CeRapiInvoke(dllLocation.utf16(), functionName.utf16(), 0, 0, &outputSize, &output, &stream, 0); + if (S_OK != res) { + DWORD ce_error = CeGetLastError(); + if (S_OK != ce_error) { + qWarning("Error invoking %s on %s: %s", qPrintable(functionName), + qPrintable(dllLocation), strwinerror(ce_error).constData()); + } else { + qWarning("Error: %s on %s unexpectedly returned %d", qPrintable(functionName), + qPrintable(dllLocation), res); + } + } else { + DWORD written; + + if (S_OK != stream->Write(&toggle, sizeof(toggle), &written)) { + qWarning(" Could not write toggle option to process"); + return false; + } + + if (S_OK != stream->Read(&returned, sizeof(returned), &written)) { + qWarning(" Could not access return value of process"); + } + else + result = true; + } + + if (returnValue) + *returnValue = returned; + + return result; +} + +bool ActiveSyncConnection::resetDevice() +{ + if (!isConnected()) { + qWarning("Cannot execute, connect to device first!"); + return false; + } + + bool result = false; + if (!isConnected() && !connect()) + return false; + + QString dllLocation = "\\Windows\\QtRemote.dll"; + QString functionName = "qRemoteSoftReset"; + + DWORD outputSize; + BYTE* output; + IRAPIStream *stream; + + int returned = 0; + + HRESULT res = CeRapiInvoke(dllLocation.utf16(), functionName.utf16(), 0, 0, &outputSize, &output, &stream, 0); + if (S_OK != res) { + DWORD ce_error = CeGetLastError(); + if (S_OK != ce_error) { + qWarning("Error invoking %s on %s: %s", qPrintable(functionName), + qPrintable(dllLocation), strwinerror(ce_error).constData()); + } else { + qWarning("Error: %s on %s unexpectedly returned %d", qPrintable(functionName), + qPrintable(dllLocation), res); + } + } else { + result = true; + } + return result; +} + +bool ActiveSyncConnection::toggleDevicePower(int *returnValue) +{ + if (!isConnected()) { + qWarning("Cannot execute, connect to device first!"); + return false; + } + + bool result = false; + if (!isConnected() && !connect()) + return false; + + QString dllLocation = "\\Windows\\QtRemote.dll"; + QString functionName = "qRemotePowerButton"; + + DWORD outputSize; + BYTE* output; + IRAPIStream *stream; + int returned = 0; + + HRESULT res = CeRapiInvoke(dllLocation.utf16(), functionName.utf16(), 0, 0, &outputSize, &output, &stream, 0); + if (S_OK != res) { + DWORD ce_error = CeGetLastError(); + if (S_OK != ce_error) { + qWarning("Error invoking %s on %s: %s", qPrintable(functionName), + qPrintable(dllLocation), strwinerror(ce_error).constData()); + } else { + qWarning("Error: %s on %s unexpectedly returned %d", qPrintable(functionName), + qPrintable(dllLocation), res); + } + } else { + DWORD written; + if (S_OK != stream->Read(&returned, sizeof(returned), &written)) { + qWarning(" Could not access return value of process"); + } + else { + result = true; + } + } + + if (returnValue) + *returnValue = returned; + return result; +} + +bool ActiveSyncConnection::createDirectory(const QString &path, bool deleteBefore) +{ + if (deleteBefore) + deleteDirectory(path); + QStringList separated = path.split(QLatin1Char('\\')); + QString current = QLatin1String("\\"); + bool result; + for (int i=1; i < separated.size(); ++i) { + current += separated.at(i); + result = CeCreateDirectory(current.utf16(), NULL); + current += QLatin1String("\\"); + } + return result; +} + +bool ActiveSyncConnection::timeStampForLocalFileTime(FILETIME* fTime) const +{ + QString tmpFile = QString::fromLatin1("\\qt_tmp_ftime_convert"); + HANDLE remoteHandle = CeCreateFile(tmpFile.utf16(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (remoteHandle == INVALID_HANDLE_VALUE) + return false; + + LocalFileTimeToFileTime(fTime, fTime); + + if (!CeSetFileTime(remoteHandle, fTime, NULL, NULL)) { + CeCloseHandle(remoteHandle); + return false; + } + + CeCloseHandle(remoteHandle); + remoteHandle = CeCreateFile(tmpFile.utf16(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (remoteHandle == INVALID_HANDLE_VALUE) + return false; + if (!CeGetFileTime(remoteHandle, fTime, NULL, NULL)) { + CeCloseHandle(remoteHandle); + return false; + } + + CeCloseHandle(remoteHandle); + CeDeleteFile(tmpFile.utf16()); + return true; +} + +bool ActiveSyncConnection::fileCreationTime(const QString &fileName, FILETIME* deviceCreationTime) const +{ + HANDLE deviceHandle = CeCreateFile(fileName.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + if (deviceHandle == INVALID_HANDLE_VALUE) + return false; + + bool result = true; + if (!CeGetFileTime(deviceHandle, deviceCreationTime, NULL, NULL)) + result = false; + + CeCloseHandle(deviceHandle); + return result; +} diff --git a/src/qtestlib/wince/cetest/activesyncconnection.h b/src/qtestlib/wince/cetest/activesyncconnection.h new file mode 100644 index 000000000..c23742e50 --- /dev/null +++ b/src/qtestlib/wince/cetest/activesyncconnection.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ACTIVESYNC_REMOTECONNECTION_H +#define ACTIVESYNC_REMOTECONNECTION_H + +#include "remoteconnection.h" + +#if defined(Q_OS_WIN32) +#define REMOTELIBNAME "remotecommands" +#endif + +class ActiveSyncConnection : public AbstractRemoteConnection +{ +public: + ActiveSyncConnection(); + virtual ~ActiveSyncConnection(); + + bool connect(QVariantList &list = QVariantList()); + void disconnect(); + bool isConnected() const; + + // These functions are designed for transfer between desktop and device + // Caution: deviceDest path has to be device specific (eg. no drive letters for CE) + bool copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists = false); + bool copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive = true); + bool copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists = false); + bool copyDirectoryFromDevice(const QString &deviceSource, const QString &localDest, bool recursive = true); + + bool timeStampForLocalFileTime(FILETIME*) const; + bool fileCreationTime(const QString &fileName, FILETIME*) const; + + // These functions only work on files existing on the device + bool copyFile(const QString&, const QString&, bool failIfExists = false); + bool copyDirectory(const QString&, const QString&, bool recursive = true); + bool deleteFile(const QString&); + bool deleteDirectory(const QString&, bool recursive = true, bool failIfContentExists = false); + bool moveFile(const QString&, const QString&, bool FailIfExists = false); + bool moveDirectory(const QString&, const QString&, bool recursive = true); + + bool createDirectory(const QString&, bool deleteBefore=false); + + bool execute(QString program, QString arguments = QString(), int timeout = -1, int *returnValue = NULL); + bool resetDevice(); + bool toggleDevicePower(int *returnValue = NULL); + bool setDeviceAwake(bool activate, int *returnValue = NULL); +private: + bool connected; +}; + +#endif diff --git a/src/qtestlib/wince/cetest/bootstrapped.pri b/src/qtestlib/wince/cetest/bootstrapped.pri new file mode 100644 index 000000000..56c8ab7e9 --- /dev/null +++ b/src/qtestlib/wince/cetest/bootstrapped.pri @@ -0,0 +1,45 @@ +# Bootstrapped Input +SOURCES += \ + $$QT_SOURCE_TREE/src/corelib/tools/qstring.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qstringlist.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qfile.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qdir.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qfilesystementry.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qfilesystemengine.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qfilesystemengine_win.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qfilesystemiterator_win.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qfsfileengine.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qabstractfileengine.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qfsfileengine_win.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qfsfileengine_iterator.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qfileinfo.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qtemporaryfile.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qdiriterator.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qiodevice.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qbuffer.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qtextstream.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qurl.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qsettings.cpp \ + $$QT_SOURCE_TREE/src/corelib/io/qsettings_win.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qdatetime.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qlocale.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qbytearray.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qbytearraymatcher.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qvector.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qvsnprintf.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qlist.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qhash.cpp \ + $$QT_SOURCE_TREE/src/corelib/global/qglobal.cpp \ + $$QT_BUILD_TREE/src/corelib/global/qconfig.cpp \ + $$QT_SOURCE_TREE/src/corelib/global/qmalloc.cpp \ + $$QT_SOURCE_TREE/src/corelib/global/qnumeric.cpp \ + $$QT_SOURCE_TREE/src/corelib/global/qlibraryinfo.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qregexp.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qmap.cpp \ + $$QT_SOURCE_TREE/src/corelib/tools/qbitarray.cpp \ + $$QT_SOURCE_TREE/src/corelib/kernel/qmetatype.cpp \ + $$QT_SOURCE_TREE/src/corelib/kernel/qvariant.cpp \ + $$QT_SOURCE_TREE/src/corelib/codecs/qutfcodec.cpp \ + $$QT_SOURCE_TREE/src/corelib/xml/qxmlstream.cpp \ + $$QT_SOURCE_TREE/src/corelib/xml/qxmlutils.cpp + diff --git a/src/qtestlib/wince/cetest/cetcpsyncconnection.cpp b/src/qtestlib/wince/cetest/cetcpsyncconnection.cpp new file mode 100644 index 000000000..ba55c3928 --- /dev/null +++ b/src/qtestlib/wince/cetest/cetcpsyncconnection.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "CeTcpSyncConnection.h" +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo> + +static const QString ceTcpSyncProgram = "cetcpsync"; +extern void debugOutput(const QString& text, int level); + +CeTcpSyncConnection::CeTcpSyncConnection() + : AbstractRemoteConnection() + , connected(false) +{ +} + +CeTcpSyncConnection::~CeTcpSyncConnection() +{ + if (isConnected()) + disconnect(); +} + +bool CeTcpSyncConnection::connect(QVariantList&) +{ + // We connect with each command, so this is always true + // The command itself will fail then + const QString cmd = ceTcpSyncProgram + " noop"; + if (system(qPrintable(cmd)) != 0) + return false; + connected = true; + return true; +} + +void CeTcpSyncConnection::disconnect() +{ + connected = false; +} + +bool CeTcpSyncConnection::isConnected() const +{ + return connected; +} + +inline QString boolToString(bool b) +{ + return b ? "true" : "false"; +} + +static bool fileTimeFromString(FILETIME& ft, const QString& str) +{ + int idx = str.indexOf("*"); + if (idx <= 0) + return false; + bool ok; + ft.dwLowDateTime = str.left(idx).toULong(&ok); + if (!ok) + return false; + ft.dwHighDateTime = str.mid(idx+1).toULong(&ok); + return ok; +} + +static QString fileTimeToString(FILETIME& ft) +{ + return QString::number(ft.dwLowDateTime) + "*" + QString::number(ft.dwHighDateTime); +} + +bool CeTcpSyncConnection::copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists) +{ + QString cmd = ceTcpSyncProgram + " copyFileToDevice \"" + localSource + "\" \"" + deviceDest + "\" " + boolToString(failIfExists); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive) +{ + QString cmd = ceTcpSyncProgram + " copyDirectoryToDevice \"" + localSource + "\" \"" + deviceDest + "\" " + boolToString(recursive); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists) +{ + QString cmd = ceTcpSyncProgram + " copyFileFromDevice \"" + deviceSource + "\" \"" + localDest + "\" " + boolToString(failIfExists); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::copyDirectoryFromDevice(const QString &deviceSource, const QString &localDest, bool recursive) +{ + QString cmd = ceTcpSyncProgram + " copyDirectoryFromDevice \"" + deviceSource + "\" \"" + localDest + "\" " + boolToString(recursive); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::copyFile(const QString &srcFile, const QString &destFile, bool failIfExists) +{ + QString cmd = ceTcpSyncProgram + " copyFile \"" + srcFile + "\" \"" + destFile + "\" " + boolToString(failIfExists); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::copyDirectory(const QString &srcDirectory, const QString &destDirectory, + bool recursive) +{ + QString cmd = ceTcpSyncProgram + " copyDirectory \"" + srcDirectory + "\" \"" + destDirectory + "\" " + boolToString(recursive); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::deleteFile(const QString &fileName) +{ + QString cmd = ceTcpSyncProgram + " deleteFile \"" + fileName + "\""; + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::deleteDirectory(const QString &directory, bool recursive, bool failIfContentExists) +{ + QString cmd = ceTcpSyncProgram + " deleteDirectory \"" + directory + "\" " + boolToString(recursive) + " " + boolToString(failIfContentExists); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::execute(QString program, QString arguments, int timeout, int *returnValue) +{ + QString cmd = ceTcpSyncProgram + " execute \"" + program + "\" \"" + arguments + "\" " + QString::number(timeout); + int exitCode = system(qPrintable(cmd)); + if (returnValue) + *returnValue = exitCode; + return true; +} + +bool CeTcpSyncConnection::createDirectory(const QString &path, bool deleteBefore) +{ + QString cmd = ceTcpSyncProgram + " createDirectory \"" + path + "\" " + boolToString(deleteBefore); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::timeStampForLocalFileTime(FILETIME* fTime) const +{ + QString cmd = ceTcpSyncProgram + " timeStampForLocalFileTime " + fileTimeToString(*fTime) + " >qt_cetcpsyncdata.txt"; + if (system(qPrintable(cmd)) != 0) + return false; + + QFile file("qt_cetcpsyncdata.txt"); + if (!file.open(QIODevice::ReadOnly)) + return false; + + bool result = fileTimeFromString(*fTime, file.readLine()); + file.close(); + file.remove(); + return result; +} + +bool CeTcpSyncConnection::fileCreationTime(const QString &fileName, FILETIME* deviceCreationTime) const +{ + QString cmd = ceTcpSyncProgram + " fileCreationTime \"" + fileName + "\" >qt_cetcpsyncdata.txt"; + if (system(qPrintable(cmd)) != 0) + return false; + + QFile file("qt_cetcpsyncdata.txt"); + if (!file.open(QIODevice::ReadOnly)) + return false; + + bool result = fileTimeFromString(*deviceCreationTime, file.readLine()); + file.close(); + file.remove(); + return result; +} + +bool CeTcpSyncConnection::resetDevice() +{ + qWarning("CeTcpSyncConnection::resetDevice not implemented"); + return false; +} + +bool CeTcpSyncConnection::toggleDevicePower(int *returnValue) +{ + Q_UNUSED(returnValue); + qWarning("CeTcpSyncConnection::toggleDevicePower not implemented"); + return false; +} + +bool CeTcpSyncConnection::setDeviceAwake(bool activate, int *returnValue) +{ + Q_UNUSED(activate); + Q_UNUSED(returnValue); + qWarning("CeTcpSyncConnection::setDeviceAwake not implemented"); + return false; +} diff --git a/src/qtestlib/wince/cetest/cetcpsyncconnection.h b/src/qtestlib/wince/cetest/cetcpsyncconnection.h new file mode 100644 index 000000000..47150a11b --- /dev/null +++ b/src/qtestlib/wince/cetest/cetcpsyncconnection.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CETCPSYNC_REMOTECONNECTION_H +#define CETCPSYNC_REMOTECONNECTION_H + +#include "remoteconnection.h" + +class CeTcpSyncConnection : public AbstractRemoteConnection +{ +public: + CeTcpSyncConnection(); + virtual ~CeTcpSyncConnection(); + + bool connect(QVariantList &list = QVariantList()); + void disconnect(); + bool isConnected() const; + + // These functions are designed for transfer between desktop and device + // Caution: deviceDest path has to be device specific (eg. no drive letters for CE) + bool copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists = false); + bool copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive = true); + bool copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists = false); + bool copyDirectoryFromDevice(const QString &deviceSource, const QString &localDest, bool recursive = true); + + bool timeStampForLocalFileTime(FILETIME*) const; + bool fileCreationTime(const QString &fileName, FILETIME*) const; + + // These functions only work on files existing on the device + bool copyFile(const QString&, const QString&, bool failIfExists = false); + bool copyDirectory(const QString&, const QString&, bool recursive = true); + bool deleteFile(const QString&); + bool deleteDirectory(const QString&, bool recursive = true, bool failIfContentExists = false); + bool moveFile(const QString&, const QString&, bool FailIfExists = false); + bool moveDirectory(const QString&, const QString&, bool recursive = true); + + bool createDirectory(const QString&, bool deleteBefore=false); + + bool execute(QString program, QString arguments = QString(), int timeout = -1, int *returnValue = NULL); + bool resetDevice(); + bool toggleDevicePower(int *returnValue = NULL); + bool setDeviceAwake(bool activate, int *returnValue = NULL); +private: + bool connected; +}; + +#endif diff --git a/src/qtestlib/wince/cetest/cetest.pro b/src/qtestlib/wince/cetest/cetest.pro new file mode 100644 index 000000000..82747dada --- /dev/null +++ b/src/qtestlib/wince/cetest/cetest.pro @@ -0,0 +1,56 @@ +TEMPLATE = app +TARGET = cetest +DESTDIR = ../../../../bin +build_all:!build_pass { + CONFIG -= build_all + CONFIG += release +} + +CONFIG += console no_batch +CONFIG -= qt + +DEFINES += QT_BUILD_QMAKE QT_BOOTSTRAPPED QT_NO_CODECS QT_LITE_UNICODE QT_NO_LIBRARY \ + QT_NO_STL QT_NO_COMPRESS QT_NO_DATASTREAM \ + QT_NO_TEXTCODEC QT_NO_UNICODETABLES QT_NO_THREAD \ + QT_NO_SYSTEMLOCALE QT_NO_GEOM_VARIANT \ + QT_NODLL QT_NO_QOBJECT + +INCLUDEPATH = \ + $$QT_SOURCE_TREE/tools/qtestlib/ce/cetest \ + $$QT_SOURCE_TREE/qmake \ + $$QT_SOURCE_TREE/qmake/generators/symbian \ + $$QT_SOURCE_TREE/tools/shared \ + $$QT_BUILD_TREE/include \ + $$QT_BUILD_TREE/include/QtCore \ + $$QT_BUILD_TREE/src/corelib/global + +VPATH += $$QT_SOURCE_TREE/tools/shared + +DEPENDPATH += $$QT_BUILD_TREE/src/corelib/tools $$QT_BUILD_TREE/src/corelib/io + +# Input +HEADERS += \ + remoteconnection.h \ + deployment.h + +SOURCES += \ + remoteconnection.cpp \ + deployment.cpp \ + main.cpp + +LIBS += ole32.lib advapi32.lib + +isEmpty(QT_CE_RAPI_INC) { + DEFINES += QT_CETEST_NO_ACTIVESYNC + HEADERS += cetcpsyncconnection.h + SOURCES += cetcpsyncconnection.cpp +} else { + HEADERS += activesyncconnection.h + SOURCES += activesyncconnection.cpp + LIBS += rapi.lib + INCLUDEPATH += $$QT_CE_RAPI_INC + LIBS += -L$$QT_CE_RAPI_LIB +} + +include(qmake_include.pri) +include(bootstrapped.pri) diff --git a/src/qtestlib/wince/cetest/deployment.cpp b/src/qtestlib/wince/cetest/deployment.cpp new file mode 100644 index 000000000..cd6ce2806 --- /dev/null +++ b/src/qtestlib/wince/cetest/deployment.cpp @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "deployment.h" +#include "remoteconnection.h" +#include <option.h> +#include <qdir.h> +#include <qfile.h> +#include <qstring.h> + +extern void debugOutput(const QString& text, int level); + +bool DeploymentHandler::deviceCopy(const DeploymentList &deploymentList) +{ + for (int i=0; i<deploymentList.size(); ++i) { + CopyItem item = deploymentList.at(i); + m_connection->createDirectory(item.to.left(item.to.lastIndexOf(QLatin1Char('\\')))); + if (!m_connection->copyFileToDevice(item.from , item.to)) { + debugOutput(QString::fromLatin1("Error while copy: %1 -> %2").arg(item.from).arg(item.to),0); + return false; + } + } + return true; +} + +bool DeploymentHandler::deviceDeploy(const DeploymentList &deploymentList) +{ + DeploymentList copyList; + for (int i=0; i<deploymentList.size(); ++i) { +#if defined(Q_OS_WIN) + HANDLE localHandle = CreateFile(deploymentList.at(i).from.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + if (localHandle == INVALID_HANDLE_VALUE) { + copyList.append(deploymentList.at(i)); + continue; + } + FILETIME localCreationTime; + if (!GetFileTime(localHandle, NULL, NULL, &localCreationTime) || !m_connection->timeStampForLocalFileTime(&localCreationTime)) { + copyList.append(deploymentList.at(i)); + CloseHandle(localHandle); + continue; + } + CloseHandle(localHandle); + + FILETIME deviceCreationTime; + if (!m_connection->fileCreationTime(deploymentList.at(i).to , &deviceCreationTime)) { + copyList.append(deploymentList.at(i)); + continue; + } + + int res = CompareFileTime(&localCreationTime, &deviceCreationTime); + if (res != 0) + copyList.append(deploymentList.at(i)); + else + debugOutput(QString::fromLatin1("Skipping File %1, already latest version").arg(deploymentList.at(i).from),0); +#else + copyList.append(deploymentList.at(i)); +#endif + } + return deviceCopy(copyList); +} + +void DeploymentHandler::cleanup(const DeploymentList &deploymentList) +{ + for (int i=0; i<deploymentList.size(); ++i) { + m_connection->deleteFile(deploymentList.at(i).to); +#ifdef Q_OS_WIN + QString path = deploymentList.at(i).to; + int pos; + while ( (pos = path.lastIndexOf(QLatin1Char('\\'))) > 0) { + path = path.left(pos); + if (!m_connection->deleteDirectory(path, false, true)) + break; + } +#endif + } +} + +void DeploymentHandler::initQtDeploy(QMakeProject *project, DeploymentList &deploymentList, const QString &testPath) +{ + QString targetPath = project->values("deploy.path").join(" "); + if (targetPath.isEmpty()) + targetPath = testPath; + if (targetPath.endsWith("/") || targetPath.endsWith("\\")) + targetPath = targetPath.mid(0,targetPath.size()-1); + + // Only deploy Qt libs for shared build + if (!project->values("QMAKE_QT_DLL").isEmpty() && !project->values("QMAKE_LIBDIR").isEmpty()) { + QStringList libs = project->values("LIBS"); + QStringList qtLibs; + QStringList libPaths; + foreach (QString item, libs) { + + if (item.startsWith("-L")) { + // -L -> a directory containing DLLs + libPaths << item.mid(2); + continue; + } + + QStringList libCandidates; + + if (item.startsWith("-l")) { + // -l -> a library located within one of the standard library paths + QString lib = item.mid(2); + + // Check if it's a Qt library first, then check in all paths given with -L. + // Note Qt libraries get a `4' appended to them, others don't. + libCandidates << project->values("QMAKE_LIBDIR").at(0) + QDir::separator() + lib + QLatin1String("4.dll"); + foreach (QString const& libPath, libPaths) { + libCandidates << libPath + QDir::separator() + lib + QLatin1String(".dll"); + } + } else { + libCandidates << item.replace(".lib",".dll"); + } + + foreach (QString const& file, libCandidates) { + QFileInfo info(file); + if (info.exists()) { + qtLibs += info.dir().absoluteFilePath(info.fileName()); + break; + } + } + } + for (QStringList::ConstIterator it = qtLibs.constBegin(); it != qtLibs.constEnd(); ++it) { + QString dllName = *it; + QFileInfo info(dllName); + if (!info.exists()) + continue; + deploymentList.append(CopyItem(Option::fixPathToLocalOS(info.absoluteFilePath()) , + Option::fixPathToLocalOS(targetPath + "/" + info.fileName()))); + } + } + +#ifndef QT_CETEST_NO_ACTIVESYNC + // QtRemote deployment. We always deploy to \Windows + if (!project->values("QMAKE_LIBDIR").isEmpty()) { + QString remoteLibName = QLatin1String("QtRemote.dll"); + QString remoteLib = Option::fixPathToLocalOS(project->values("QMAKE_LIBDIR").at(0) + QDir::separator() + remoteLibName); + if (QFile::exists(remoteLib)) + deploymentList.append(CopyItem(remoteLib, QString::fromLatin1("\\Windows\\") + remoteLibName)); + else + debugOutput(QString::fromLatin1("Could not find QtRemote. Might not be able to launch target executable"),0); + } +#endif + + // C-runtime deployment + QString runtime = project->values("QT_CE_C_RUNTIME").join(QLatin1String(" ")); + debugOutput(QString::fromLatin1("Runtime:%1").arg(runtime), 2); + if (!runtime.isEmpty() && (runtime != QLatin1String("no"))) { + QString runtimeVersion = QLatin1String("msvcr"); + const QString mkspec = project->values("QMAKESPEC").first(); + if (mkspec.endsWith("2008")) + runtimeVersion.append("90"); + else + runtimeVersion.append("80"); + if (project->isActiveConfig("debug")) + runtimeVersion.append("d"); + runtimeVersion.append(".dll"); + + if (runtime == "yes") { + // Auto-find C-runtime + QString vcInstallDir = qgetenv("VCINSTALLDIR"); + if (!vcInstallDir.isEmpty()) { + vcInstallDir += "\\ce\\dll\\"; + vcInstallDir += project->values("CE_ARCH").join(QLatin1String(" ")); + if (!QFileInfo(vcInstallDir + QDir::separator() + runtimeVersion).exists()) + runtime.clear(); + else + runtime = vcInstallDir; + } + } + + if (!runtime.isEmpty()) { + deploymentList.append(CopyItem(Option::fixPathToLocalOS(runtime + "/" + runtimeVersion ) , + Option::fixPathToLocalOS(targetPath + "/" + runtimeVersion))); + } + } +} + +void DeploymentHandler::initProjectDeploy(QMakeProject* project, DeploymentList &deploymentList, const QString &testPath) +{ + QString targetPath = project->values("deploy.path").join(" "); + if (targetPath.isEmpty()) + targetPath = testPath; + if (targetPath.endsWith("/") || targetPath.endsWith("\\")) + targetPath = targetPath.mid(0,targetPath.size()-1); + + QStringList& list = project->values("DEPLOYMENT"); + if (list.isEmpty()) + return; + + for (int it = 0; it < list.size(); ++it) { + QString argSource = list.at(it); + QString argPath = list.at(it) + QString(".path"); + if (((project->values(argSource + QString(".files")).isEmpty() && project->values(argSource + QString(".sources")).isEmpty()) || project->values(argPath).isEmpty()) && list.at(it) != "deploy") { + debugOutput(QString::fromLatin1("cannot deploy \"%1\" because of missing data.").arg(list.at(it)), 0); + continue; + } + + QString addPath = project->values(argPath).join(QLatin1String(" ")); + if (addPath == QLatin1String(".")) + addPath.clear(); + if (!addPath.startsWith("/") && !addPath.startsWith(QLatin1String("\\"))) + addPath = targetPath + "/" + addPath; + + QStringList addSources = project->values(argSource + QString(".files")) + project->values(argSource + QString(".sources")); + addSources.replaceInStrings(QLatin1String("/"), QLatin1String("\\")); + for(int index=0; index < addSources.size(); ++index) { + QString dirstr = qmake_getpwd(); + QString filestr = Option::fixPathToLocalOS(addSources.at(index), false, false); + int slsh = filestr.lastIndexOf(Option::dir_sep); + if(slsh != -1) { + dirstr = filestr.left(slsh+1); + filestr = filestr.right(filestr.length() - slsh - 1); + } + if(dirstr.right(Option::dir_sep.length()) != Option::dir_sep) + dirstr += Option::dir_sep; + QFileInfo info(dirstr + filestr); + + static int addQMakeDeployCounter = 0; + QStringList entryList = info.absoluteDir().entryList(QStringList() << info.fileName()); + if (entryList.size() > 1) { + foreach(QString s, entryList) { + // We do not include directories when using wildcards + QFileInfo wildInfo(info.absolutePath() + "/" + s); + if (wildInfo.isDir()) { + continue; + } + QString appendedQmakeDeploy = QString::fromLatin1("_q_make_additional_deploy_%1").arg(addQMakeDeployCounter++); + project->parse(appendedQmakeDeploy + QLatin1String(".files = \"") + wildInfo.absoluteFilePath()); + project->parse(appendedQmakeDeploy + QLatin1String(".path = \"") + addPath); + list.append(appendedQmakeDeploy); + } + continue; + } + + if (info.isDir()) { + QDir additionalDir(dirstr + filestr); + QStringList additionalEntries = additionalDir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::NoSymLinks); + foreach(QString item, additionalEntries) { + QString appendedDeploy = QString::fromLatin1("_q_make_additional_deploy_%1").arg(addQMakeDeployCounter++); + project->parse(appendedDeploy + QLatin1String(".files = \"") + Option::fixPathToLocalOS(additionalDir.absoluteFilePath(item)) + QLatin1String("\"")); + QString appendTargetPath = project->values(argPath).join(QLatin1String(" ")); + if (appendTargetPath == QLatin1String(".")) + appendTargetPath = filestr; + else + appendTargetPath.append(QLatin1String("\\") + filestr); + project->parse(appendedDeploy + QLatin1String(".path = ") + appendTargetPath); + list.append(appendedDeploy); + } + } else if (entryList.size() == 1) + deploymentList.append(CopyItem(Option::fixPathToLocalOS(info.absolutePath() + "/" + entryList.at(0)) , + Option::fixPathToLocalOS(addPath + "/" + entryList.at(0)))); + } + } +} diff --git a/src/qtestlib/wince/cetest/deployment.h b/src/qtestlib/wince/cetest/deployment.h new file mode 100644 index 000000000..9d0b9caec --- /dev/null +++ b/src/qtestlib/wince/cetest/deployment.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DEPLOYMENT_INCL +#define DEPLOYMENT_INCL + +#include <qstring.h> +#include <qlist.h> +#include <project.h> + +class AbstractRemoteConnection; + +struct CopyItem +{ + CopyItem(const QString& f, const QString& t) : from(f) , to(t) { } + QString from; + QString to; +}; +typedef QList<CopyItem> DeploymentList; + +class DeploymentHandler +{ +public: + inline void setConnection(AbstractRemoteConnection*); + inline AbstractRemoteConnection* connection() const; + bool deviceCopy(const DeploymentList &deploymentList); + bool deviceDeploy(const DeploymentList &deploymentList); + void cleanup(const DeploymentList &deploymentList); + static void initProjectDeploy(QMakeProject* project, DeploymentList &deploymentList, const QString &testPath = "\\Program Files\\qt_test"); + static void initQtDeploy(QMakeProject* project, DeploymentList &deploymentList, const QString &testPath = "\\Program Files\\qt_test"); +private: + AbstractRemoteConnection* m_connection; +}; + +inline void DeploymentHandler::setConnection(AbstractRemoteConnection *connection) { m_connection = connection; } +inline AbstractRemoteConnection* DeploymentHandler::connection() const { return m_connection; } +#endif
\ No newline at end of file diff --git a/src/qtestlib/wince/cetest/main.cpp b/src/qtestlib/wince/cetest/main.cpp new file mode 100644 index 000000000..7e9ea5dfb --- /dev/null +++ b/src/qtestlib/wince/cetest/main.cpp @@ -0,0 +1,445 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef QT_CETEST_NO_ACTIVESYNC +# include "cetcpsyncconnection.h" +#else +# include "activesyncconnection.h" +#endif + +const int SLEEP_AFTER_RESET = 60000; // sleep for 1 minute +const int SLEEP_RECONNECT = 2000; // sleep for 2 seconds before trying another reconnect + +#include "deployment.h" +#include <option.h> +#include <project.h> +#include <property.h> +#include <qstringlist.h> +#include <qfileinfo.h> +#include <qdir.h> +#include <iostream> +using namespace std; + +const int debugLevel = 0; +void debugOutput(const QString& text, int level) +{ + if (level <= debugLevel) + cout << qPrintable(text) << endl; +} + +// needed for QMake sources to compile +QString project_builtin_regx() { return QString();} +static QString pwd; +QString qmake_getpwd() +{ + if(pwd.isNull()) + pwd = QDir::currentPath(); + return pwd; +} +bool qmake_setpwd(const QString &p) +{ + if(QDir::setCurrent(p)) { + pwd = QDir::currentPath(); + return true; + } + return false; +} + +namespace TestConfiguration { + QString localExecutable; + QString localQtConf; + QString remoteTestPath; + QString remoteLibraryPath; + QString remoteExecutable; + QString remoteResultFile; + + bool testDebug; + void init() + { + testDebug = true; + localQtConf = QLatin1String("no"); + remoteTestPath = QLatin1String("\\Program Files\\qt_test"); + remoteLibraryPath = remoteTestPath; + remoteResultFile = QLatin1String("\\qt_test_results.txt"); + } +} + +void usage() +{ + cout << + "QTestLib options\n" + " -functions : Returns a list of current testfunctions\n" + " -xml : Outputs results as XML document\n" + " -lightxml : Outputs results as stream of XML tags\n" + " -o filename: Writes all output into a file\n" + " -silent : Only outputs warnings and failures\n" + " -v1 : Print enter messages for each testfunction\n" + " -v2 : Also print out each QVERIFY/QCOMPARE/QTEST\n" + " -vs : Print every signal emitted\n" + " -eventdelay ms : Set default delay for mouse and keyboard simulation to ms milliseconds\n" + " -keydelay ms : Set default delay for keyboard simulation to ms milliseconds\n" + " -mousedelay ms : Set default delay for mouse simulation to ms milliseconds\n" + " -keyevent-verbose : Turn on verbose messages for keyboard simulation\n" + " -maxwarnings n : Sets the maximum amount of messages to output.\n" + " 0 means unlimited, default: 2000\n" + " -help : This help\n"; + cout << + "cetest specific options\n" + " -debug : Test debug version[default]\n" + " -release : Test release version\n" + " -libpath <path> : Remote path to deploy Qt libraries to\n" + " -reset : Reset device before starting a test\n" + " -awake : Device does not go sleep mode\n" + " -qt-delete : Delete the Qt libraries after execution\n" + " -project-delete : Delete the project file(s) after execution\n" + " -delete : Delete everything deployed after execution\n" + " -conf : Specify location of qt.conf file\n" + " -f <file> : Specify project file\n" + " -cache <file> : Specify .qmake.cache file to use\n" + " -d : Increase qmake debugging \n" + " -timeout <value> : Specify a timeout value after which the test will be terminated\n" + " -1 specifies waiting forever (default)\n" + " 0 specifies starting the process detached\n" + " >0 wait <value> seconds\n" + "\n"; +} + +int main(int argc, char **argv) +{ + QStringList arguments; + for (int i=0; i<argc; ++i) + arguments.append(QString::fromLatin1(argv[i])); + + TestConfiguration::init(); + + QStringList launchArguments; + QString resultFile; + QString proFile; + QString cacheFile; + int timeout = -1; + bool cleanupQt = false; + bool cleanupProject = false; + bool deviceReset = false; + bool keepAwake = false; + + for (int i=1; i<arguments.size(); ++i) { + if (arguments.at(i).toLower() == QLatin1String("-help") + || arguments.at(i).toLower() == QLatin1String("--help") + || arguments.at(i).toLower() == QLatin1String("/?")) { + usage(); + return 0; + } else if (arguments.at(i).toLower() == QLatin1String("-o")) { + if (++i == arguments.size()) { + cout << "Error: No output file specified!" << endl; + return -1; + } + resultFile = arguments.at(i); + } else if (arguments.at(i).toLower() == QLatin1String("-eventdelay") + || arguments.at(i).toLower() == QLatin1String("-keydelay") + || arguments.at(i).toLower() == QLatin1String("-mousedelay") + || arguments.at(i).toLower() == QLatin1String("-maxwarnings")) { + launchArguments.append(arguments.at(i++)); + if (i == arguments.size()) { + cout << "Please specify value for:" << qPrintable(arguments.at(i-1).mid(1)) << endl; + return -1; + } + launchArguments.append(arguments.at(i)); + } else if (arguments.at(i).toLower() == QLatin1String("-debug")) { + TestConfiguration::testDebug = true; + Option::before_user_vars.append("CONFIG-=release"); + Option::before_user_vars.append("CONFIG+=debug"); + } else if (arguments.at(i).toLower() == QLatin1String("-release")) { + TestConfiguration::testDebug = false; + Option::before_user_vars.append("CONFIG-=debug"); + Option::before_user_vars.append("CONFIG+=release"); + } else if (arguments.at(i).toLower() == QLatin1String("-libpath")) { + if (++i == arguments.size()) { + cout << "Error: No library path specified!" << endl; + return -1; + } + TestConfiguration::remoteLibraryPath = arguments.at(i); + } else if (arguments.at(i).toLower() == QLatin1String("-qt-delete")) { + cleanupQt = true; + } else if (arguments.at(i).toLower() == QLatin1String("-project-delete")) { + cleanupProject = true; + } else if (arguments.at(i).toLower() == QLatin1String("-delete")) { + cleanupQt = true; + cleanupProject = true; + } else if (arguments.at(i).toLower() == QLatin1String("-reset")) { + deviceReset = true; + } else if (arguments.at(i).toLower() == QLatin1String("-awake")) { + keepAwake = true; + } else if (arguments.at(i).toLower() == QLatin1String("-conf")) { + if (++i == arguments.size()) { + cout << "Error: No qt.conf file specified!" << endl; + return -1; + } + if (!QFileInfo(arguments.at(i)).exists()) + cout << "Warning: could not find qt.conf file at:" << qPrintable(arguments.at(i)) << endl; + else + TestConfiguration::localQtConf = arguments.at(i); + } else if (arguments.at(i).toLower() == QLatin1String("-f")) { + if (++i == arguments.size()) { + cout << "Error: No output file specified!" << endl; + return -1; + } + proFile = arguments.at(i); + } else if (arguments.at(i).toLower() == QLatin1String("-cache")) { + if (++i == arguments.size()) { + cout << "Error: No cache file specified!" << endl; + return -1; + } + cacheFile = arguments.at(i); + } else if (arguments.at(i).toLower() == QLatin1String("-d")) { + Option::debug_level++; + } else if (arguments.at(i).toLower() == QLatin1String("-timeout")) { + if (++i == arguments.size()) { + cout << "Error: No timeout value specified!" << endl; + return -1; + } + timeout = QString(arguments.at(i)).toInt(); + } else { + launchArguments.append(arguments.at(i)); + } + } + + // check for .pro file + if (proFile.isEmpty()) { + proFile = QDir::current().dirName() + QLatin1String(".pro"); + if (!QFileInfo(proFile).exists()) { + cout << "Error: Could not find project file in current directory." << endl; + return -1; + } + debugOutput(QString::fromLatin1("Using Project File:").append(proFile),1); + }else { + if (!QFileInfo(proFile).exists()) { + cout << "Error: Project file does not exist " << qPrintable(proFile) << endl; + return -1; + } + } + + Option::before_user_vars.append("CONFIG+=build_pass"); + + // read target and deployment rules passing the .pro to use instead of + // relying on qmake guessing the .pro to use + int qmakeArgc = 2; + QByteArray ba(QFile::encodeName(proFile)); + char* proFileEncodedName = ba.data(); + char* qmakeArgv[2] = { "qmake.exe", proFileEncodedName }; + + Option::qmake_mode = Option::QMAKE_GENERATE_NOTHING; + Option::output_dir = qmake_getpwd(); + if (!cacheFile.isEmpty()) + Option::mkfile::cachefile = cacheFile; + int ret = Option::init(qmakeArgc, qmakeArgv); + if(ret != Option::QMAKE_CMDLINE_SUCCESS) { + cout << "Error: could not parse " << qPrintable(proFile) << endl; + return -1; + } + + QMakeProperty prop; + QMakeProject project(&prop); + + project.read(proFile); + if (project.values("TEMPLATE").join(" ").toLower() != QString("app")) { + cout << "Error: Can only test executables!" << endl; + return -1; + } + // Check whether the project is still in debug/release mode after reading + // If .pro specifies to be one mode only, we need to accept this + if (project.isActiveConfig("debug") && !project.isActiveConfig("release")) { + TestConfiguration::testDebug = true; + debugOutput("ActiveConfig: debug only in .pro.", 1); + } + if (!project.isActiveConfig("debug") && project.isActiveConfig("release")) { + TestConfiguration::testDebug = false; + debugOutput("ActiveConfig: release only in .pro.", 1); + } + + // determine what is the real mkspec to use if the default mkspec is being used + if (Option::mkfile::qmakespec.endsWith("/default")) + project.values("QMAKESPEC") = project.values("QMAKESPEC_ORIGINAL"); + else + project.values("QMAKESPEC") = QStringList() << Option::mkfile::qmakespec; + + // ensure that QMAKESPEC is non-empty .. to meet requirements of QList::at() + if (project.values("QMAKESPEC").isEmpty()){ + cout << "Error: QMAKESPEC not set after parsing " << qPrintable(proFile) << endl; + return -1; + } + + // ensure that QT_CE_C_RUNTIME is non-empty .. to meet requirements of QList::at() + if (project.values("QT_CE_C_RUNTIME").isEmpty()){ + cout << "Error: QT_CE_C_RUNTIME not defined in mkspec/qconfig.pri " << qPrintable(project.values("QMAKESPEC").join(" ")); + return -1; + } + + QString destDir = project.values("DESTDIR").join(" "); + if (!destDir.isEmpty()) { + if (QDir::isRelativePath(destDir)) { + QFileInfo fi(proFile); + if (destDir == QLatin1String(".")) + destDir = fi.absolutePath() + "/" + destDir + "/" + (TestConfiguration::testDebug ? "debug" : "release"); + else + destDir = fi.absolutePath() + QDir::separator() + destDir; + } + } else { + QFileInfo fi(proFile); + destDir = fi.absolutePath(); + destDir += QDir::separator() + QLatin1String(TestConfiguration::testDebug ? "debug" : "release"); + } + + DeploymentList qtDeploymentList; + DeploymentList projectDeploymentList; + + TestConfiguration::localExecutable = Option::fixPathToLocalOS(destDir + QDir::separator() + project.values("TARGET").join(" ") + QLatin1String(".exe")); + TestConfiguration::remoteTestPath = QLatin1String("\\Program Files\\") + Option::fixPathToLocalOS(project.values("TARGET").join(QLatin1String(" "))); + if (!arguments.contains(QLatin1String("-libpath"), Qt::CaseInsensitive)) + TestConfiguration::remoteLibraryPath = TestConfiguration::remoteTestPath; + + QString targetExecutable = Option::fixPathToLocalOS(project.values("TARGET").join(QLatin1String(" "))); + int last = targetExecutable.lastIndexOf(QLatin1Char('\\')); + targetExecutable = targetExecutable.mid( last == -1 ? 0 : last+1 ); + TestConfiguration::remoteExecutable = TestConfiguration::remoteTestPath + QDir::separator() + targetExecutable + QLatin1String(".exe"); + projectDeploymentList.append(CopyItem(TestConfiguration::localExecutable , TestConfiguration::remoteExecutable)); + + // deploy +#ifdef QT_CETEST_NO_ACTIVESYNC + CeTcpSyncConnection connection; +#else + ActiveSyncConnection connection; +#endif + if (!connection.connect()) { + cout << "Error: Could not connect to device!" << endl; + return -1; + } + DeploymentHandler deployment; + deployment.setConnection(&connection); + + deployment.initQtDeploy(&project, qtDeploymentList, TestConfiguration::remoteLibraryPath); + deployment.initProjectDeploy(&project , projectDeploymentList, TestConfiguration::remoteTestPath); + + // add qt.conf + if (TestConfiguration::localQtConf != QLatin1String("no")) { + QString qtConfOrigin = QFileInfo(TestConfiguration::localQtConf).absoluteFilePath(); + QString qtConfTarget = Option::fixPathToLocalOS(TestConfiguration::remoteTestPath + QDir::separator() + QLatin1String("qt.conf")); + projectDeploymentList.append(CopyItem(qtConfOrigin, qtConfTarget)); + } + + if (!deployment.deviceDeploy(qtDeploymentList) || !deployment.deviceDeploy(projectDeploymentList)) { + cout << "Error: Could not copy file(s) to device" << endl; + return -1; + } + // device power mode + if (keepAwake) + { + int retVal = 0; + if (!connection.setDeviceAwake(true, &retVal)) { + cout << "Error: Could not set unattended mode on device" << endl; + return -1; + } + } + + // reset device + if (deviceReset) + { + if (!connection.resetDevice()) { + //if (!connection.toggleDevicePower( &retVal)) { + cout << "Error: Could not reset the device" << endl; + return -1; + } + cout << " Entering sleep after reset for " << SLEEP_AFTER_RESET / 1000 << " seconds ... " << endl; + Sleep(SLEEP_AFTER_RESET); + cout << " ... woke up. " << endl; + connection.disconnect(); + // reconnect after reset + int retryCount = 21; + while (--retryCount) + { + if (!connection.connect()) + Sleep(SLEEP_RECONNECT); + else + break; + } + if (!connection.isConnected()) + { + cout << "Error: Could not connect to device!" << endl; + return -1; + } + } + + // launch + launchArguments.append("-o"); + launchArguments.append(TestConfiguration::remoteResultFile); + + cout << endl << "Remote Launch:" << qPrintable(TestConfiguration::remoteExecutable) << " " << qPrintable(launchArguments.join(" ")) << endl; + if (!connection.execute(TestConfiguration::remoteExecutable, launchArguments.join(" "), timeout)) { + cout << "Error: Could not execute target file" << endl; + return -1; + } + + + // copy result file + // show results + if (resultFile.isEmpty()) { + QString tempResultFile = Option::fixPathToLocalOS(QDir::tempPath() + "/qt_ce_temp_result_file.txt"); + if (connection.copyFileFromDevice(TestConfiguration::remoteResultFile, tempResultFile)) { + QFile file(tempResultFile); + QByteArray arr; + if (file.open(QIODevice::ReadOnly)) { + arr = file.readAll(); + cout << arr.constData() << endl; + } + file.close(); + file.remove(); + } + } else { + connection.copyFileFromDevice(TestConfiguration::remoteResultFile, resultFile); + } + + // delete + connection.deleteFile(TestConfiguration::remoteResultFile); + if (cleanupQt) + deployment.cleanup(qtDeploymentList); + if (cleanupProject) + deployment.cleanup(projectDeploymentList); + return 0; +} diff --git a/src/qtestlib/wince/cetest/qmake_include.pri b/src/qtestlib/wince/cetest/qmake_include.pri new file mode 100644 index 000000000..35fbc64a6 --- /dev/null +++ b/src/qtestlib/wince/cetest/qmake_include.pri @@ -0,0 +1,13 @@ +#qmake source files needed for cetest +HEADERS += \ + $$QT_SOURCE_TREE/qmake/option.h +SOURCES += \ + $$QT_SOURCE_TREE/qmake/option.cpp \ + $$QT_SOURCE_TREE/qmake/project.cpp \ + $$QT_SOURCE_TREE/qmake/property.cpp \ + $$QT_SOURCE_TREE/qmake/generators/metamakefile.cpp \ + $$QT_SOURCE_TREE/qmake/generators/symbian/initprojectdeploy_symbian.cpp \ + $$QT_SOURCE_TREE/tools/shared/symbian/epocroot.cpp \ + $$QT_SOURCE_TREE/tools/shared/windows/registry.cpp + +DEFINES += QT_QMAKE_PARSER_ONLY diff --git a/src/qtestlib/wince/cetest/remoteconnection.cpp b/src/qtestlib/wince/cetest/remoteconnection.cpp new file mode 100644 index 000000000..aa3059cd3 --- /dev/null +++ b/src/qtestlib/wince/cetest/remoteconnection.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "remoteconnection.h" + +QByteArray strwinerror(DWORD errorcode) +{ + QByteArray out(512, 0); + + DWORD ok = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + errorcode, + 0, + out.data(), + out.size(), + 0 + ); + + if (!ok) { + qsnprintf(out.data(), out.size(), + "(error %d; additionally, error %d while looking up error string)", + (int)errorcode, (int)GetLastError()); + } + else { + out.resize(qstrlen(out.constData())); + if (out.endsWith("\r\n")) + out.chop(2); + + /* Append error number to error message for good measure */ + out.append(" (0x"); + out.append(QByteArray::number(uint(errorcode), 16).rightJustified(8, '0')); + out.append(")"); + } + return out; +} + +AbstractRemoteConnection::AbstractRemoteConnection() +{ +} + +AbstractRemoteConnection::~AbstractRemoteConnection() +{ +} + + +// Slow but should be ok... +bool AbstractRemoteConnection::moveFile(const QString &src, const QString &dest, bool FailIfExists) +{ + bool result = copyFile(src, dest, FailIfExists); + deleteFile(src); + return result; +} + +// Slow but should be ok... +bool AbstractRemoteConnection::moveDirectory(const QString &src, const QString &dest, bool recursive) +{ + bool result = copyDirectory(src, dest, true); + deleteDirectory(src, recursive); + return result; +} + diff --git a/src/qtestlib/wince/cetest/remoteconnection.h b/src/qtestlib/wince/cetest/remoteconnection.h new file mode 100644 index 000000000..d2671397d --- /dev/null +++ b/src/qtestlib/wince/cetest/remoteconnection.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef REMOTECONNECTION_H +#define REMOTECONNECTION_H + +#include <QtCore/QString> +#include <QtCore/QVariant> +#include <windows.h> +class AbstractRemoteConnection +{ +public: + AbstractRemoteConnection(); + virtual ~AbstractRemoteConnection(); + + virtual bool connect(QVariantList&) = 0; + virtual void disconnect() = 0; + virtual bool isConnected() const = 0; + + // These functions are designed for transfer between desktop and device + // Caution: deviceDest path has to be device specific (eg. no drive letters for CE) + virtual bool copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists = false) = 0; + virtual bool copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive = true) = 0; + virtual bool copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists = false) = 0; + virtual bool copyDirectoryFromDevice(const QString &deviceSource, const QString &localDest, bool recursive = true) = 0; + + // For "intelligent deployment" we need to investigate on filetimes on the device + virtual bool timeStampForLocalFileTime(FILETIME*) const = 0; + virtual bool fileCreationTime(const QString &fileName, FILETIME*) const = 0; + + // These functions only work on files existing on the device + virtual bool copyFile(const QString&, const QString&, bool failIfExists = false) = 0; + virtual bool copyDirectory(const QString&, const QString&, bool recursive = true) = 0; + virtual bool deleteFile(const QString&) = 0; + virtual bool deleteDirectory(const QString&, bool recursive = true, bool failIfContentExists = false) = 0; + bool moveFile(const QString&, const QString&, bool FailIfExists = false); + bool moveDirectory(const QString&, const QString&, bool recursive = true); + + virtual bool createDirectory(const QString&, bool deleteBefore=false) = 0; + + virtual bool execute(QString program, QString arguments = QString(), int timeout = -1, int *returnValue = NULL) = 0; +}; + +QByteArray strwinerror(DWORD); + +#endif diff --git a/src/qtestlib/wince/remotelib/commands.cpp b/src/qtestlib/wince/remotelib/commands.cpp new file mode 100644 index 000000000..32d6e33ae --- /dev/null +++ b/src/qtestlib/wince/remotelib/commands.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commands.h" +#include <Pm.h> +#include <Pmpolicy.h> + + +#define CLEAN_FAIL(a) {delete appName; \ + delete arguments; \ + return (a); } + +int qRemoteLaunch(DWORD, BYTE*, DWORD*, BYTE**, IRAPIStream* stream) +{ + if (!stream) + return -1; + + DWORD bytesRead; + int appLength; + wchar_t* appName = 0; + int argumentsLength; + wchar_t* arguments = 0; + int timeout = -1; + int returnValue = -2; + DWORD error = 0; + + if (S_OK != stream->Read(&appLength, sizeof(appLength), &bytesRead)) + CLEAN_FAIL(-2); + appName = (wchar_t*) malloc(sizeof(wchar_t)*(appLength + 1)); + if (S_OK != stream->Read(appName, sizeof(wchar_t)*appLength, &bytesRead)) + CLEAN_FAIL(-2); + appName[appLength] = '\0'; + + if (S_OK != stream->Read(&argumentsLength, sizeof(argumentsLength), &bytesRead)) + CLEAN_FAIL(-2); + arguments = (wchar_t*) malloc(sizeof(wchar_t)*(argumentsLength + 1)); + if (S_OK != stream->Read(arguments, sizeof(wchar_t)*argumentsLength, &bytesRead)) + CLEAN_FAIL(-2); + arguments[argumentsLength] = '\0'; + + if (S_OK != stream->Read(&timeout, sizeof(timeout), &bytesRead)) + CLEAN_FAIL(-2); + + bool result = qRemoteExecute(appName, arguments, &returnValue, &error, timeout); + + if (timeout != 0) { + if (S_OK != stream->Write(&returnValue, sizeof(returnValue), &bytesRead)) + CLEAN_FAIL(-4); + if (S_OK != stream->Write(&error, sizeof(error), &bytesRead)) + CLEAN_FAIL(-5); + } + delete appName; + delete arguments; + // We need to fail here for the execute, otherwise the calling application will wait + // forever for the returnValue. + if (!result) + return -3; + return S_OK; +} + + +bool qRemoteExecute(const wchar_t* program, const wchar_t* arguments, int *returnValue, DWORD* error, int timeout) +{ + *error = 0; + + if (!program) + return false; + + PROCESS_INFORMATION pid; + if (!CreateProcess(program, arguments, NULL, NULL, false, 0, NULL, NULL, NULL, &pid)) { + *error = GetLastError(); + wprintf(L"Could not launch: %s\n", program); + return false; + } + + // Timeout is in seconds + DWORD waitingTime = (timeout == -1) ? INFINITE : timeout * 1000; + + if (waitingTime != 0) { + DWORD waitStatus = WaitForSingleObject(pid.hProcess, waitingTime); + if (waitStatus == WAIT_TIMEOUT) { + TerminateProcess(pid.hProcess, 2); + return false; + } else if (waitStatus == WAIT_OBJECT_0 && returnValue) { + *returnValue = 0; + DWORD exitCode; + if (GetExitCodeProcess(pid.hProcess, &exitCode)) + *returnValue = exitCode; + } + } + return true; +} +/** +\brief Reset the device. +*/ +int qRemoteSoftReset(DWORD, BYTE*, DWORD*, BYTE**, IRAPIStream* stream) +{ + //POWER_STATE_ON On state + //POWER_STATE_OFF Off state + //POWER_STATE_CRITICAL Critical state + //POWER_STATE_BOOT Boot state + //POWER_STATE_IDLE Idle state + //POWER_STATE_SUSPEND Suspend state + //POWER_STATE_RESET Reset state + + DWORD returnValue = SetSystemPowerState(0, POWER_STATE_RESET, POWER_FORCE); + return returnValue; +} + +/** +\brief Toggle the unattended powermode of the device +*/ +int qRemoteToggleUnattendedPowerMode(DWORD, BYTE*, DWORD*, BYTE**, IRAPIStream* stream) +{ + if (!stream) + return -1; + + DWORD bytesRead; + int toggleVal = 0; + int returnValue = S_OK; + + if (S_OK != stream->Read(&toggleVal, sizeof(toggleVal), &bytesRead)) + return -2; + + //PPN_REEVALUATESTATE 0x0001 Reserved. Set dwData to zero (0). + //PPN_POWERCHANGE 0x0002 Reserved. Set dwData to zero (0). + //PPN_UNATTENDEDMODE 0x0003 Set dwData to TRUE or FALSE. + //PPN_SUSPENDKEYPRESSED or + //PPN_POWERBUTTONPRESSED 0x0004 Reserved. Set dwData to zero (0). + //PPN_SUSPENDKEYRELEASED 0x0005 Reserved. Set dwData to zero (0). + //PPN_APPBUTTONPRESSED 0x0006 Reserved. Set dwData to zero (0). + //PPN_OEMBASE Greater than or equal to 0x10000 + //You can define higher values, such as 0x10001, 0x10002, and so on. + // Reserved. Set dwData to zero (0). + returnValue = PowerPolicyNotify(PPN_UNATTENDEDMODE, toggleVal); + + if (S_OK != stream->Write(&returnValue, sizeof(returnValue), &bytesRead)) + return -3; + else + return S_OK; +} + +/** +\brief Virtually press the power button of the device +*/ +int qRemotePowerButton(DWORD, BYTE*, DWORD*, BYTE**, IRAPIStream* stream) +{ + if (!stream) + return -1; + + DWORD bytesRead; + int toggleVal = 0; + int returnValue = S_OK; + + if (S_OK != stream->Read(&toggleVal, sizeof(toggleVal), &bytesRead)) + return -2; + + //PPN_REEVALUATESTATE 0x0001 Reserved. Set dwData to zero (0). + //PPN_POWERCHANGE 0x0002 Reserved. Set dwData to zero (0). + //PPN_UNATTENDEDMODE 0x0003 Set dwData to TRUE or FALSE. + //PPN_SUSPENDKEYPRESSED or + //PPN_POWERBUTTONPRESSED 0x0004 Reserved. Set dwData to zero (0). + //PPN_SUSPENDKEYRELEASED 0x0005 Reserved. Set dwData to zero (0). + //PPN_APPBUTTONPRESSED 0x0006 Reserved. Set dwData to zero (0). + //PPN_OEMBASE Greater than or equal to 0x10000 + //You can define higher values, such as 0x10001, 0x10002, and so on. + // Reserved. Set dwData to zero (0). + returnValue = PowerPolicyNotify(PPN_POWERBUTTONPRESSED, 0); + + if (S_OK != stream->Write(&returnValue, sizeof(returnValue), &bytesRead)) + return -3; + else + return S_OK; +} + diff --git a/src/qtestlib/wince/remotelib/commands.h b/src/qtestlib/wince/remotelib/commands.h new file mode 100644 index 000000000..959aed625 --- /dev/null +++ b/src/qtestlib/wince/remotelib/commands.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QREMOTECOMMANDS_H +#define QREMOTECOMMANDS_H +#include <winbase.h> +#include <rapi.h> + +extern "C" { + int __declspec(dllexport) qRemoteLaunch(DWORD, BYTE*, DWORD*, BYTE**, IRAPIStream*); + int __declspec(dllexport) qRemoteSoftReset(DWORD, BYTE*, DWORD*, BYTE**, IRAPIStream* stream); + int __declspec(dllexport) qRemoteToggleUnattendedPowerMode(DWORD, BYTE*, DWORD*, BYTE**, IRAPIStream* stream); + int __declspec(dllexport) qRemotePowerButton(DWORD, BYTE*, DWORD*, BYTE**, IRAPIStream* stream); + bool __declspec(dllexport) qRemoteExecute(const wchar_t* program, const wchar_t* arguments = NULL, int *returnValue = NULL , DWORD* error = NULL, int timeout = -1); +} + +#endif diff --git a/src/qtestlib/wince/remotelib/remotelib.pro b/src/qtestlib/wince/remotelib/remotelib.pro new file mode 100644 index 000000000..dc61c7ffd --- /dev/null +++ b/src/qtestlib/wince/remotelib/remotelib.pro @@ -0,0 +1,15 @@ +TEMPLATE = lib +CONFIG += dll +CONFIG -= staticlib +TARGET = QtRemote +DESTDIR = ../../../../lib +DEPENDPATH += . +INCLUDEPATH += . +QT = +# Input +HEADERS += commands.h +SOURCES += commands.cpp + +INCLUDEPATH += $$QT_CE_RAPI_INC +LIBS += -L$$QT_CE_RAPI_LIB + diff --git a/src/qtestlib/wince/wince.pro b/src/qtestlib/wince/wince.pro new file mode 100644 index 000000000..7c88d6572 --- /dev/null +++ b/src/qtestlib/wince/wince.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +wince*:SUBDIRS = remotelib
\ No newline at end of file |