Commit 028c7009 authored by Don Gagne's avatar Don Gagne

New Unit Testing framework

Allows for UI unit tests to be written:
- Ability to respond to expected message boxes
- Ability to fail test if unexpected message boxes occurs
parent 9f6a69b5
......@@ -633,7 +633,8 @@ INCLUDEPATH += \
src/qgcunittest
HEADERS += \
src/qgcunittest/AutoTest.h \
src/qgcunittest/UnitTest.h \
src/qgcunittest/MessageBoxTest.h \
src/qgcunittest/UASUnitTest.h \
src/qgcunittest/MockLink.h \
src/qgcunittest/MockLinkMissionItemHandler.h \
......@@ -651,7 +652,9 @@ HEADERS += \
src/qgcunittest/LinkManagerTest.h
SOURCES += \
src/qgcunittest/UnitTest.cc \
src/qgcunittest/UASUnitTest.cc \
src/qgcunittest/MessageBoxTest.cc \
src/qgcunittest/MockLink.cc \
src/qgcunittest/MockLinkMissionItemHandler.cc \
src/qgcunittest/MockUASManager.cc \
......
......@@ -84,8 +84,9 @@ const char* QGCApplication::_savedFileParameterDirectoryName = "SavedParameters"
**/
QGCApplication::QGCApplication(int &argc, char* argv[]) :
QApplication(argc, argv)
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) :
QApplication(argc, argv),
_runningUnitTests(unitTesting)
{
Q_ASSERT(_app == NULL);
_app = this;
......@@ -120,6 +121,13 @@ QGCApplication::QGCApplication(int &argc, char* argv[]) :
// The setting will delete all settings on this boot
fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
// We don't want unit tests to use the same QSettings space as the normal app. So we tweak the app
// name. Also we want to run unit tests with clean settings every time.
if (_runningUnitTests) {
setApplicationName(applicationName().append("UnitTest"));
fClearSettingsOptions = true;
}
if (fClearSettingsOptions) {
// User requested settings to be cleared on command line
settings.clear();
......
......@@ -54,7 +54,7 @@ class QGCApplication : public QApplication
Q_OBJECT
public:
QGCApplication(int &argc, char* argv[]);
QGCApplication(int &argc, char* argv[], bool unitTesting);
~QGCApplication();
/// @brief Sets the persistent flag to delete all settings the next time QGroundControl is started.
......@@ -94,6 +94,9 @@ public:
/// @brief Destroys all singletons. Used by unit test code to reset global state.
void destroySingletonsForUnitTest(void);
/// @brief Returns truee if unit test are being run
bool runningUnitTests(void) { return _runningUnitTests; }
public:
/// @brief Perform initialize which is common to both normal application running and unit tests.
/// Although public should only be called by main.
......@@ -122,6 +125,8 @@ private:
static const char* _savedFileParameterDirectoryName; ///< Name of parameter subdirectory
QList<QGCSingleton*> _singletons; ///< List of registered global singletons
bool _runningUnitTests; ///< true: running unit tests, false: normal app
};
/// @brief Returns the QGCApplication object singleton.
......
......@@ -27,6 +27,8 @@
#include <QMessageBox>
#include "MainWindow.h"
#include "QGCApplication.h"
#include "UnitTest.h"
/// @file
/// @brief Subclass of QMessageBox which re-implements the static public functions. There are two reasons for this:
......@@ -50,30 +52,81 @@ public:
static StandardButton warning(const QString& title, const QString& text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton, QWidget* parent = NULL)
{ return _messageBox(QMessageBox::Warning, title, text, buttons, defaultButton, parent); }
private slots:
/// @brief The exec slot is private becasue when only want QGCMessageBox users to use the static methods. Otherwise it will break
/// unit testing.
int exec(void) { return QMessageBox::exec(); }
private:
static QWidget* _validateParameters(StandardButtons buttons, StandardButton* defaultButton, QWidget* parent)
{
// This is an obsolete bit which unit tests use for signalling. It should not be used in regular code.
Q_ASSERT(!(buttons & QMessageBox::Escape));
// If there is more than one button displayed, make sure a default button is set. Without this unit test code
// will not be able to respond to unexpected message boxes.
unsigned int bits = static_cast<unsigned int>(buttons);
int buttonCount = 0;
for (size_t i=0; i<sizeof(bits)*8; i++) {
if (bits & (1 << i)) {
buttonCount++;
}
}
Q_ASSERT(buttonCount != 0);
if (buttonCount > 1) {
Q_ASSERT(buttons & *defaultButton);
} else {
// Force default button to be set correctly for single button case to make unit test code simpler
*defaultButton = static_cast<QMessageBox::StandardButton>(static_cast<int>(buttons));
}
return (parent == NULL) ? MainWindow::instance() : parent;
}
#ifdef Q_OS_MAC
static StandardButton _messageBox(Icon icon, const QString& title, const QString& text, StandardButtons buttons, StandardButton defaultButton, QWidget* parent)
{
if (parent == NULL) {
parent = MainWindow::instance();
// You can't use QGCMessageBox if QGCApplication is not created yet.
Q_ASSERT(qgcApp());
parent = _validateParameters(buttons, &defaultButton, parent);
#ifdef QT_DEBUG
if (qgcApp()->runningUnitTests()) {
return UnitTest::_messageBox(icon, title, text, buttons, defaultButton);
} else
#endif // QT_DEBUG
{
QString emptyTitle;
QMessageBox box(icon, emptyTitle, title, buttons, parent);
box.setDefaultButton(defaultButton);
box.setInformativeText(text);
return static_cast<QMessageBox::StandardButton>(box.exec());
}
QString emptyTitle;
QMessageBox box(icon, emptyTitle, title, buttons, parent);
box.setDefaultButton(defaultButton);
box.setInformativeText(text);
return static_cast<QMessageBox::StandardButton>(box.exec());
}
#else
static StandardButton _messageBox(Icon icon, const QString& title, const QString& text, StandardButtons buttons, StandardButton defaultButton, QWidget* parent)
{
if (parent == NULL) {
parent = MainWindow::instance();
// You can't use QGCMessageBox if QGCApplication is not created yet.
Q_ASSERT(qgcApp());
parent = _validateParameters(buttons, &defaultButton, parent);
#ifdef QT_DEBUG
if (qgcApp()->runningUnitTests()) {
return UnitTest::_messageBox(icon, title, text, buttons, defaultButton);
} else
#endif // QT_DEBUG
{
QMessageBox box(icon, title, text, buttons, parent);
box.setDefaultButton(defaultButton);
return static_cast<QMessageBox::StandardButton>(box.exec());
}
QMessageBox box(icon, title, text, buttons, parent);
box.setDefaultButton(defaultButton);
return static_cast<QMessageBox::StandardButton>(box.exec());
}
#endif
#endif // Q_OS_MAC
};
#endif
......@@ -37,7 +37,7 @@ This file is part of the QGROUNDCONTROL project
#include "SerialLink.h"
#include "TCPLink.h"
#ifdef QT_DEBUG
#include "AutoTest.h"
#include "UnitTest.h"
#include "CmdLineOptParser.h"
#ifdef Q_OS_WIN
#include <crtdbg.h>
......@@ -104,11 +104,12 @@ int main(int argc, char *argv[])
qRegisterMetaType<QSerialPort::SerialPortError>();
qRegisterMetaType<QAbstractSocket::SocketError>();
bool runUnitTests = false; // Run unit tests
#ifdef QT_DEBUG
// We parse a small set of command line options here prior to QGCApplication in order to handle the ones
// which need to be handled before a QApplication object is started.
bool runUnitTests = false; // Run unit test
bool quietWindowsAsserts = false; // Don't let asserts pop dialog boxes
CmdLineOpt_t rgCmdLineOptions[] = {
......@@ -126,7 +127,7 @@ int main(int argc, char *argv[])
}
#endif
QGCApplication* app = new QGCApplication(argc, argv);
QGCApplication* app = new QGCApplication(argc, argv, runUnitTests);
Q_CHECK_PTR(app);
app->_initCommon();
......@@ -140,13 +141,10 @@ int main(int argc, char *argv[])
}
// Run the test
int failures = AutoTest::run(argc-1, argv, rgCmdLineOptions[0].optionArg);
if (failures == 0)
{
int failures = UnitTest::run(argc-1, argv, rgCmdLineOptions[0].optionArg);
if (failures == 0) {
qDebug() << "ALL TESTS PASSED";
}
else
{
} else {
qDebug() << failures << " TESTS FAILED!";
}
exitCode = -failures;
......
/**
* @author Rob Caldecott
* @note This was obtained from http://qtcreator.blogspot.com/2010/04/sample-multiple-unit-test-project.html
*
*/
#ifndef AUTOTEST_H
#define AUTOTEST_H
#include <QTest>
#include <QList>
#include <QString>
#include <QSharedPointer>
#include "QGCApplication.h"
namespace AutoTest
{
typedef QList<QObject*> TestList;
inline TestList& testList()
{
static TestList list;
return list;
}
inline bool findObject(QObject* object)
{
TestList& list = testList();
if (list.contains(object))
{
return true;
}
foreach (QObject* test, list)
{
if (test->objectName() == object->objectName())
{
return true;
}
}
return false;
}
inline void addTest(QObject* object)
{
TestList& list = testList();
if (!findObject(object))
{
list.append(object);
}
}
inline int run(int argc, char *argv[], QString& singleTest)
{
int ret = 0;
foreach (QObject* test, testList())
{
if (singleTest.isEmpty() || singleTest == test->objectName()) {
qgcApp()->destroySingletonsForUnitTest();
qgcApp()->createSingletonsForUnitTest();
ret += QTest::qExec(test, argc, argv);
}
}
return ret;
}
}
template <class T>
class Test
{
public:
QSharedPointer<T> child;
Test(const QString& name) : child(new T)
{
child->setObjectName(name);
AutoTest::addTest(child.data());
}
};
#define DECLARE_TEST(className) static Test<className> t(#className);
#endif // AUTOTEST_H
......@@ -29,23 +29,13 @@
///
/// @author Don Gagne <don@thegagnes.com>
UT_REGISTER_TEST(FlightGearUnitTest)
FlightGearUnitTest::FlightGearUnitTest(void)
{
}
// Called before every test
void FlightGearUnitTest::init(void)
{
// Nothing here yet
}
// Called after every test
void FlightGearUnitTest::cleanup(void)
{
// Nothing here yet
}
/// @brief The QGCFlightGearLink::parseUIArguments method is fairly involved so we have a unit
// test for it.
void FlightGearUnitTest::_parseUIArguments_test(void)
......
......@@ -24,11 +24,7 @@
#ifndef TCPLINKTEST_H
#define TCPLINKTEST_H
#include <QObject>
#include <QtTest>
#include <QApplication>
#include "AutoTest.h"
#include "UnitTest.h"
#include "TCPLink.h"
#include "MultiSignalSpy.h"
......@@ -37,7 +33,7 @@
///
/// @author Don Gagne <don@thegagnes.com>
class FlightGearUnitTest : public QObject
class FlightGearUnitTest : public UnitTest
{
Q_OBJECT
......@@ -45,14 +41,12 @@ public:
FlightGearUnitTest(void);
private slots:
void init(void);
void cleanup(void);
UT_DECLARE_DEFAULT_initTestCase
UT_DECLARE_DEFAULT_cleanupTestCase
UT_DECLARE_DEFAULT_init
UT_DECLARE_DEFAULT_cleanup
void _parseUIArguments_test(void);
private:
};
DECLARE_TEST(FlightGearUnitTest)
#endif
......@@ -29,6 +29,8 @@
#include "LinkManagerTest.h"
#include "MockLink.h"
UT_REGISTER_TEST(LinkManagerTest)
LinkManagerTest::LinkManagerTest(void) :
_linkMgr(NULL),
_multiSpy(NULL)
......@@ -38,6 +40,8 @@ LinkManagerTest::LinkManagerTest(void) :
void LinkManagerTest::init(void)
{
UnitTest::init();
Q_ASSERT(_linkMgr == NULL);
Q_ASSERT(_multiSpy == NULL);
......@@ -53,6 +57,8 @@ void LinkManagerTest::init(void)
void LinkManagerTest::cleanup(void)
{
UnitTest::cleanup();
Q_ASSERT(_linkMgr);
Q_ASSERT(_multiSpy);
......
......@@ -24,10 +24,7 @@
#ifndef UASUNITTEST_H
#define UASUNITTEST_H
#include <QObject>
#include <QtTest>
#include "AutoTest.h"
#include "UnitTest.h"
#include "LinkManager.h"
#include "MultiSignalSpy.h"
......@@ -36,7 +33,7 @@
///
/// @author Don Gagne <don@thegagnes.com>
class LinkManagerTest : public QObject
class LinkManagerTest : public UnitTest
{
Q_OBJECT
......@@ -44,6 +41,9 @@ public:
LinkManagerTest(void);
private slots:
UT_DECLARE_DEFAULT_initTestCase
UT_DECLARE_DEFAULT_cleanupTestCase
void init(void);
void cleanup(void);
......@@ -69,9 +69,6 @@ private:
MultiSignalSpy* _multiSpy;
static const size_t _cSignals = maxSignalIndex;
const char* _rgSignals[_cSignals];
};
DECLARE_TEST(LinkManagerTest)
#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/>.
======================================================================*/
/// @file
/// @brief The tests the unit test QGCMessageBox catching mechanism.
///
/// @author Don Gagne <don@thegagnes.com>
#include "MessageBoxTest.h"
#include "QGCMessageBox.h"
UT_REGISTER_TEST(MessageBoxTest)
MessageBoxTest::MessageBoxTest(void) :
_expectMissedMessageBox(false)
{
}
void MessageBoxTest::cleanup(void)
{
if (_expectMissedMessageBox) {
_expectMissedMessageBox = false;
QEXPECT_FAIL("", "Supposed to fail in cleanup with a missed message box", Continue);
}
UnitTest::cleanup();
}
void MessageBoxTest::_messageBoxExpected_test(void)
{
setExpectedMessageBox(QMessageBox::Ok);
QCOMPARE(QGCMessageBox::information(QString(), QString()), QMessageBox::Ok);
checkExpectedMessageBox();
}
void MessageBoxTest::_messageBoxUnexpected_test(void)
{
// This should cause the test to fail in the cleanup method
QGCMessageBox::information(QString(), QString());
_expectMissedMessageBox = true;
}
void MessageBoxTest::_previousMessageBox_test(void)
{
// This is the previous unexpected message box
QGCMessageBox::information(QString(), QString());
// Setup for an expected message box.
QEXPECT_FAIL("", "Expecting failure due to previous message boxes", Continue);
setExpectedMessageBox(QMessageBox::Ok);
}
void MessageBoxTest::_noMessageBox_test(void)
{
setExpectedMessageBox(QMessageBox::Ok);
checkExpectedMessageBox(expectFailNoMessageBox);
}
void MessageBoxTest::_badResponseButton_test(void)
{
setExpectedMessageBox(QMessageBox::Cancel);
// Will return Ok even though Cancel was specified, since that was wrong
QCOMPARE(QGCMessageBox::information(QString(), QString()), QMessageBox::Ok);
checkExpectedMessageBox(expectFailBadResponseButton);
}
/*=====================================================================
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/>.
======================================================================*/
/// @file
/// @brief The tests the unit test QGCMessageBox catching mechanism.
///
/// @author Don Gagne <don@thegagnes.com>
#ifndef MESSAGEBOXTEST_H
#define MESSAGEBOXTEST_H
#include "UnitTest.h"
class MessageBoxTest : public UnitTest
{
Q_OBJECT
public:
MessageBoxTest(void);
private slots:
UT_DECLARE_DEFAULT_initTestCase
UT_DECLARE_DEFAULT_cleanupTestCase
UT_DECLARE_DEFAULT_init
void cleanup(void);
void _messageBoxExpected_test(void);
void _messageBoxUnexpected_test(void);
void _previousMessageBox_test(void);
void _noMessageBox_test(void);
void _badResponseButton_test(void);
private:
bool _expectMissedMessageBox;
};
#endif
......@@ -30,6 +30,8 @@
///
/// @author Don Gagne <don@thegagnes.com>
UT_REGISTER_TEST(PX4RCCalibrationTest)
// This will check for the wizard buttons being enabled of disabled according to the mask you pass in.
// We use a macro instead of a method so that we get better line number reporting on failure.
#define CHK_BUTTONS(mask) \
......@@ -137,6 +139,8 @@ PX4RCCalibrationTest::PX4RCCalibrationTest(void) :
/// @brief Called one time before any test cases are run.
void PX4RCCalibrationTest::initTestCase(void)
{
UnitTest::initTestCase();
// Validate that our function to channel mapping is still correct.
for (int function=0; function<PX4RCCalibration::rcCalFunctionMax; function++) {
int chanIndex = _rgFunctionChannelMap[function];
......@@ -149,6 +153,8 @@ void PX4RCCalibrationTest::initTestCase(void)
void PX4RCCalibrationTest::init(void)
{
UnitTest::init();
_mockUASManager = new MockUASManager();
Q_ASSERT(_mockUASManager);
......@@ -196,6 +202,8 @@ void PX4RCCalibrationTest::init(void)
void PX4RCCalibrationTest::cleanup(void)
{
UnitTest::cleanup();
Q_ASSERT(_calWidget);
delete _calWidget;
......
......@@ -24,7 +24,7 @@
#ifndef PX4RCCALIBRATIONTEST_H
#define PX4RCCALIBRATIONTEST_H
#include "AutoTest.h"
#include "UnitTest.h"
#include "MockUASManager.h"
#include "MockUAS.h"
#include "MultiSignalSpy.h"
......@@ -36,7 +36,7 @@
/// @author Don Gagne <don@thegagnes.com>
/// @brief PX4RCCalibration Widget unit test
class PX4RCCalibrationTest : public QObject
class PX4RCCalibrationTest : public UnitTest
{
Q_OBJECT
......@@ -45,6 +45,7 @@ public:
private slots:
void initTestCase(void);
UT_DECLARE_DEFAULT_cleanupTestCase
void init(void);
void cleanup(void);
......@@ -129,6 +130,4 @@ private:
static const int _rgFunctionChannelMap[PX4RCCalibration::rcCalFunctionMax];
};
DECLARE_TEST(PX4RCCalibrationTest)
#endif
......@@ -30,17 +30,20 @@
///
/// @author Don Gagne <don@thegagnes.com>
UT_REGISTER_TEST(QGCUASFileManagerUnitTest)
QGCUASFileManagerUnitTest::QGCUASFileManagerUnitTest(void) :
_mockFileServer(_systemIdQGC, _systemIdServer),
_fileManager(NULL),
_multiSpy(NULL)
{
}
// Called once before all test cases are run
void QGCUASFileManagerUnitTest::initTestCase(void)
{
UnitTest::initTestCase();
_mockUAS.setMockSystemId(_systemIdServer);
_mockUAS.setMockMavlinkPlugin(&_mockFileServer);
}
......@@ -48,6 +51,8 @@ void QGCUASFileManagerUnitTest::initTestCase(void)
// Called before every test case
void QGCUASFileManagerUnitTest::init(void)
{
UnitTest::init();
Q_ASSERT(_multiSpy == NULL);
_fileManager = new QGCUASFileManager(NULL, &_mockUAS, _systemIdQGC);
......@@ -77,6 +82,8 @@ void QGCUASFileManagerUnitTest::init(void)
// Called after every test case
void QGCUASFileManagerUnitTest::cleanup(void)
{
UnitTest::cleanup();
Q_ASSERT(_multiSpy);
Q_ASSERT(_fileManager);
......
......@@ -27,7 +27,7 @@
#include <QObject>
#include <QtTest/QtTest>
#include "AutoTest.h"
#include "UnitTest.h"
#include "MockUAS.h"
#include "MockMavlinkFileServer.h"
#include "QGCUASFileManager.h"
......@@ -38,7 +38,7 @@
///
/// @author Don Gagne <don@thegagnes.com>
class QGCUASFileManagerUnitTest : public QObject
class QGCUASFileManagerUnitTest : public UnitTest
{
Q_OBJECT
......@@ -48,6 +48,7 @@ public:
private slots:
// Test case initialization
void initTestCase(void);
UT_DECLARE_DEFAULT_cleanupTestCase
void init(void);
void cleanup(void);
......@@ -100,6 +101,4 @@ private:
QStringList _fileListReceived;
};
DECLARE_TEST(QGCUASFileManagerUnitTest)
#endif
......@@ -29,18 +29,24 @@
///
/// @author Don Gagne <don@thegagnes.com>
// This unit test has gotten too flaky to run reliably under TeamCity. Removing for now till there is
// time to debug.
//UT_REGISTER_TEST(TCPLinkUnitTest)
TCPLinkUnitTest::TCPLinkUnitTest(void) :
_link(NULL),
_hostAddress(QHostAddress::LocalHost),
_port(5760),
_multiSpy(NULL)
{
}
// Called before every test
void TCPLinkUnitTest::init(void)
{
UnitTest::init();
Q_ASSERT(_link == NULL);
Q_ASSERT(_multiSpy == NULL);
......@@ -63,6 +69,8 @@ void TCPLinkUnitTest::init(void)
// Called after every test
void TCPLinkUnitTest::cleanup(void)
{
UnitTest::cleanup();
Q_ASSERT(_multiSpy);
Q_ASSERT(_link);
......
......@@ -24,11 +24,7 @@
#ifndef TCPLINKTEST_H
#define TCPLINKTEST_H
#include <QObject>
#include <QtTest/QtTest>
#include <QApplication>
#include "AutoTest.h"
#include "UnitTest.h"
#include "TCPLink.h"
#include "MultiSignalSpy.h"
......@@ -37,7 +33,7 @@
///
/// @author Don Gagne <don@thegagnes.com>
class TCPLinkUnitTest : public QObject
class TCPLinkUnitTest : public UnitTest
{
Q_OBJECT
......@@ -49,6 +45,8 @@ signals:
void waitForReadyRead(int msecs);
private slots:
UT_DECLARE_DEFAULT_initTestCase
UT_DECLARE_DEFAULT_cleanupTestCase
void init(void);
void cleanup(void);
......@@ -89,8 +87,4 @@ private:
const char* _rgSignals[_cSignals];
};
// This unit test has gotten too flaky to run reliably under TeamCity. Removing for now till there is
// time to debug.
//DECLARE_TEST(TCPLinkUnitTest)
#endif
This diff is collapsed.
#ifndef UASUNITTEST_H
#define UASUNITTEST_H
#include <QObject>
#include <QtCore/QString>
#include <QtTest/QtTest>
#include <QApplication>
#include "UAS.h"
#include "MAVLinkProtocol.h"
#include "SerialLink.h"
#include "UASInterface.h"
#include "AutoTest.h"
#include "UnitTest.h"
#include "LinkManager.h"
#include "UASWaypointManager.h"
#include "SerialLink.h"
#include "LinkInterface.h"
class UASUnitTest : public QObject
#define UASID 100
class UASUnitTest : public UnitTest
{
Q_OBJECT
public:
#define UASID 100
MAVLinkProtocol* mav;
UAS* uas;
UASUnitTest();
UASUnitTest(void);
private slots:
void init();
void cleanup();
void getUASID_test();
void getUASName_test();
void getUpTime_test();
void getCommunicationStatus_test();
void filterVoltage_test();
void getAutopilotType_test();
void setAutopilotType_test();
void getStatusForCode_test();
void getLocalX_test();
void getLocalY_test();
void getLocalZ_test();
void getLatitude_test();
void getLongitude_test();
void getAltitudeAMSL_test();
void getAltitudeRelative_test();
void getRoll_test();
void getPitch_test();
void getYaw_test();
void getSelected_test();
void getSystemType_test();
void getAirframe_test();
void setAirframe_test();
void getWaypointList_test();
void signalWayPoint_test();
void getWaypoint_test();
UT_DECLARE_DEFAULT_initTestCase
UT_DECLARE_DEFAULT_cleanupTestCase
void init(void);
void cleanup(void);
void getUASID_test(void);
void getUASName_test(void);
void getUpTime_test(void);
void getCommunicationStatus_test(void);
void filterVoltage_test(void);
void getAutopilotType_test(void);
void setAutopilotType_test(void);
void getStatusForCode_test(void);
void getLocalX_test(void);
void getLocalY_test(void);
void getLocalZ_test(void);
void getLatitude_test(void);
void getLongitude_test(void);
void getAltitudeAMSL_test(void);
void getAltitudeRelative_test(void);
void getRoll_test(void);
void getPitch_test(void);
void getYaw_test(void);
void getSelected_test(void);
void getSystemType_test(void);
void getAirframe_test(void);
void setAirframe_test(void);
void getWaypointList_test(void);
void signalWayPoint_test(void);
void getWaypoint_test(void);
private:
MAVLinkProtocol* _mavlink;
UAS* _uas;
};
DECLARE_TEST(UASUnitTest)
#endif // UASUNITTEST_H
#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/>.
======================================================================*/
/// @file
/// @brief Base class for all unit tests
///
/// @author Don Gagne <don@thegagnes.com>
#include "UnitTest.h"
#include "QGCApplication.h"
bool UnitTest::_messageBoxRespondedTo = false;
bool UnitTest::_badResponseButton = false;
QMessageBox::StandardButton UnitTest::_messageBoxResponseButton = QMessageBox::NoButton;
int UnitTest::_missedMessageBoxCount = 0;
UnitTest::UnitTestList_t UnitTest::_tests;
UnitTest::UnitTest(void) :
_unitTestRun(false),
_initTestCaseCalled(false),
_cleanupTestCaseCalled(false),
_initCalled(false),
_cleanupCalled(false)
{
}
UnitTest::~UnitTest()
{
if (_unitTestRun) {
// Derived classes must call base class implementations
Q_ASSERT(_initTestCaseCalled);
Q_ASSERT(_cleanupTestCaseCalled);
Q_ASSERT(_initCalled);
Q_ASSERT(_cleanupCalled);
}
}
void UnitTest::_addTest(QObject* test)
{
Q_ASSERT(!_tests.contains(test));
_tests.append(test);
}
void UnitTest::_unitTestCalled(void)
{
_unitTestRun = true;
}
int UnitTest::run(int argc, char *argv[], QString& singleTest)
{
int ret = 0;
foreach (QObject* test, _tests) {
if (singleTest.isEmpty() || singleTest == test->objectName()) {
ret += QTest::qExec(test, argc, argv);
}
}
return ret;
}
/// @brief Called at the initialization of the entire unit test.
/// Make sure to call first in your derived class
void UnitTest::initTestCase(void)
{
_initTestCaseCalled = true;
_activeUnitTest = this;
_missedMessageBoxCount = 0;
_badResponseButton = false;
}
/// @brief Called at the end of the entire unit test.
/// Make sure to call first in your derived class
void UnitTest::cleanupTestCase(void)
{
_cleanupTestCaseCalled = true;
Q_ASSERT(_activeUnitTest != NULL);
_activeUnitTest = NULL;
int missedMessageBoxCount = _missedMessageBoxCount;
_missedMessageBoxCount = 0;
// Make sure to reset any needed variables since this can fall and cause the rest of the method to be skipped
QCOMPARE(missedMessageBoxCount, 0);
}
/// @brief Called before each test.
/// Make sure to call first in your derived class
void UnitTest::init(void)
{
_initCalled = true;
_messageBoxRespondedTo = false;
_missedMessageBoxCount = 0;
_badResponseButton = false;
_messageBoxResponseButton = QMessageBox::NoButton;
// Each test gets a clean global state
qgcApp()->destroySingletonsForUnitTest();
qgcApp()->createSingletonsForUnitTest();
}
/// @brief Called after each test.
/// Make sure to call first in your derived class
void UnitTest::cleanup(void)
{
_cleanupCalled = true;
int missedMessageBoxCount = _missedMessageBoxCount;
_missedMessageBoxCount = 0;
// Make sure to reset any needed variables since this can fall and cause the rest of the method to be skipped
QCOMPARE(missedMessageBoxCount, 0);
}
void UnitTest::setExpectedMessageBox(QMessageBox::StandardButton response)
{
Q_ASSERT(!_messageBoxRespondedTo);
// We use an obsolete StandardButton value to signal that response button has not been set. So you can't use this.
Q_ASSERT(response != QMessageBox::NoButton);
Q_ASSERT(_messageBoxResponseButton == QMessageBox::NoButton);
int missedMessageBoxCount = _missedMessageBoxCount;
_missedMessageBoxCount = 0;
QCOMPARE(missedMessageBoxCount, 0);
_messageBoxResponseButton = response;
}
void UnitTest::checkExpectedMessageBox(int expectFailFlags)
{
// Previous call to setExpectedMessageBox should have already checked this
Q_ASSERT(_missedMessageBoxCount == 0);
// Check for a valid response
if (expectFailFlags & expectFailBadResponseButton) {
QEXPECT_FAIL("", "Expecting failure due to bad button response", Continue);
}
QCOMPARE(_badResponseButton, false);
if (expectFailFlags & expectFailNoMessageBox) {
QEXPECT_FAIL("", "Expecting failure due to no message box", Continue);
}
QCOMPARE(_messageBoxRespondedTo, true);
}
QMessageBox::StandardButton UnitTest::_messageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
{
QMessageBox::StandardButton retButton;
Q_UNUSED(icon);
Q_UNUSED(title);
Q_UNUSED(text);
if (_messageBoxResponseButton == QMessageBox::NoButton) {
// If no response button is set it means we were not expecting this message box. Response with default
_missedMessageBoxCount++;
retButton = defaultButton;
} else {
if (_messageBoxResponseButton & buttons) {
// Everything is correct, use the specified response
retButton = _messageBoxResponseButton;
} else {
// Trying to respond with a button not in the dialog. This is an error. Respond with default
_badResponseButton = true;
retButton = defaultButton;
}
_messageBoxRespondedTo = true;
}
// Clear response for next message box
_messageBoxResponseButton = QMessageBox::NoButton;
return retButton;
}
/*=====================================================================
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/>.
======================================================================*/
/// @file
/// @brief Base class for all unit tests
///
/// @author Don Gagne <don@thegagnes.com>
#ifndef UNITTEST_H
#define UNITTEST_H
#include <QObject>
#include <QtTest>
#include <QMessageBox>
#define UT_REGISTER_TEST(className) static UnitTestWrapper<className> t(#className);
/// @brief If you don't need you own specific implemenation of the test case setup methods
/// you can use these macros to declare the default implementation which just calls
/// the base class.
#define UT_DECLARE_DEFAULT_initTestCase
//virtual void initTestCase(void) { UnitTest::_initTestCase(); }
#define UT_DECLARE_DEFAULT_cleanupTestCase
// virtual void cleanupTestCase(void) { UnitTest::_cleanupTestCase(); }
#define UT_DECLARE_DEFAULT_init
//virtual void init(void) { UnitTest::_init(); }
#define UT_DECLARE_DEFAULT_cleanup
//virtual void cleanup(void) { UnitTest::_cleanup(); }
class QGCMessageBox;
class UnitTest;
static UnitTest* _activeUnitTest = NULL; ///< Currently active unit test
class UnitTest : public QObject
{
Q_OBJECT
public:
UnitTest(void);
virtual ~UnitTest(void);
/// @brief Called to run all the registered unit tests
/// @param argc argc from main
/// @param argv argv from main
/// @param singleTest Name of test to just run a single test
static int run(int argc, char *argv[], QString& singleTest);
/// @brief Sets up for an expected QGCMessageBox
/// @param response Response to take on message box
void setExpectedMessageBox(QMessageBox::StandardButton response);
enum {
expectFailNoFailure = 1 << 0, ///< not expecting any failures
expectFailNoMessageBox = 1 << 1, ///< expecting a failure due to no message box displayed
expectFailBadResponseButton = 1 << 2 ///< expecting a failure due to bad button response
};
/// @brief Check whether a message box was displayed and correctly responded to
// @param Expected failure response flags
void checkExpectedMessageBox(int expectFailFlags = expectFailNoFailure);
// Should only be called by UnitTestWrapper
static void _addTest(QObject* test);
protected slots:
// These are all pure virtuals to force the derived class to implement each one and in turn
// call the UnitTest private implementation.
/// @brief Called at the initialization of the entire unit test.
/// Make sure to call _initTestCase first in your derived class.
virtual void initTestCase(void);
/// @brief Called at the end of the entire unit test.
/// Make sure to call _cleanupTestCase first in your derived class.
virtual void cleanupTestCase(void);
/// @brief Called before each test.
/// Make sure to call _init first in your derived class.
virtual void init(void);
/// @brief Called after each test.
/// Make sure to call _cleanup first in your derived class.
virtual void cleanup(void);
protected:
/// @brief Must be called first by derived class implementation
void _initTestCase(void);
/// @brief Must be called first by derived class implementation
void _cleanupTestCase(void);
/// @brief Must be called first by derived class implementation
void _init(void);
/// @brief Must be called first by derived class implementation
void _cleanup(void);
private:
// When the app is running in unit test mode the QGCMessageBox methods are re-routed here.
static QMessageBox::StandardButton _messageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton);
// This allows the private call to _messageBox
friend class QGCMessageBox;
void _unitTestCalled(void);
static bool _messageBoxRespondedTo; ///< Message box was responded to
static bool _badResponseButton; ///< Attempt to repond to expected message box with button not being displayed
static QMessageBox::StandardButton _messageBoxResponseButton; ///< Response to next message box
static int _missedMessageBoxCount; ///< Count of message box not checked with call to messageBoxWasDisplayed
bool _unitTestRun; ///< true: Unit Test was run
bool _initTestCaseCalled; ///< true: UnitTest::_initTestCase was called
bool _cleanupTestCaseCalled; ///< true: UnitTest::_cleanupTestCase was called
bool _initCalled; ///< true: UnitTest::_init was called
bool _cleanupCalled; ///< true: UnitTest::_cleanup was called
typedef QList<QObject*> UnitTestList_t;
static UnitTestList_t _tests;
};
template <class T>
class UnitTestWrapper {
public:
UnitTestWrapper(const QString& name) :
_unitTest(new T)
{
_unitTest->setObjectName(name);
UnitTest::_addTest(_unitTest.data());
}
private:
QSharedPointer<T> _unitTest;
};
#endif
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