summaryrefslogtreecommitdiffstats
path: root/src/testlib/qsignalspy.h
blob: ccde13820ae5a0247bfeb067155ea5e6701e2d6b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QSIGNALSPY_H
#define QSIGNALSPY_H

#include <QtCore/qbytearray.h>
#include <QtCore/qlist.h>
#include <QtCore/qobject.h>
#include <QtCore/qmetaobject.h>
#include <QtTest/qtesteventloop.h>
#include <QtCore/qvariant.h>
#include <QtCore/qmutex.h>

#include <mutex>

QT_BEGIN_NAMESPACE


class QVariant;

class QSignalSpy: public QObject, public QList<QList<QVariant> >
{
    struct ObjectSignal {
        const QObject *obj;
        QMetaMethod sig;
    };

public:
    explicit QSignalSpy(const QObject *obj, const char *aSignal)
        : QSignalSpy(verify(obj, aSignal)) {}
#ifdef Q_QDOC
    template <typename PointerToMemberFunction>
    QSignalSpy(const QObject *object, PointerToMemberFunction signal);
#else
    template <typename Func>
    QSignalSpy(const typename QtPrivate::FunctionPointer<Func>::Object *obj, Func signal0)
        : QSignalSpy(verify(obj, QMetaMethod::fromSignal(signal0))) {}
#endif // Q_QDOC
    QSignalSpy(const QObject *obj, QMetaMethod signal)
        : QSignalSpy(verify(obj, signal)) {}

    inline bool isValid() const { return !sig.isEmpty(); }
    inline QByteArray signal() const { return sig; }

    bool wait(int timeout)
    { return wait(std::chrono::milliseconds{timeout}); }

    Q_TESTLIB_EXPORT bool wait(std::chrono::milliseconds timeout = std::chrono::seconds{5});

    int qt_metacall(QMetaObject::Call call, int methodId, void **a) override
    {
        methodId = QObject::qt_metacall(call, methodId, a);
        if (methodId < 0)
            return methodId;

        if (call == QMetaObject::InvokeMetaMethod) {
            if (methodId == 0) {
                appendArgs(a);
            }
            --methodId;
        }
        return methodId;
    }

private:
    explicit QSignalSpy(ObjectSignal os)
    {
        if (!os.obj)
            return;

        auto tmp = makeArgs(os.sig, os.obj);
        {
            const auto lock = std::scoped_lock(m_mutex);
            args = std::move(tmp);
        }
        if (!connectToSignal(os.obj, os.sig.methodIndex()))
            return;

        sig = os.sig.methodSignature();
    }


    Q_TESTLIB_EXPORT static ObjectSignal verify(const QObject *obj, QMetaMethod signal);
    Q_TESTLIB_EXPORT static ObjectSignal verify(const QObject *obj, const char *aSignal);

    Q_TESTLIB_EXPORT bool connectToSignal(const QObject *sender, int sigIndex);

    Q_TESTLIB_EXPORT static QList<int> makeArgs(const QMetaMethod &member, const QObject *obj);
    Q_TESTLIB_EXPORT void appendArgs(void **a);

    // the full, normalized signal name
    QByteArray sig;
    // holds the QMetaType types for the argument list of the signal
    QList<int> args;

    QTestEventLoop m_loop;
    bool m_waiting = false;
    QMutex m_mutex; // protects m_waiting, args and the QList base class, between appendArgs() and wait()
};

QT_END_NAMESPACE

#endif