Commit f19760e5 authored by Lorenz Meier's avatar Lorenz Meier

Merge pull request #452 from DonLakeFlyer/TCPLinkUnitTest

TcpLink unit test
parents 9d42f680 f00d8adf
...@@ -751,13 +751,17 @@ CONFIG(debug, debug|release) { ...@@ -751,13 +751,17 @@ CONFIG(debug, debug|release) {
src/qgcunittest/UASUnitTest.h \ src/qgcunittest/UASUnitTest.h \
src/qgcunittest/MockUASManager.h \ src/qgcunittest/MockUASManager.h \
src/qgcunittest/MockUAS.h \ src/qgcunittest/MockUAS.h \
src/qgcunittest/MockQGCUASParamManager.h src/qgcunittest/MockQGCUASParamManager.h \
src/qgcunittest/MultiSignalSpy.h \
src/qgcunittest/TCPLinkTest.h
SOURCES += \ SOURCES += \
src/qgcunittest/UASUnitTest.cc \ src/qgcunittest/UASUnitTest.cc \
src/qgcunittest/MockUASManager.cc \ src/qgcunittest/MockUASManager.cc \
src/qgcunittest/MockUAS.cc \ src/qgcunittest/MockUAS.cc \
src/qgcunittest/MockQGCUASParamManager.cc src/qgcunittest/MockQGCUASParamManager.cc \
src/qgcunittest/MultiSignalSpy.cc \
src/qgcunittest/TCPLinkTest.cc
} }
# Enable Google Earth only on Mac OS and Windows with Visual Studio compiler # Enable Google Earth only on Mac OS and Windows with Visual Studio compiler
......
...@@ -413,8 +413,7 @@ bool SerialLink::hardwareConnect() ...@@ -413,8 +413,7 @@ bool SerialLink::hardwareConnect()
} }
QObject::connect(m_port,SIGNAL(aboutToClose()),this,SIGNAL(disconnected())); QObject::connect(m_port,SIGNAL(aboutToClose()),this,SIGNAL(disconnected()));
QObject::connect(m_port, SIGNAL(error(SerialLinkPortError_t)), QObject::connect(m_port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(linkError(QSerialPort::SerialPortError)));
this, SLOT(linkError(SerialLinkPortError_t)));
// port->setCommTimeouts(QSerialPort::CtScheme_NonBlockingRead); // port->setCommTimeouts(QSerialPort::CtScheme_NonBlockingRead);
...@@ -444,7 +443,7 @@ bool SerialLink::hardwareConnect() ...@@ -444,7 +443,7 @@ bool SerialLink::hardwareConnect()
return true; // successful connection return true; // successful connection
} }
void SerialLink::linkError(SerialLinkPortError_t error) void SerialLink::linkError(QSerialPort::SerialPortError error)
{ {
qDebug() << error; qDebug() << error;
} }
......
...@@ -40,9 +40,6 @@ This file is part of the QGROUNDCONTROL project ...@@ -40,9 +40,6 @@ This file is part of the QGROUNDCONTROL project
#include <configuration.h> #include <configuration.h>
#include "SerialLinkInterface.h" #include "SerialLinkInterface.h"
// convenience type for passing errors
typedef QSerialPort::SerialPortError SerialLinkPortError_t;
/** /**
* @brief The SerialLink class provides cross-platform access to serial links. * @brief The SerialLink class provides cross-platform access to serial links.
* It takes care of the link management and provides a common API to higher * It takes care of the link management and provides a common API to higher
...@@ -137,7 +134,7 @@ public slots: ...@@ -137,7 +134,7 @@ public slots:
bool connect(); bool connect();
bool disconnect(); bool disconnect();
void linkError(SerialLinkPortError_t error); void linkError(QSerialPort::SerialPortError error);
protected: protected:
quint64 m_bytesRead; quint64 m_bytesRead;
......
...@@ -21,13 +21,6 @@ ...@@ -21,13 +21,6 @@
======================================================================*/ ======================================================================*/
/**
* @file
* @brief Definition of TCP connection (server) for unmanned vehicles
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#include <QTimer> #include <QTimer>
#include <QList> #include <QList>
#include <QDebug> #include <QDebug>
...@@ -37,26 +30,29 @@ ...@@ -37,26 +30,29 @@
#include "LinkManager.h" #include "LinkManager.h"
#include "QGC.h" #include "QGC.h"
#include <QHostInfo> #include <QHostInfo>
#include <QSignalSpy>
TCPLink::TCPLink(QHostAddress hostAddress, quint16 socketPort) : /// @file
host(hostAddress), /// @brief TCP link type for SITL support
port(socketPort), ///
socket(NULL), /// @author Don Gagne <don@thegagnes.com>
socketIsConnected(false)
TCPLink::TCPLink(QHostAddress hostAddress, quint16 socketPort) :
_hostAddress(hostAddress),
_port(socketPort),
_socket(NULL),
_socketIsConnected(false)
{ {
// Set unique ID and add link to the list of links _linkId = getNextLinkId();
this->id = getNextLinkId(); _resetName();
this->name = tr("TCP Link (port:%1)").arg(this->port);
emit nameChanged(this->name);
qDebug() << "TCP Created " << this->name; qDebug() << "TCP Created " << _name;
} }
TCPLink::~TCPLink() TCPLink::~TCPLink()
{ {
disconnect(); disconnect();
this->deleteLater(); deleteLater();
} }
void TCPLink::run() void TCPLink::run()
...@@ -64,45 +60,47 @@ void TCPLink::run() ...@@ -64,45 +60,47 @@ void TCPLink::run()
exec(); exec();
} }
void TCPLink::setAddress(const QString &text) void TCPLink::setHostAddress(QHostAddress hostAddress)
{ {
setAddress(QHostAddress(text)); bool reconnect = false;
}
if (this->isConnected()) {
void TCPLink::setAddress(QHostAddress host)
{
bool reconnect(false);
if (this->isConnected())
{
disconnect(); disconnect();
reconnect = true; reconnect = true;
} }
this->host = host;
if (reconnect) _hostAddress = hostAddress;
{ _resetName();
if (reconnect) {
connect(); connect();
} }
} }
void TCPLink::setHostAddress(const QString& hostAddress)
{
setHostAddress(QHostAddress(hostAddress));
}
void TCPLink::setPort(int port) void TCPLink::setPort(int port)
{ {
bool reconnect(false); bool reconnect = false;
if(this->isConnected())
{ if (this->isConnected()) {
disconnect(); disconnect();
reconnect = true; reconnect = true;
} }
this->port = port;
this->name = tr("TCP Link (port:%1)").arg(this->port); _port = port;
emit nameChanged(this->name); _resetName();
if(reconnect)
{ if (reconnect) {
connect(); connect();
} }
} }
#ifdef TCPLINK_READWRITE_DEBUG #ifdef TCPLINK_READWRITE_DEBUG
void TCPLink::writeDebugBytes(const char *data, qint16 size) void TCPLink::_writeDebugBytes(const char *data, qint16 size)
{ {
QString bytes; QString bytes;
QString ascii; QString ascii;
...@@ -119,7 +117,7 @@ void TCPLink::writeDebugBytes(const char *data, qint16 size) ...@@ -119,7 +117,7 @@ void TCPLink::writeDebugBytes(const char *data, qint16 size)
ascii.append(219); ascii.append(219);
} }
} }
qDebug() << "Sent" << size << "bytes to" << host.toString() << ":" << port << "data:"; qDebug() << "Sent" << size << "bytes to" << _hostAddress.toString() << ":" << _port << "data:";
qDebug() << bytes; qDebug() << bytes;
qDebug() << "ASCII:" << ascii; qDebug() << "ASCII:" << ascii;
} }
...@@ -128,9 +126,9 @@ void TCPLink::writeDebugBytes(const char *data, qint16 size) ...@@ -128,9 +126,9 @@ void TCPLink::writeDebugBytes(const char *data, qint16 size)
void TCPLink::writeBytes(const char* data, qint64 size) void TCPLink::writeBytes(const char* data, qint64 size)
{ {
#ifdef TCPLINK_READWRITE_DEBUG #ifdef TCPLINK_READWRITE_DEBUG
writeDebugBytes(data, size); _writeDebugBytes(data, size);
#endif #endif
socket->write(data, size); _socket->write(data, size);
// Log the amount and time written out for future data rate calculations. // Log the amount and time written out for future data rate calculations.
QMutexLocker dataRateLocker(&dataRateMutex); QMutexLocker dataRateLocker(&dataRateMutex);
...@@ -145,14 +143,14 @@ void TCPLink::writeBytes(const char* data, qint64 size) ...@@ -145,14 +143,14 @@ void TCPLink::writeBytes(const char* data, qint64 size)
**/ **/
void TCPLink::readBytes() void TCPLink::readBytes()
{ {
qint64 byteCount = socket->bytesAvailable(); qint64 byteCount = _socket->bytesAvailable();
if (byteCount) if (byteCount)
{ {
QByteArray buffer; QByteArray buffer;
buffer.resize(byteCount); buffer.resize(byteCount);
socket->read(buffer.data(), buffer.size()); _socket->read(buffer.data(), buffer.size());
emit bytesReceived(this, buffer); emit bytesReceived(this, buffer);
...@@ -166,7 +164,6 @@ void TCPLink::readBytes() ...@@ -166,7 +164,6 @@ void TCPLink::readBytes()
} }
} }
/** /**
* @brief Get the number of bytes to read. * @brief Get the number of bytes to read.
* *
...@@ -174,7 +171,7 @@ void TCPLink::readBytes() ...@@ -174,7 +171,7 @@ void TCPLink::readBytes()
**/ **/
qint64 TCPLink::bytesAvailable() qint64 TCPLink::bytesAvailable()
{ {
return socket->bytesAvailable(); return _socket->bytesAvailable();
} }
/** /**
...@@ -184,15 +181,18 @@ qint64 TCPLink::bytesAvailable() ...@@ -184,15 +181,18 @@ qint64 TCPLink::bytesAvailable()
**/ **/
bool TCPLink::disconnect() bool TCPLink::disconnect()
{ {
this->quit(); quit();
this->wait(); wait();
if (socket) if (_socket)
{ {
socket->disconnect(); _socket->disconnectFromHost();
socketIsConnected = false; _socketIsConnected = false;
delete socket; delete _socket;
socket = NULL; _socket = NULL;
emit disconnected();
emit connected(false);
} }
return true; return true;
...@@ -205,42 +205,54 @@ bool TCPLink::disconnect() ...@@ -205,42 +205,54 @@ bool TCPLink::disconnect()
**/ **/
bool TCPLink::connect() bool TCPLink::connect()
{ {
if(this->isRunning()) if (isRunning())
{ {
this->quit(); quit();
this->wait(); wait();
} }
bool connected = this->hardwareConnect(); bool connected = _hardwareConnect();
start(HighPriority); if (connected) {
start(HighPriority);
}
return connected; return connected;
} }
bool TCPLink::hardwareConnect(void) bool TCPLink::_hardwareConnect(void)
{ {
socket = new QTcpSocket(); Q_ASSERT(_socket == NULL);
_socket = new QTcpSocket();
QSignalSpy errorSpy(_socket, SIGNAL(error(QAbstractSocket::SocketError)));
socket->connectToHost(host, port); _socket->connectToHost(_hostAddress, _port);
QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(readBytes())); QObject::connect(_socket, SIGNAL(readyRead()), this, SLOT(readBytes()));
QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); QObject::connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(_socketError(QAbstractSocket::SocketError)));
// Give the socket a second to connect to the other side otherwise error out // Give the socket a second to connect to the other side otherwise error out
if (!socket->waitForConnected(1000)) if (!_socket->waitForConnected(1000))
{ {
emit communicationError(getName(), "connection failed"); // Whether a failed connection emits an error signal or not is platform specific.
// So in cases where it is not emitted, we emit one ourselves.
if (errorSpy.count() == 0) {
emit communicationError(getName(), "Connection failed");
}
delete _socket;
_socket = NULL;
return false; return false;
} }
socketIsConnected = true; _socketIsConnected = true;
emit connected(true); emit connected(true);
emit connected();
return true; return true;
} }
void TCPLink::socketError(QAbstractSocket::SocketError socketError) void TCPLink::_socketError(QAbstractSocket::SocketError socketError)
{ {
Q_UNUSED(socketError); Q_UNUSED(socketError);
emit communicationError(getName(), "Error on socket: " + socket->errorString()); emit communicationError(getName(), "Error on socket: " + _socket->errorString());
} }
/** /**
...@@ -250,26 +262,19 @@ void TCPLink::socketError(QAbstractSocket::SocketError socketError) ...@@ -250,26 +262,19 @@ void TCPLink::socketError(QAbstractSocket::SocketError socketError)
**/ **/
bool TCPLink::isConnected() const bool TCPLink::isConnected() const
{ {
return socketIsConnected; return _socketIsConnected;
} }
int TCPLink::getId() const int TCPLink::getId() const
{ {
return id; return _linkId;
} }
QString TCPLink::getName() const QString TCPLink::getName() const
{ {
return name; return _name;
}
void TCPLink::setName(QString name)
{
this->name = name;
emit nameChanged(this->name);
} }
qint64 TCPLink::getConnectionSpeed() const qint64 TCPLink::getConnectionSpeed() const
{ {
return 54000000; // 54 Mbit return 54000000; // 54 Mbit
...@@ -284,3 +289,9 @@ qint64 TCPLink::getCurrentOutDataRate() const ...@@ -284,3 +289,9 @@ qint64 TCPLink::getCurrentOutDataRate() const
{ {
return 0; return 0;
} }
void TCPLink::_resetName(void)
{
_name = QString("TCP Link (host:%1 port:%2)").arg(_hostAddress.toString()).arg(_port);
emit nameChanged(_name);
}
...@@ -21,12 +21,10 @@ ...@@ -21,12 +21,10 @@
======================================================================*/ ======================================================================*/
/** /// @file
* @file /// @brief TCP link type for SITL support
* @brief TCP connection (server) for unmanned vehicles ///
* @author Lorenz Meier <mavteam@student.ethz.ch> /// @author Don Gagne <don@thegagnes.com>
*
*/
#ifndef TCPLINK_H #ifndef TCPLINK_H
#define TCPLINK_H #define TCPLINK_H
...@@ -50,66 +48,65 @@ public: ...@@ -50,66 +48,65 @@ public:
TCPLink(QHostAddress hostAddress = QHostAddress::LocalHost, quint16 socketPort = 5760); TCPLink(QHostAddress hostAddress = QHostAddress::LocalHost, quint16 socketPort = 5760);
~TCPLink(); ~TCPLink();
void requestReset() { } void setHostAddress(QHostAddress hostAddress);
bool isConnected() const; QHostAddress getHostAddress(void) const { return _hostAddress; }
qint64 bytesAvailable(); quint16 getPort(void) const { return _port; }
int getPort() const { QTcpSocket* getSocket(void) { return _socket; }
return port;
}
QHostAddress getHostAddress() const {
return host;
}
QString getName() const; // LinkInterface methods
int getBaudRate() const; virtual int getId(void) const;
int getBaudRateType() const; virtual QString getName(void) const;
int getFlowType() const; virtual bool isConnected(void) const;
int getParityType() const; virtual bool connect(void);
int getDataBitsType() const; virtual bool disconnect(void);
int getStopBitsType() const; virtual qint64 bytesAvailable(void);
virtual void requestReset(void) {};
// Extensive statistics for scientific purposes // Extensive statistics for scientific purposes
qint64 getConnectionSpeed() const; qint64 getConnectionSpeed() const;
qint64 getCurrentInDataRate() const; qint64 getCurrentInDataRate() const;
qint64 getCurrentOutDataRate() const; qint64 getCurrentOutDataRate() const;
void run();
int getId() const;
public slots: public slots:
void setAddress(QHostAddress host); void setHostAddress(const QString& hostAddress);
void setPort(int port); void setPort(int port);
void readBytes();
void writeBytes(const char* data, qint64 length);
bool connect();
bool disconnect();
void socketError(QAbstractSocket::SocketError socketError);
void setAddress(const QString &text);
// From LinkInterface
virtual void writeBytes(const char* data, qint64 length);
protected slots:
void _socketError(QAbstractSocket::SocketError socketError);
// From LinkInterface
virtual void readBytes(void);
protected: protected:
QString name; // From LinkInterface->QThread
QHostAddress host; virtual void run(void);
quint16 port;
int id;
QTcpSocket* socket;
bool socketIsConnected;
QMutex dataMutex;
void setName(QString name);
private: private:
bool hardwareConnect(void); void _resetName(void);
bool _hardwareConnect(void);
#ifdef TCPLINK_READWRITE_DEBUG #ifdef TCPLINK_READWRITE_DEBUG
void writeDebugBytes(const char *data, qint16 size); void _writeDebugBytes(const char *data, qint16 size);
#endif #endif
QString _name;
QHostAddress _hostAddress;
quint16 _port;
int _linkId;
QTcpSocket* _socket;
bool _socketIsConnected;
signals: quint64 _bitsSentTotal;
//Signals are defined by LinkInterface quint64 _bitsSentCurrent;
quint64 _bitsSentMax;
quint64 _bitsReceivedTotal;
quint64 _bitsReceivedCurrent;
quint64 _bitsReceivedMax;
quint64 _connectionStartTime;
QMutex _statisticsMutex;
}; };
#endif // TCPLINK_H #endif // TCPLINK_H
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include "MultiSignalSpy.h"
#include <QEventLoop>
#include <QCoreApplication>
#include <QDebug>
/// @file
/// @brief This class allows you to keep track of signal counts on a set of signals associated with an object.
/// Mainly used for writing object unit tests.
///
/// @author Don Gagne <don@thegagnes.com>
MultiSignalSpy::MultiSignalSpy(QObject* parent) :
QObject(parent),
_signalEmitter(NULL),
_rgSignals(NULL),
_cSignals(0)
{
}
MultiSignalSpy::~MultiSignalSpy()
{
Q_ASSERT(_rgSignals);
for (size_t i=0; i<_cSignals; i++) {
delete _rgSpys[i];
}
}
/// Initializes the class. Must be called once before use.
/// @return true if success, false for failure
bool MultiSignalSpy::init(
QObject* signalEmitter, ///< [in] object which the signals are emitted from
const char** rgSignals, ///< [in] array of signals to spy on
size_t cSignals) ///< [in] numbers of signals in rgSignals
{
if (!signalEmitter || !rgSignals || cSignals == 0) {
qDebug() << "Invalid arguments";
return false;
}
_signalEmitter = signalEmitter;
_rgSignals = rgSignals;
_cSignals = cSignals;
// Allocate and connect QSignalSpy's
_rgSpys = new QSignalSpy*[_cSignals];
Q_ASSERT(_rgSpys != NULL);
for (size_t i=0; i<_cSignals; i++) {
_rgSpys[i] = new QSignalSpy(_signalEmitter, _rgSignals[i]);
if (_rgSpys[i] == NULL) {
qDebug() << "Unabled to allocated QSignalSpy";
return false;
}
if (!_rgSpys[i]->isValid()) {
qDebug() << "Invalid signal";
return false;
}
}
return true;
}
/// @param mask bit mask specifying which signals to check. The lowest order bit represents
/// index 0 into the rgSignals array and so on up the bit mask.
/// @return true if signal count = 1 for the specified signals
bool MultiSignalSpy::checkSignalByMask(quint16 mask)
{
for (size_t i=0; i<_cSignals; i++) {
if ((1 << i) & mask) {
QSignalSpy* spy = _rgSpys[i];
Q_ASSERT(spy != NULL);
if (spy->count() != 1) {
return false;
}
}
}
return true;
}
/// @return true if signal count = 1 for specified signals and signal count of 0
/// for all other signals
bool MultiSignalSpy::checkOnlySignalByMask(quint16 mask)
{
for (size_t i=0; i<_cSignals; i++) {
QSignalSpy* spy = _rgSpys[i];
Q_ASSERT(spy != NULL);
if ((1 << i) & mask) {
if (spy->count() != 1) {
return false;
}
} else {
if (spy->count() != 0) {
return false;
}
}
}
return true;
}
/// @return true if signal count = 0 for specified signals
bool MultiSignalSpy::checkNoSignalByMask(quint16 mask)
{
for (size_t i=0; i<_cSignals; i++) {
if ((1 << i) & mask) {
QSignalSpy* spy = _rgSpys[i];
Q_ASSERT(spy != NULL);
if (spy->count() != 0) {
return false;
}
}
}
return true;
}
/// @return true if signal count = 0 on all signals
bool MultiSignalSpy::checkNoSignals(void)
{
return checkNoSignalByMask(~0);
}
/// @return QSignalSpy for the specified signal
QSignalSpy* MultiSignalSpy::getSpyByIndex(quint16 index)
{
Q_ASSERT(index < _cSignals);
Q_ASSERT(_rgSpys[index] != NULL);
return _rgSpys[index];
}
/// Sets the signal count to 0 for the specified signal
void MultiSignalSpy::clearSignalByIndex(quint16 index)
{
Q_ASSERT(index < _cSignals);
Q_ASSERT(_rgSpys[index] != NULL);
_rgSpys[index]->clear();
}
/// Sets the signal count to 0 for all specified signals
void MultiSignalSpy::clearSignalsByMask(quint16 mask)
{
for (size_t i=0; i<_cSignals; i++) {
if ((1 << i) & mask) {
QSignalSpy* spy = _rgSpys[i];
Q_ASSERT(spy != NULL);
spy->clear();
}
}
}
/// Sets the signal count to 0 for all signals
void MultiSignalSpy::clearAllSignals(void)
{
for (quint16 i=0;i<_cSignals; i++) {
clearSignalByIndex(i);
}
}
void MultiSignalSpy::timerEvent(QTimerEvent * event)
{
Q_UNUSED(event);
_timeout = true;
}
/// Waits the specified signal
/// @return false for timeout
bool MultiSignalSpy::waitForSignalByIndex(
quint16 index, ///< [in] index of signal to wait on
int msec) ///< [in] numbers of milleconds to wait before timeout, -1 wait forever
{
// Check input parameters
if (msec < -1 || msec == 0)
return false;
// activate the timeout
_timeout = false;
int timerId;
if (msec != -1) {
timerId = startTimer(msec);
Q_ASSERT(timerId);
} else {
timerId = 0;
}
// Begin waiting
QSignalSpy* spy = _rgSpys[index];
Q_ASSERT(spy);
while (spy->count() == 0 && !_timeout) {
QCoreApplication::processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents);
}
// Clean up and return status
if (timerId) {
killTimer(timerId);
}
return spy->count() != 0;
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef MULTISIGNALSPY_H
#define MULTISIGNALSPY_H
#include <QObject>
#include <QSignalSpy>
/// @file
/// @brief This class allows you to keep track of signal counts on a set of signals associated with an object.
/// Mainly used for writing object unit tests.
///
/// @author Don Gagne <don@thegagnes.com>
class MultiSignalSpy : public QObject
{
Q_OBJECT
public:
MultiSignalSpy(QObject* parent = NULL);
~MultiSignalSpy();
bool init(QObject* signalEmitter, const char** rgSignals, size_t cSignals);
bool checkSignalByMask(quint16 mask);
bool checkOnlySignalByMask(quint16 mask);
bool checkNoSignalByMask(quint16 mask);
bool checkNoSignals(void);
void clearSignalByIndex(quint16 index);
void clearSignalsByMask(quint16 mask);
void clearAllSignals(void);
bool waitForSignalByIndex(quint16 index, int msec);
QSignalSpy* getSpyByIndex(quint16 index);
private:
// QObject overrides
void timerEvent(QTimerEvent * event);
QObject* _signalEmitter;
const char** _rgSignals;
QSignalSpy** _rgSpys;
size_t _cSignals;
bool _timeout;
};
#endif
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include "TCPLinkTest.h"
#include <QTcpServer>
/// @file
/// @brief TCPLink class unit test
///
/// @author Don Gagne <don@thegagnes.com>
TCPLinkUnitTest::TCPLinkUnitTest(void) :
_link(NULL),
_hostAddress(QHostAddress::LocalHost),
_port(5760),
_multiSpy(NULL)
{
}
// Called before every test
void TCPLinkUnitTest::init(void)
{
Q_ASSERT(_link == NULL);
Q_ASSERT(_multiSpy == NULL);
_link = new TCPLink(_hostAddress, _port);
Q_ASSERT(_link != NULL);
_rgSignals[bytesReceivedSignalIndex] = SIGNAL(bytesReceived(LinkInterface*, QByteArray));
_rgSignals[connectedSignalIndex] = SIGNAL(connected(void));
_rgSignals[disconnectedSignalIndex] = SIGNAL(disconnected(void));
_rgSignals[connected2SignalIndex] = SIGNAL(connected(bool));
_rgSignals[nameChangedSignalIndex] = SIGNAL(nameChanged(QString));
_rgSignals[communicationErrorSignalIndex] = SIGNAL(communicationError(const QString&, const QString&));
_rgSignals[communicationUpdateSignalIndex] = SIGNAL(communicationUpdate(const QString&, const QString&));
_rgSignals[deleteLinkSignalIndex] = SIGNAL(deleteLink(LinkInterface* const));
_multiSpy = new MultiSignalSpy();
QCOMPARE(_multiSpy->init(_link, _rgSignals, _cSignals), true);
}
// Called after every test
void TCPLinkUnitTest::cleanup(void)
{
Q_ASSERT(_multiSpy);
Q_ASSERT(_link);
delete _multiSpy;
delete _link;
_multiSpy = NULL;
_link = NULL;
}
void TCPLinkUnitTest::_properties_test(void)
{
Q_ASSERT(_link);
Q_ASSERT(_multiSpy);
Q_ASSERT(_multiSpy->checkNoSignals() == true);
// Make sure we get the right values back
QHostAddress hostAddressOut;
quint16 portOut;
hostAddressOut = _link->getHostAddress();
QCOMPARE(_hostAddress, hostAddressOut);
portOut = _link->getPort();
QCOMPARE(_port, portOut);
}
void TCPLinkUnitTest::_nameChangedSignal_test(void)
{
Q_ASSERT(_link);
Q_ASSERT(_multiSpy);
Q_ASSERT(_multiSpy->checkNoSignals() == true);
_link->setHostAddress(QHostAddress("127.1.1.1"));
QCOMPARE(_multiSpy->checkOnlySignalByMask(nameChangedSignalMask), true);
_multiSpy->clearSignalByIndex(nameChangedSignalIndex);
_link->setPort(42);
QCOMPARE(_multiSpy->checkOnlySignalByMask(nameChangedSignalMask), true);
_multiSpy->clearSignalByIndex(nameChangedSignalIndex);
}
void TCPLinkUnitTest::_connectFail_test(void)
{
Q_ASSERT(_link);
Q_ASSERT(_multiSpy);
Q_ASSERT(_multiSpy->checkNoSignals() == true);
QCOMPARE(_link->connect(), false);
// Make sure we get a linkError signal with the right link name
QCOMPARE(_multiSpy->waitForSignalByIndex(communicationErrorSignalIndex, 1000), true);
QCOMPARE(_multiSpy->checkOnlySignalByMask(communicationErrorSignalMask), true);
QList<QVariant> arguments = _multiSpy->getSpyByIndex(communicationErrorSignalIndex)->takeFirst();
QCOMPARE(arguments.at(0).toString(), _link->getName());
_multiSpy->clearSignalByIndex(communicationErrorSignalIndex);
// Try to connect again to make sure everything was cleaned up correctly from previous failed connection
QCOMPARE(_link->connect(), false);
// Make sure we get a linkError signal with the right link name
QCOMPARE(_multiSpy->waitForSignalByIndex(communicationErrorSignalIndex, 1000), true);
QCOMPARE(_multiSpy->checkOnlySignalByMask(communicationErrorSignalMask), true);
arguments = _multiSpy->getSpyByIndex(communicationErrorSignalIndex)->takeFirst();
QCOMPARE(arguments.at(0).toString(), _link->getName());
_multiSpy->clearSignalByIndex(communicationErrorSignalIndex);
}
void TCPLinkUnitTest::_connectSucceed_test(void)
{
Q_ASSERT(_link);
Q_ASSERT(_multiSpy);
Q_ASSERT(_multiSpy->checkNoSignals() == true);
// Start the server side
QTcpServer* server = new QTcpServer(this);
QCOMPARE(server->listen(_hostAddress, _port), true);
// Connect to the server
QCOMPARE(_link->connect(), true);
// Wait for the connection to come through on server side
QCOMPARE(server->waitForNewConnection(1000), true);
QTcpSocket* serverSocket = server->nextPendingConnection();
Q_ASSERT(serverSocket);
// Make sure we get the two different connected signals
QCOMPARE(_multiSpy->waitForSignalByIndex(connectedSignalIndex, 1000), true);
QCOMPARE(_multiSpy->checkOnlySignalByMask(connectedSignalMask | connected2SignalMask), true);
QList<QVariant> arguments = _multiSpy->getSpyByIndex(connected2SignalIndex)->takeFirst();
QCOMPARE(arguments.at(0).toBool(), true);
_multiSpy->clearSignalsByMask(connectedSignalMask);
// Test server->link data path
QByteArray bytesOut("test");
// Write data from server to link
serverSocket->write(bytesOut);
// Make sure we get the bytesReceived signal, with the correct data
QCOMPARE(_multiSpy->waitForSignalByIndex(bytesReceivedSignalIndex, 1000), true);
QCOMPARE(_multiSpy->checkOnlySignalByMask(bytesReceivedSignalMask), true);
arguments = _multiSpy->getSpyByIndex(bytesReceivedSignalIndex)->takeFirst();
QCOMPARE(arguments.at(1), QVariant(bytesOut));
_multiSpy->clearSignalByIndex(bytesReceivedSignalIndex);
// Test link->server data path
// Write data from link to server
_link->writeBytes(bytesOut.data(), bytesOut.size());
QCOMPARE(_link->getSocket()->waitForBytesWritten(1000), true);
// Make sure we get readyRead on server
QCOMPARE(serverSocket->waitForReadyRead(1000), true);
// Read the data and make sure it matches
QByteArray bytesIn = serverSocket->read(bytesOut.size() + 100);
QCOMPARE(bytesIn, bytesOut);
// Disconnect the link
_link->disconnect();
// Make sure we get the disconnected signals on link side
QCOMPARE(_multiSpy->waitForSignalByIndex(disconnectedSignalIndex, 1000), true);
QCOMPARE(_multiSpy->checkOnlySignalByMask(disconnectedSignalMask | connected2SignalMask), true);
arguments = _multiSpy->getSpyByIndex(connected2SignalIndex)->takeFirst();
QCOMPARE(arguments.at(0).toBool(), false);
_multiSpy->clearSignalsByMask(disconnectedSignalMask);
// Make sure we get disconnected signals from the server side
QCOMPARE(serverSocket->waitForDisconnected(1000), true );
// Try to connect again to make sure everything was cleaned up correctly from previous connection
// Connect to the server
QCOMPARE(_link->connect(), true);
// Wait for the connection to come through on server side
QCOMPARE(server->waitForNewConnection(1000), true);
serverSocket = server->nextPendingConnection();
Q_ASSERT(serverSocket);
// Make sure we get the two different connected signals
QCOMPARE(_multiSpy->waitForSignalByIndex(connectedSignalIndex, 1000), true);
QCOMPARE(_multiSpy->checkOnlySignalByMask(connectedSignalMask | connected2SignalMask), true);
arguments = _multiSpy->getSpyByIndex(connected2SignalIndex)->takeFirst();
QCOMPARE(arguments.at(0).toBool(), true);
_multiSpy->clearSignalsByMask(connectedSignalMask);
delete server;
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef TCPLINKTEST_H
#define TCPLINKTEST_H
#include <QObject>
#include <QtTest/QtTest>
#include <QApplication>
#include "AutoTest.h"
#include "TCPLink.h"
#include "MultiSignalSpy.h"
/// @file
/// @brief TCPLink class unit test
///
/// @author Don Gagne <don@thegagnes.com>
class TCPLinkUnitTest : public QObject
{
Q_OBJECT
public:
TCPLinkUnitTest(void);
private slots:
void init(void);
void cleanup(void);
void _properties_test(void);
void _nameChangedSignal_test(void);
void _connectFail_test(void);
void _connectSucceed_test(void);
private:
enum {
bytesReceivedSignalIndex = 0,
connectedSignalIndex,
disconnectedSignalIndex,
connected2SignalIndex,
nameChangedSignalIndex,
communicationErrorSignalIndex,
communicationUpdateSignalIndex,
deleteLinkSignalIndex,
maxSignalIndex
};
enum {
bytesReceivedSignalMask = 1 << bytesReceivedSignalIndex,
connectedSignalMask = 1 << connectedSignalIndex,
disconnectedSignalMask = 1 << disconnectedSignalIndex,
connected2SignalMask = 1 << connected2SignalIndex,
nameChangedSignalMask = 1 << nameChangedSignalIndex,
communicationErrorSignalMask = 1 << communicationErrorSignalIndex,
communicationUpdateSignalMask = 1 << communicationUpdateSignalIndex,
deleteLinkSignalMask = 1 << deleteLinkSignalIndex,
};
TCPLink* _link;
QHostAddress _hostAddress;
quint16 _port;
MultiSignalSpy* _multiSpy;
static const size_t _cSignals = maxSignalIndex;
const char* _rgSignals[_cSignals];
};
DECLARE_TEST(TCPLinkUnitTest)
#endif
...@@ -14,7 +14,7 @@ QGCTCPLinkConfiguration::QGCTCPLinkConfiguration(TCPLink* link, QWidget *parent) ...@@ -14,7 +14,7 @@ QGCTCPLinkConfiguration::QGCTCPLinkConfiguration(TCPLink* link, QWidget *parent)
QString addr = link->getHostAddress().toString(); QString addr = link->getHostAddress().toString();
ui->hostAddressLineEdit->setText(addr); ui->hostAddressLineEdit->setText(addr);
connect(ui->portSpinBox, SIGNAL(valueChanged(int)), link, SLOT(setPort(int))); connect(ui->portSpinBox, SIGNAL(valueChanged(int)), link, SLOT(setPort(int)));
connect(ui->hostAddressLineEdit, SIGNAL(textChanged (const QString &)), link, SLOT(setAddress(const QString &))); connect(ui->hostAddressLineEdit, SIGNAL(textChanged (const QString &)), link, SLOT(setHostAddress(const QString &)));
} }
QGCTCPLinkConfiguration::~QGCTCPLinkConfiguration() QGCTCPLinkConfiguration::~QGCTCPLinkConfiguration()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment