1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtTest/private/qsignaldumper_p.h>
5
6#include <QtCore/qlist.h>
7#include <QtCore/qmetaobject.h>
8#include <QtCore/qmetatype.h>
9#include <QtCore/qobject.h>
10#include <QtCore/qvariant.h>
11
12#include <QtTest/private/qtestlog_p.h>
13
14#include <QtCore/private/qmetaobject_p.h>
15
16QT_BEGIN_NAMESPACE
17
18namespace QTest
19{
20
21inline static void qPrintMessage(const QByteArray &ba)
22{
23 QTestLog::info(msg: ba.constData(), file: nullptr, line: 0);
24}
25
26Q_GLOBAL_STATIC(QList<QByteArray>, ignoreClasses)
27Q_CONSTINIT static QBasicMutex ignoreClassesMutex;
28Q_CONSTINIT thread_local int iLevel = 0;
29Q_CONSTINIT thread_local int ignoreLevel = 0;
30enum { IndentSpacesCount = 4 };
31
32static bool classIsIgnored(const char *className)
33{
34 if (Q_LIKELY(!ignoreClasses.exists()))
35 return false;
36 QMutexLocker locker(&ignoreClassesMutex);
37 if (ignoreClasses()->isEmpty())
38 return false;
39 return ignoreClasses()->contains(t: QByteArrayView(className));
40}
41
42static void qSignalDumperCallback(QObject *caller, int signal_index, void **argv)
43{
44 Q_ASSERT(caller);
45 Q_ASSERT(argv);
46 Q_UNUSED(argv);
47 const QMetaObject *mo = caller->metaObject();
48 Q_ASSERT(mo);
49 QMetaMethod member = QMetaObjectPrivate::signal(m: mo, signal_index);
50 Q_ASSERT(member.isValid());
51
52 if (classIsIgnored(className: mo->className())) {
53 ++QTest::ignoreLevel;
54 return;
55 }
56
57 QByteArray str;
58 str.fill(c: ' ', size: QTest::iLevel++ * QTest::IndentSpacesCount);
59 str += "Signal: ";
60 str += mo->className();
61 str += '(';
62
63 QString objname = caller->objectName();
64 str += objname.toLocal8Bit();
65 if (!objname.isEmpty())
66 str += ' ';
67 str += QByteArray::number(quintptr(caller), base: 16).rightJustified(width: 8, fill: '0');
68
69 str += ") ";
70 str += member.name();
71 str += " (";
72
73 QList<QByteArray> args = member.parameterTypes();
74 for (int i = 0; i < args.size(); ++i) {
75 const QByteArray &arg = args.at(i);
76 int typeId = QMetaType::fromName(name: args.at(i).constData()).id();
77 if (arg.endsWith(c: '*') || arg.endsWith(c: '&')) {
78 str += '(';
79 str += arg;
80 str += ')';
81 if (arg.endsWith(c: '&'))
82 str += '@';
83
84 quintptr addr = quintptr(*reinterpret_cast<void **>(argv[i + 1]));
85 str.append(a: QByteArray::number(addr, base: 16).rightJustified(width: 8, fill: '0'));
86 } else if (typeId != QMetaType::UnknownType) {
87 Q_ASSERT(typeId != QMetaType::Void); // void parameter => metaobject is corrupt
88 str.append(a: arg)
89 .append(c: '(')
90 .append(a: QVariant(QMetaType(typeId), argv[i + 1]).toString().toLocal8Bit())
91 .append(c: ')');
92 }
93 str.append(s: ", ");
94 }
95 if (str.endsWith(bv: ", "))
96 str.chop(n: 2);
97 str.append(c: ')');
98 qPrintMessage(ba: str);
99}
100
101static void qSignalDumperCallbackSlot(QObject *caller, int method_index, void **argv)
102{
103 Q_ASSERT(caller);
104 Q_ASSERT(argv);
105 Q_UNUSED(argv);
106 const QMetaObject *mo = caller->metaObject();
107 Q_ASSERT(mo);
108 QMetaMethod member = mo->method(index: method_index);
109 if (!member.isValid())
110 return;
111
112 if (QTest::ignoreLevel || classIsIgnored(className: mo->className()))
113 return;
114
115 QByteArray str;
116 str.fill(c: ' ', size: QTest::iLevel * QTest::IndentSpacesCount);
117 str += "Slot: ";
118 str += mo->className();
119 str += '(';
120
121 QString objname = caller->objectName();
122 str += objname.toLocal8Bit();
123 if (!objname.isEmpty())
124 str += ' ';
125 str += QByteArray::number(quintptr(caller), base: 16).rightJustified(width: 8, fill: '0');
126
127 str += ") ";
128 str += member.methodSignature();
129 qPrintMessage(ba: str);
130}
131
132static void qSignalDumperCallbackEndSignal(QObject *caller, int /*signal_index*/)
133{
134 Q_ASSERT(caller); Q_ASSERT(caller->metaObject());
135 if (classIsIgnored(className: caller->metaObject()->className())) {
136 --QTest::ignoreLevel;
137 Q_ASSERT(QTest::ignoreLevel >= 0);
138 return;
139 }
140 --QTest::iLevel;
141 Q_ASSERT(QTest::iLevel >= 0);
142}
143
144}
145
146void QSignalDumper::setEnabled(bool enabled)
147{
148 s_isEnabled = enabled;
149}
150
151void QSignalDumper::startDump()
152{
153 if (!s_isEnabled)
154 return;
155
156 static QSignalSpyCallbackSet set = { .signal_begin_callback: QTest::qSignalDumperCallback,
157 .slot_begin_callback: QTest::qSignalDumperCallbackSlot, .signal_end_callback: QTest::qSignalDumperCallbackEndSignal, .slot_end_callback: nullptr };
158 qt_register_signal_spy_callbacks(callback_set: &set);
159}
160
161void QSignalDumper::endDump()
162{
163 qt_register_signal_spy_callbacks(callback_set: nullptr);
164}
165
166void QSignalDumper::ignoreClass(const QByteArray &klass)
167{
168 QMutexLocker locker(&QTest::ignoreClassesMutex);
169 if (QTest::ignoreClasses())
170 QTest::ignoreClasses()->append(t: klass);
171}
172
173void QSignalDumper::clearIgnoredClasses()
174{
175 QMutexLocker locker(&QTest::ignoreClassesMutex);
176 if (QTest::ignoreClasses.exists())
177 QTest::ignoreClasses()->clear();
178}
179
180bool QSignalDumper::s_isEnabled = false;
181
182QT_END_NAMESPACE
183

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/testlib/qsignaldumper.cpp