diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 84c0d30629c91eec5864975c649252fad891a5df..eb2af7fefd6d7939dd61bb18eee5264389251bb0 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -751,13 +751,17 @@ CONFIG(debug, debug|release) { src/qgcunittest/UASUnitTest.h \ src/qgcunittest/MockUASManager.h \ src/qgcunittest/MockUAS.h \ - src/qgcunittest/MockQGCUASParamManager.h + src/qgcunittest/MockQGCUASParamManager.h \ + src/qgcunittest/MultiSignalSpy.h \ + src/qgcunittest/TCPLinkTest.h SOURCES += \ src/qgcunittest/UASUnitTest.cc \ src/qgcunittest/MockUASManager.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 diff --git a/src/comm/SerialLink.cc b/src/comm/SerialLink.cc index 8e1bfed1974d4eeebb46be66d7b813b81b1dac17..e8bf610881b2553474c35c5b1678283b0329471e 100644 --- a/src/comm/SerialLink.cc +++ b/src/comm/SerialLink.cc @@ -413,8 +413,7 @@ bool SerialLink::hardwareConnect() } QObject::connect(m_port,SIGNAL(aboutToClose()),this,SIGNAL(disconnected())); - QObject::connect(m_port, SIGNAL(error(SerialLinkPortError_t)), - this, SLOT(linkError(SerialLinkPortError_t))); + QObject::connect(m_port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(linkError(QSerialPort::SerialPortError))); // port->setCommTimeouts(QSerialPort::CtScheme_NonBlockingRead); @@ -444,7 +443,7 @@ bool SerialLink::hardwareConnect() return true; // successful connection } -void SerialLink::linkError(SerialLinkPortError_t error) +void SerialLink::linkError(QSerialPort::SerialPortError error) { qDebug() << error; } diff --git a/src/comm/SerialLink.h b/src/comm/SerialLink.h index 2245a0e9ca5dda915c580654a0e40cd5f4009537..df3877907b7030f08fa74d2f3ed0e89cac3984ad 100644 --- a/src/comm/SerialLink.h +++ b/src/comm/SerialLink.h @@ -40,9 +40,6 @@ This file is part of the QGROUNDCONTROL project #include #include "SerialLinkInterface.h" -// convenience type for passing errors -typedef QSerialPort::SerialPortError SerialLinkPortError_t; - /** * @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 @@ -137,7 +134,7 @@ public slots: bool connect(); bool disconnect(); - void linkError(SerialLinkPortError_t error); + void linkError(QSerialPort::SerialPortError error); protected: quint64 m_bytesRead; diff --git a/src/comm/TCPLink.cc b/src/comm/TCPLink.cc index d2044a8a2220bcc5aa83c7d61f2f7571430e1fb0..0188b549bb65377fcc5fcb056eb961c5ac030f5f 100644 --- a/src/comm/TCPLink.cc +++ b/src/comm/TCPLink.cc @@ -21,13 +21,6 @@ ======================================================================*/ -/** - * @file - * @brief Definition of TCP connection (server) for unmanned vehicles - * @author Lorenz Meier - * - */ - #include #include #include @@ -37,26 +30,29 @@ #include "LinkManager.h" #include "QGC.h" #include +#include -TCPLink::TCPLink(QHostAddress hostAddress, quint16 socketPort) : - host(hostAddress), - port(socketPort), - socket(NULL), - socketIsConnected(false) +/// @file +/// @brief TCP link type for SITL support +/// +/// @author Don Gagne +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 - this->id = getNextLinkId(); - this->name = tr("TCP Link (port:%1)").arg(this->port); - emit nameChanged(this->name); + _linkId = getNextLinkId(); + _resetName(); - qDebug() << "TCP Created " << this->name; + qDebug() << "TCP Created " << _name; } TCPLink::~TCPLink() { disconnect(); - this->deleteLater(); + deleteLater(); } void TCPLink::run() @@ -64,45 +60,47 @@ void TCPLink::run() exec(); } -void TCPLink::setAddress(const QString &text) +void TCPLink::setHostAddress(QHostAddress hostAddress) { - setAddress(QHostAddress(text)); -} - -void TCPLink::setAddress(QHostAddress host) -{ - bool reconnect(false); - if (this->isConnected()) - { + bool reconnect = false; + + if (this->isConnected()) { disconnect(); reconnect = true; } - this->host = host; - if (reconnect) - { + + _hostAddress = hostAddress; + _resetName(); + + if (reconnect) { connect(); } } +void TCPLink::setHostAddress(const QString& hostAddress) +{ + setHostAddress(QHostAddress(hostAddress)); +} + void TCPLink::setPort(int port) { - bool reconnect(false); - if(this->isConnected()) - { + bool reconnect = false; + + if (this->isConnected()) { disconnect(); reconnect = true; } - this->port = port; - this->name = tr("TCP Link (port:%1)").arg(this->port); - emit nameChanged(this->name); - if(reconnect) - { + + _port = port; + _resetName(); + + if (reconnect) { connect(); } } #ifdef TCPLINK_READWRITE_DEBUG -void TCPLink::writeDebugBytes(const char *data, qint16 size) +void TCPLink::_writeDebugBytes(const char *data, qint16 size) { QString bytes; QString ascii; @@ -119,7 +117,7 @@ void TCPLink::writeDebugBytes(const char *data, qint16 size) ascii.append(219); } } - qDebug() << "Sent" << size << "bytes to" << host.toString() << ":" << port << "data:"; + qDebug() << "Sent" << size << "bytes to" << _hostAddress.toString() << ":" << _port << "data:"; qDebug() << bytes; qDebug() << "ASCII:" << ascii; } @@ -128,9 +126,9 @@ void TCPLink::writeDebugBytes(const char *data, qint16 size) void TCPLink::writeBytes(const char* data, qint64 size) { #ifdef TCPLINK_READWRITE_DEBUG - writeDebugBytes(data, size); + _writeDebugBytes(data, size); #endif - socket->write(data, size); + _socket->write(data, size); // Log the amount and time written out for future data rate calculations. QMutexLocker dataRateLocker(&dataRateMutex); @@ -145,14 +143,14 @@ void TCPLink::writeBytes(const char* data, qint64 size) **/ void TCPLink::readBytes() { - qint64 byteCount = socket->bytesAvailable(); + qint64 byteCount = _socket->bytesAvailable(); if (byteCount) { QByteArray buffer; buffer.resize(byteCount); - socket->read(buffer.data(), buffer.size()); + _socket->read(buffer.data(), buffer.size()); emit bytesReceived(this, buffer); @@ -166,7 +164,6 @@ void TCPLink::readBytes() } } - /** * @brief Get the number of bytes to read. * @@ -174,7 +171,7 @@ void TCPLink::readBytes() **/ qint64 TCPLink::bytesAvailable() { - return socket->bytesAvailable(); + return _socket->bytesAvailable(); } /** @@ -184,15 +181,18 @@ qint64 TCPLink::bytesAvailable() **/ bool TCPLink::disconnect() { - this->quit(); - this->wait(); + quit(); + wait(); - if (socket) + if (_socket) { - socket->disconnect(); - socketIsConnected = false; - delete socket; - socket = NULL; + _socket->disconnectFromHost(); + _socketIsConnected = false; + delete _socket; + _socket = NULL; + + emit disconnected(); + emit connected(false); } return true; @@ -205,42 +205,54 @@ bool TCPLink::disconnect() **/ bool TCPLink::connect() { - if(this->isRunning()) + if (isRunning()) { - this->quit(); - this->wait(); + quit(); + wait(); } - bool connected = this->hardwareConnect(); - start(HighPriority); + bool connected = _hardwareConnect(); + if (connected) { + start(HighPriority); + } 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(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); + QObject::connect(_socket, SIGNAL(readyRead()), this, SLOT(readBytes())); + 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 - 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; } - socketIsConnected = true; + _socketIsConnected = true; emit connected(true); + emit connected(); return true; } -void TCPLink::socketError(QAbstractSocket::SocketError socketError) +void TCPLink::_socketError(QAbstractSocket::SocketError 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) **/ bool TCPLink::isConnected() const { - return socketIsConnected; + return _socketIsConnected; } int TCPLink::getId() const { - return id; + return _linkId; } QString TCPLink::getName() const { - return name; -} - -void TCPLink::setName(QString name) -{ - this->name = name; - emit nameChanged(this->name); + return _name; } - qint64 TCPLink::getConnectionSpeed() const { return 54000000; // 54 Mbit @@ -284,3 +289,9 @@ qint64 TCPLink::getCurrentOutDataRate() const { return 0; } + +void TCPLink::_resetName(void) +{ + _name = QString("TCP Link (host:%1 port:%2)").arg(_hostAddress.toString()).arg(_port); + emit nameChanged(_name); +} diff --git a/src/comm/TCPLink.h b/src/comm/TCPLink.h index 6834fe3706b66760d7152299ba1143855c932f00..43d160f87797a6f49e2039a188ee626e7b9fedd7 100644 --- a/src/comm/TCPLink.h +++ b/src/comm/TCPLink.h @@ -21,12 +21,10 @@ ======================================================================*/ -/** - * @file - * @brief TCP connection (server) for unmanned vehicles - * @author Lorenz Meier - * - */ +/// @file +/// @brief TCP link type for SITL support +/// +/// @author Don Gagne #ifndef TCPLINK_H #define TCPLINK_H @@ -50,66 +48,65 @@ public: TCPLink(QHostAddress hostAddress = QHostAddress::LocalHost, quint16 socketPort = 5760); ~TCPLink(); - void requestReset() { } + void setHostAddress(QHostAddress hostAddress); - bool isConnected() const; - qint64 bytesAvailable(); - int getPort() const { - return port; - } - QHostAddress getHostAddress() const { - return host; - } + QHostAddress getHostAddress(void) const { return _hostAddress; } + quint16 getPort(void) const { return _port; } + QTcpSocket* getSocket(void) { return _socket; } - QString getName() const; - int getBaudRate() const; - int getBaudRateType() const; - int getFlowType() const; - int getParityType() const; - int getDataBitsType() const; - int getStopBitsType() const; + // LinkInterface methods + virtual int getId(void) const; + virtual QString getName(void) const; + virtual bool isConnected(void) const; + virtual bool connect(void); + virtual bool disconnect(void); + virtual qint64 bytesAvailable(void); + virtual void requestReset(void) {}; // Extensive statistics for scientific purposes qint64 getConnectionSpeed() const; qint64 getCurrentInDataRate() const; qint64 getCurrentOutDataRate() const; - void run(); - - int getId() const; - public slots: - void setAddress(QHostAddress host); - 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); - + void setHostAddress(const QString& hostAddress); + void setPort(int port); + // From LinkInterface + virtual void writeBytes(const char* data, qint64 length); + +protected slots: + void _socketError(QAbstractSocket::SocketError socketError); + + // From LinkInterface + virtual void readBytes(void); + protected: - QString name; - QHostAddress host; - quint16 port; - int id; - QTcpSocket* socket; - bool socketIsConnected; - - QMutex dataMutex; - - void setName(QString name); - + // From LinkInterface->QThread + virtual void run(void); + private: - bool hardwareConnect(void); + void _resetName(void); + bool _hardwareConnect(void); #ifdef TCPLINK_READWRITE_DEBUG - void writeDebugBytes(const char *data, qint16 size); + void _writeDebugBytes(const char *data, qint16 size); #endif + + QString _name; + QHostAddress _hostAddress; + quint16 _port; + int _linkId; + QTcpSocket* _socket; + bool _socketIsConnected; -signals: - //Signals are defined by LinkInterface - + quint64 _bitsSentTotal; + quint64 _bitsSentCurrent; + quint64 _bitsSentMax; + quint64 _bitsReceivedTotal; + quint64 _bitsReceivedCurrent; + quint64 _bitsReceivedMax; + quint64 _connectionStartTime; + QMutex _statisticsMutex; }; #endif // TCPLINK_H diff --git a/src/qgcunittest/MultiSignalSpy.cc b/src/qgcunittest/MultiSignalSpy.cc new file mode 100644 index 0000000000000000000000000000000000000000..1699dad43900109b6d7d7e2a5daa03d4c8f37b94 --- /dev/null +++ b/src/qgcunittest/MultiSignalSpy.cc @@ -0,0 +1,230 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2014 QGROUNDCONTROL PROJECT + + 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 . + + ======================================================================*/ + +#include "MultiSignalSpy.h" +#include +#include +#include + +/// @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 + +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; +} diff --git a/src/qgcunittest/MultiSignalSpy.h b/src/qgcunittest/MultiSignalSpy.h new file mode 100644 index 0000000000000000000000000000000000000000..92fb905c16160f38bbb16a7a4541c92e6b1649ee --- /dev/null +++ b/src/qgcunittest/MultiSignalSpy.h @@ -0,0 +1,70 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2014 QGROUNDCONTROL PROJECT + + 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 . + + ======================================================================*/ + +#ifndef MULTISIGNALSPY_H +#define MULTISIGNALSPY_H + +#include +#include + +/// @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 + +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 diff --git a/src/qgcunittest/TCPLinkTest.cc b/src/qgcunittest/TCPLinkTest.cc new file mode 100644 index 0000000000000000000000000000000000000000..ac57772f275c3c8917e88af838826e1c415eb9a2 --- /dev/null +++ b/src/qgcunittest/TCPLinkTest.cc @@ -0,0 +1,218 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2014 QGROUNDCONTROL PROJECT + + 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 . + + ======================================================================*/ + +#include "TCPLinkTest.h" +#include + +/// @file +/// @brief TCPLink class unit test +/// +/// @author Don Gagne + +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 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 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; +} diff --git a/src/qgcunittest/TCPLinkTest.h b/src/qgcunittest/TCPLinkTest.h new file mode 100644 index 0000000000000000000000000000000000000000..120fa96b14243727a02cb3f2d2138918cd71d343 --- /dev/null +++ b/src/qgcunittest/TCPLinkTest.h @@ -0,0 +1,90 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2014 QGROUNDCONTROL PROJECT + + 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 . + + ======================================================================*/ + +#ifndef TCPLINKTEST_H +#define TCPLINKTEST_H + +#include +#include +#include + +#include "AutoTest.h" +#include "TCPLink.h" +#include "MultiSignalSpy.h" + +/// @file +/// @brief TCPLink class unit test +/// +/// @author Don Gagne + +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 diff --git a/src/ui/QGCTCPLinkConfiguration.cc b/src/ui/QGCTCPLinkConfiguration.cc index 7eb0f661ca83132e8e20d9abf17417560c34efb4..e816ec73552b5d42b38600f2a2443eb4e7f6b2db 100644 --- a/src/ui/QGCTCPLinkConfiguration.cc +++ b/src/ui/QGCTCPLinkConfiguration.cc @@ -14,7 +14,7 @@ QGCTCPLinkConfiguration::QGCTCPLinkConfiguration(TCPLink* link, QWidget *parent) QString addr = link->getHostAddress().toString(); ui->hostAddressLineEdit->setText(addr); 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()