1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "debugutil_p.h"
30#include "qqmldebugprocess_p.h"
31
32#include <private/qqmldebugconnection_p.h>
33
34#include <QtCore/qeventloop.h>
35#include <QtCore/qtimer.h>
36
37bool QQmlDebugTest::waitForSignal(QObject *receiver, const char *member, int timeout) {
38 QEventLoop loop;
39 QTimer timer;
40 timer.setSingleShot(true);
41 QObject::connect(sender: &timer, SIGNAL(timeout()), receiver: &loop, SLOT(quit()));
42 QObject::connect(sender: receiver, signal: member, receiver: &loop, SLOT(quit()));
43 timer.start(msec: timeout);
44 loop.exec();
45 if (!timer.isActive())
46 qWarning(msg: "waitForSignal %s timed out after %d ms", member, timeout);
47 return timer.isActive();
48}
49
50QList<QQmlDebugClient *> QQmlDebugTest::createOtherClients(QQmlDebugConnection *connection)
51{
52 QList<QQmlDebugClient *> ret;
53
54 static const auto debuggerServices
55 = QStringList({"V8Debugger", "QmlDebugger", "DebugMessages"});
56 static const auto inspectorServices
57 = QStringList({"QmlInspector"});
58 static const auto profilerServices
59 = QStringList({"CanvasFrameRate", "EngineControl", "DebugMessages"});
60
61 for (const QString &service : debuggerServices) {
62 if (!connection->client(name: service))
63 ret << new QQmlDebugClient(service, connection);
64 }
65 for (const QString &service : inspectorServices) {
66 if (!connection->client(name: service))
67 ret << new QQmlDebugClient(service, connection);
68 }
69 for (const QString &service : profilerServices) {
70 if (!connection->client(name: service))
71 ret << new QQmlDebugClient(service, connection);
72 }
73 return ret;
74}
75
76QString QQmlDebugTest::clientStateString(const QQmlDebugClient *client)
77{
78 if (!client)
79 return QLatin1String("null");
80
81 switch (client->state()) {
82 case QQmlDebugClient::NotConnected: return QLatin1String("Not connected");
83 case QQmlDebugClient::Unavailable: return QLatin1String("Unavailable");
84 case QQmlDebugClient::Enabled: return QLatin1String("Enabled");
85 default: return QLatin1String("Invalid");
86 }
87
88}
89
90QString QQmlDebugTest::connectionStateString(const QQmlDebugConnection *connection)
91{
92 if (!connection)
93 return QLatin1String("null");
94
95 return connection->isConnected() ? QLatin1String("connected") : QLatin1String("not connected");
96}
97
98QQmlDebugTestClient::QQmlDebugTestClient(const QString &s, QQmlDebugConnection *c)
99 : QQmlDebugClient(s, c)
100{
101 connect(sender: this, signal: &QQmlDebugClient::stateChanged, context: this, slot: [this](QQmlDebugClient::State newState) {
102 QCOMPARE(newState, state());
103 });
104}
105
106QByteArray QQmlDebugTestClient::waitForResponse()
107{
108 lastMsg.clear();
109 QQmlDebugTest::waitForSignal(receiver: this, SIGNAL(serverMessage(QByteArray)));
110 if (lastMsg.isEmpty()) {
111 qWarning() << "no response from server!";
112 return QByteArray();
113 }
114 return lastMsg;
115}
116
117void QQmlDebugTestClient::messageReceived(const QByteArray &ba)
118{
119 lastMsg = ba;
120 emit serverMessage(ba);
121}
122
123QQmlDebugTest::ConnectResult QQmlDebugTest::connectTo(
124 const QString &executable, const QString &services, const QString &extraArgs,
125 bool block)
126{
127 QStringList arguments;
128 arguments << QString::fromLatin1(str: "-qmljsdebugger=port:13773,13783%3%4")
129 .arg(a: block ? QStringLiteral(",block") : QString())
130 .arg(a: services.isEmpty() ? services : (QStringLiteral(",services:") + services))
131 << extraArgs;
132
133 m_process = createProcess(executable);
134 if (!m_process)
135 return ProcessFailed;
136
137 m_process->start(arguments: QStringList() << arguments);
138 if (!m_process->waitForSessionStart())
139 return SessionFailed;
140
141 m_connection = createConnection();
142 if (!m_connection)
143 return ConnectionFailed;
144
145 m_clients = createClients();
146 if (m_clients.contains(t: nullptr))
147 return ClientsFailed;
148
149 ClientStateHandler stateHandler(m_clients, createOtherClients(connection: m_connection), services.isEmpty()
150 ? QQmlDebugClient::Enabled : QQmlDebugClient::Unavailable);
151
152
153 const int port = m_process->debugPort();
154 m_connection->connectToHost(hostName: QLatin1String("127.0.0.1"), port);
155
156 QEventLoop loop;
157 QTimer timer;
158 QObject::connect(sender: &stateHandler, signal: &ClientStateHandler::allOk, receiver: &loop, slot: &QEventLoop::quit);
159 QObject::connect(sender: m_connection, signal: &QQmlDebugConnection::disconnected, receiver: &loop, slot: &QEventLoop::quit);
160 QObject::connect(sender: &timer, signal: &QTimer::timeout, receiver: &loop, slot: &QEventLoop::quit);
161
162 timer.start(msec: 5000);
163 loop.exec();
164
165 if (!stateHandler.allEnabled())
166 return EnableFailed;
167
168 if (!stateHandler.othersAsExpected())
169 return RestrictFailed;
170
171 return ConnectSuccess;
172}
173
174QList<QQmlDebugClient *> QQmlDebugTest::createClients()
175{
176 return QList<QQmlDebugClient *>();
177}
178
179QQmlDebugProcess *QQmlDebugTest::createProcess(const QString &executable)
180{
181 return new QQmlDebugProcess(executable, this);
182}
183
184QQmlDebugConnection *QQmlDebugTest::createConnection()
185{
186 return new QQmlDebugConnection(this);
187}
188
189void QQmlDebugTest::cleanup()
190{
191 if (QTest::currentTestFailed()) {
192 const QString null = QStringLiteral("null");
193
194 qDebug() << "Process State:" << (m_process ? m_process->stateString() : null);
195 qDebug() << "Application Output:" << (m_process ? m_process->output() : null);
196 qDebug() << "Connection State:" << QQmlDebugTest::connectionStateString(connection: m_connection);
197 for (QQmlDebugClient *client : m_clients) {
198 if (client)
199 qDebug() << client->name() << "State:" << QQmlDebugTest::clientStateString(client);
200 else
201 qDebug() << "Failed Client:" << null;
202 }
203 }
204
205 qDeleteAll(c: m_clients);
206 m_clients.clear();
207
208 delete m_connection;
209 m_connection = nullptr;
210
211 if (m_process) {
212 m_process->stop();
213 delete m_process;
214 m_process = nullptr;
215 }
216}
217
218ClientStateHandler::ClientStateHandler(const QList<QQmlDebugClient *> &clients,
219 const QList<QQmlDebugClient *> &others,
220 QQmlDebugClient::State expectedOthers) :
221 m_clients(clients), m_others(others), m_expectedOthers(expectedOthers)
222{
223 for (QQmlDebugClient *client : m_clients) {
224 QObject::connect(sender: client, signal: &QQmlDebugClient::stateChanged,
225 receiver: this, slot: &ClientStateHandler::checkStates);
226 }
227 for (QQmlDebugClient *client : m_others) {
228 QObject::connect(sender: client, signal: &QQmlDebugClient::stateChanged,
229 receiver: this, slot: &ClientStateHandler::checkStates);
230 }
231}
232
233ClientStateHandler::~ClientStateHandler()
234{
235 qDeleteAll(c: m_others);
236}
237
238void ClientStateHandler::checkStates()
239{
240 for (QQmlDebugClient *client : m_clients) {
241 if (client->state() != QQmlDebugClient::Enabled)
242 return;
243 }
244
245 m_allEnabled = true;
246
247 for (QQmlDebugClient *other : m_others) {
248 if (other->state() != m_expectedOthers)
249 return;
250 }
251
252 m_othersAsExpected = true;
253 emit allOk();
254}
255
256QString debugJsServerPath(const QString &selfPath)
257{
258 static const char *debugserver = "qqmldebugjsserver";
259 QString appPath = QCoreApplication::applicationDirPath();
260 const int position = appPath.lastIndexOf(s: selfPath);
261 return (position == -1 ? appPath : appPath.replace(i: position, len: selfPath.length(), after: debugserver))
262 + "/" + debugserver;
263}
264

source code of qtdeclarative/tests/auto/qml/debugger/shared/debugutil.cpp