MultiSignalSpy.cc 7.02 KB
Newer Older
1 2 3 4 5 6 7 8 9
/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

Don Gagne's avatar
Don Gagne committed
10 11 12 13 14

#include "MultiSignalSpy.h"
#include <QEventLoop>
#include <QCoreApplication>
#include <QDebug>
15
#include <QTest>
Don Gagne's avatar
Don Gagne committed
16 17 18 19 20 21 22 23 24

/// @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),
25 26
    _signalEmitter(nullptr),
    _rgSignals(nullptr),
Don Gagne's avatar
Don Gagne committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
    _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

Don Gagne's avatar
Don Gagne committed
43 44 45
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
Don Gagne's avatar
Don Gagne committed
46 47 48 49 50 51 52 53 54 55 56 57
{
    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];
58
    Q_ASSERT(_rgSpys != nullptr);
Don Gagne's avatar
Don Gagne committed
59 60
    for (size_t i=0; i<_cSignals; i++) {
        _rgSpys[i] = new QSignalSpy(_signalEmitter, _rgSignals[i]);
61
        if (_rgSpys[i] == nullptr) {
Don Gagne's avatar
Don Gagne committed
62 63 64 65 66 67 68 69 70 71 72 73
            qDebug() << "Unabled to allocated QSignalSpy";
            return false;
        }
        if (!_rgSpys[i]->isValid()) {
            qDebug() << "Invalid signal";
            return false;
        }
    }
    
    return true;
}

Don Gagne's avatar
Don Gagne committed
74
bool MultiSignalSpy::_checkSignalByMaskWorker(quint32 mask, bool multipleSignalsAllowed)
Don Gagne's avatar
Don Gagne committed
75 76 77 78
{
    for (size_t i=0; i<_cSignals; i++) {
        if ((1 << i) & mask) {
            QSignalSpy* spy = _rgSpys[i];
79
            Q_ASSERT(spy != nullptr);
Don Gagne's avatar
Don Gagne committed
80
            
81 82
            if ((multipleSignalsAllowed && spy->count() ==  0) || (!multipleSignalsAllowed && spy->count() != 1)) {
                qDebug() << "Failed index:" << i;
Don Gagne's avatar
Don Gagne committed
83
                _printSignalState(mask);
Don Gagne's avatar
Don Gagne committed
84 85 86 87 88 89 90 91
                return false;
            }
        }
    }
    
    return true;
}

Don Gagne's avatar
Don Gagne committed
92
bool MultiSignalSpy::_checkOnlySignalByMaskWorker(quint32 mask, bool multipleSignalsAllowed)
Don Gagne's avatar
Don Gagne committed
93 94 95
{
    for (size_t i=0; i<_cSignals; i++) {
        QSignalSpy* spy = _rgSpys[i];
96
        Q_ASSERT(spy != nullptr);
Don Gagne's avatar
Don Gagne committed
97 98

        if ((1 << i) & mask) {
99
            if ((multipleSignalsAllowed && spy->count() ==  0) || (!multipleSignalsAllowed && spy->count() != 1)) {
Don Gagne's avatar
Don Gagne committed
100
                _printSignalState(mask);
Don Gagne's avatar
Don Gagne committed
101 102 103 104
                return false;
            }
        } else {
            if (spy->count() != 0) {
Don Gagne's avatar
Don Gagne committed
105
                _printSignalState(mask);
Don Gagne's avatar
Don Gagne committed
106 107 108 109 110 111 112 113
                return false;
            }
        }
    }
    
    return true;
}

Don Gagne's avatar
Don Gagne committed
114
bool MultiSignalSpy::checkSignalByMask(quint32 mask)
115 116 117 118
{
    return _checkSignalByMaskWorker(mask, false /* multipleSignalsAllowed */);
}

Don Gagne's avatar
Don Gagne committed
119
bool MultiSignalSpy::checkOnlySignalByMask(quint32 mask)
120 121 122 123
{
    return _checkOnlySignalByMaskWorker(mask, false /* multipleSignalsAllowed */);
}

Don Gagne's avatar
Don Gagne committed
124
bool MultiSignalSpy::checkSignalsByMask(quint32 mask)
125 126 127 128
{
    return _checkSignalByMaskWorker(mask, true /* multipleSignalsAllowed */);
}

Don Gagne's avatar
Don Gagne committed
129
bool MultiSignalSpy::checkOnlySignalsByMask(quint32 mask)
130 131 132 133
{
    return _checkOnlySignalByMaskWorker(mask, true /* multipleSignalsAllowed */);
}

Don Gagne's avatar
Don Gagne committed
134
/// @return true if signal count = 0 for specified signals
Don Gagne's avatar
Don Gagne committed
135
bool MultiSignalSpy::checkNoSignalByMask(quint32 mask)
Don Gagne's avatar
Don Gagne committed
136 137 138 139
{
    for (size_t i=0; i<_cSignals; i++) {
        if ((1 << i) & mask) {
            QSignalSpy* spy = _rgSpys[i];
140
            Q_ASSERT(spy != nullptr);
Don Gagne's avatar
Don Gagne committed
141 142

            if (spy->count() != 0) {
Don Gagne's avatar
Don Gagne committed
143
                _printSignalState(mask);
Don Gagne's avatar
Don Gagne committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
                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
Don Gagne's avatar
Don Gagne committed
159
QSignalSpy* MultiSignalSpy::getSpyByIndex(quint32 index)
Don Gagne's avatar
Don Gagne committed
160 161
{
    Q_ASSERT(index < _cSignals);
162
    Q_ASSERT(_rgSpys[index] != nullptr);
Don Gagne's avatar
Don Gagne committed
163 164 165 166 167

    return _rgSpys[index];
}

/// Sets the signal count to 0 for the specified signal
Don Gagne's avatar
Don Gagne committed
168
void MultiSignalSpy::clearSignalByIndex(quint32 index)
Don Gagne's avatar
Don Gagne committed
169 170
{
    Q_ASSERT(index < _cSignals);
171
    Q_ASSERT(_rgSpys[index] != nullptr);
Don Gagne's avatar
Don Gagne committed
172 173 174 175 176
    
    _rgSpys[index]->clear();
}

/// Sets the signal count to 0 for all specified signals
Don Gagne's avatar
Don Gagne committed
177
void MultiSignalSpy::clearSignalsByMask(quint32 mask)
Don Gagne's avatar
Don Gagne committed
178 179 180 181
{
    for (size_t i=0; i<_cSignals; i++) {
        if ((1 << i) & mask) {
            QSignalSpy* spy = _rgSpys[i];
182
            Q_ASSERT(spy != nullptr);
Don Gagne's avatar
Don Gagne committed
183 184 185 186 187 188 189 190 191

            spy->clear();
        }
    }
}

/// Sets the signal count to 0 for all signals
void MultiSignalSpy::clearAllSignals(void)
{
Don Gagne's avatar
Don Gagne committed
192
    for (quint32 i=0;i<_cSignals; i++) {
Don Gagne's avatar
Don Gagne committed
193 194 195 196 197 198 199 200 201 202 203 204 205
        clearSignalByIndex(i);
    }
}

void MultiSignalSpy::timerEvent(QTimerEvent * event)
{
    Q_UNUSED(event);
    _timeout = true;
}

/// Waits the specified signal
///     @return false for timeout
bool MultiSignalSpy::waitForSignalByIndex(
Don Gagne's avatar
Don Gagne committed
206
                                          quint32 index,  ///< [in] index of signal to wait on
Don Gagne's avatar
Don Gagne committed
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
                                          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) {
Don Gagne's avatar
Don Gagne committed
228
        QTest::qWait(100);
Don Gagne's avatar
Don Gagne committed
229 230 231 232 233 234 235 236 237
    }
    
    // Clean up and return status
    if (timerId) {
        killTimer(timerId);
    }

    return spy->count() != 0;
}
238

Don Gagne's avatar
Don Gagne committed
239
void MultiSignalSpy::_printSignalState(quint32 mask)
240 241
{
    for (size_t i=0; i<_cSignals; i++) {
Don Gagne's avatar
Don Gagne committed
242 243
        bool expected = (1 << i) & mask;

244
        QSignalSpy* spy = _rgSpys[i];
245
        Q_ASSERT(spy != nullptr);
Don Gagne's avatar
Don Gagne committed
246
        qDebug() << "Signal index:" << i << "count:" << spy->count() << "expected:" << expected << _rgSignals[i];
247 248
    }
}
Don Gagne's avatar
Don Gagne committed
249

Don Gagne's avatar
Don Gagne committed
250
bool MultiSignalSpy::pullBoolFromSignalIndex(quint32 index)
Don Gagne's avatar
Don Gagne committed
251 252 253 254
{
    QSignalSpy* spy = getSpyByIndex(index);
    return spy->value(0).value(0).toBool();
}
Don Gagne's avatar
Don Gagne committed
255 256 257 258 259 260 261 262 263 264 265 266

int MultiSignalSpy::pullIntFromSignalIndex(quint32 index)
{
    QSignalSpy* spy = getSpyByIndex(index);
    return spy->value(0).value(0).toInt();
}

QGeoCoordinate MultiSignalSpy::pullQGeoCoordinateFromSignalIndex(quint32 index)
{
    QSignalSpy* spy = getSpyByIndex(index);
    return spy->value(0).value(0).value<QGeoCoordinate>();
}