GCC Code Coverage Report


Directory: ./
File: qtpdcom/src/Process.cpp
Date: 2023-11-12 04:06:57
Exec Total Coverage
Lines: 212 315 67.3%
Branches: 153 364 42.0%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * Copyright (C) 2009-2022 Florian Pose <fp@igh.de>
4 *
5 * This file is part of the QtPdCom library.
6 *
7 * The QtPdCom library is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * The QtPdCom library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 * License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with the QtPdCom Library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 ****************************************************************************/
21
22 #include "Process.h"
23 #include "MessageManager.h"
24
25 #include <pdcom5/MessageManagerBase.h>
26 #include <pdcom5/Exception.h>
27
28 #ifdef PDCOM_USE_SASL
29 #include "LoginManager_p.h"
30 #include "LoginManager.h"
31 #endif
32
33
34 #ifdef __WIN32__
35 #include <windows.h> // GetUserName(), gethostname()
36 #include <lmcons.h> // UNLEN
37 #else
38 #include <unistd.h> // getlogin()
39 #endif
40
41 #include <string>
42 #include <list>
43
44 #include <QTranslator>
45 #include <QFutureInterface>
46 #include <QQueue>
47 #include <QSslSocket>
48 #include <QSslKey>
49 #include <QSslCertificate>
50 #include <QSslConfiguration>
51
52 #include <QtPdCom1.h>
53 #include <git_revision_hash.h>
54
55 #define STR(x) #x
56 #define QUOTE(x) STR(x)
57
58 const char *const QtPdCom::qtpdcom_version_code =
59 QUOTE(QTPDCOM_MAJOR) "." QUOTE(QTPDCOM_MINOR) "." QUOTE(QTPDCOM_RELEASE);
60
61 const char *const QtPdCom::qtpdcom_full_version = GIT_REV;
62
63 #define DEBUG_DATA 0
64
65 using QtPdCom::Process;
66 using VariablePromise = QFutureInterface<PdCom::Variable>;
67 using ListPromise = QFutureInterface<QtPdCom::VariableList>;
68 using PingPromise = QFutureInterface<void>;
69 using ClientStatisticsPromise = QFutureInterface<std::vector<PdCom::ClientStatistics>>;
70
71 /****************************************************************************/
72
73 18 struct Process::Impl
74 {
75 18 Impl(Process *process):
76 process(process),
77 messageManager(),
78 appName("QtPdCom1"),
79 socket(),
80 defaultSslConfig(socket.sslConfiguration()),
81 socketValid(false),
82 connectionState(Disconnected),
83 rxBytes(0),
84
8/16
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 18 times.
✗ Branch 9 not taken.
✓ Branch 15 taken 18 times.
✗ Branch 16 not taken.
✓ Branch 22 taken 18 times.
✗ Branch 23 not taken.
✓ Branch 27 taken 18 times.
✗ Branch 28 not taken.
✓ Branch 31 taken 18 times.
✗ Branch 32 not taken.
✓ Branch 43 taken 18 times.
✗ Branch 44 not taken.
✓ Branch 49 taken 18 times.
✗ Branch 50 not taken.
18 txBytes(0)
85 {
86 // set the default process to the always last instance of process
87 18 defaultProcess = process;
88 18 }
89
90 void connectToHost(const QString &address, quint16 port);
91
92 Process * const process;
93
94 QtPdCom::MessageManager messageManager;
95
96 QString appName; /**< Our application name, that is announced to the
97 server. Default: QtPdCom1. */
98
99 QUrl url;
100 SslCaMode caMode = SslCaMode::NoTLS;
101 QList<QSslCertificate> caCertificates;
102 QSslKey privateKey;
103 QSslCertificate privateCert;
104 QSslSocket socket; /**< TCP socket to the process. */
105 // make backup from inital ssl config
106 const QSslConfiguration defaultSslConfig;
107
108 bool socketValid; /**< Connection state of the socket. */
109 ConnectionState connectionState; /**< The current connection state. */
110 QString errorString; /**< Error reason. Set, before error() is
111 emitted. */
112 quint64 rxBytes;
113 quint64 txBytes;
114
115 QQueue<VariablePromise> findVariableQueue;
116 VariablePromise currentFindVariablePromise;
117 QQueue<ListPromise> listVariableQueue;
118 ListPromise currentListPromise;
119 QQueue<PingPromise> pingQueue;
120 QQueue<ClientStatisticsPromise> clientStatisticsQueue;
121
122 static QtPdCom::Process *defaultProcess; /**< last created process
123 is the default process */
124
125 50 void setConnectionState(ConnectionState const state)
126 {
127
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 50 times.
✗ Branch 4 not taken.
50 if (connectionState != state) {
128 50 connectionState = state;
129 50 emit process->connectionStatusChanged();
130 }
131 50 }
132 };
133
134 /****************************************************************************/
135
136 QtPdCom::Process *QtPdCom::Process::Impl::defaultProcess = nullptr;
137
138 /*****************************************************************************
139 * Public implementation
140 ****************************************************************************/
141
142 /** Constructor.
143 */
144 18 Process::Process(QObject *parent):
145 QObject(parent),
146 PdCom::Process(),
147
3/6
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 11 taken 18 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 18 times.
✗ Branch 16 not taken.
18 impl{std::unique_ptr<Impl>{new Impl{this}}}
148 {
149
3/6
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 18 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 connect(&impl->socket, SIGNAL(connected()),
150 this, SLOT(socketConnected()));
151
3/6
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 18 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 connect(&impl->socket, SIGNAL(disconnected()),
152 this, SLOT(socketDisconnected()));
153
3/6
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 18 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 connect(&impl->socket, SIGNAL(error(QAbstractSocket::SocketError)),
154 this, SLOT(socketError()));
155
3/6
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 18 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 connect(&impl->socket, SIGNAL(readyRead()),
156 this, SLOT(socketRead()));
157
158
1/2
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
18 setMessageManager(&impl->messageManager);
159 18 }
160
161 /****************************************************************************/
162
163 /** Destructor.
164 */
165 37 Process::~Process()
166 {
167 18 setMessageManager(nullptr);
168
169 // reset default process if this instance is the default
170
2/2
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 1 times.
18 if (impl->defaultProcess == this) {
171 17 impl->defaultProcess = nullptr;
172 }
173
174 18 disconnectFromHost();
175 18 reset();
176 19 }
177
178 /****************************************************************************/
179
180 /** Sets the application name.
181 */
182 1 void Process::setApplicationName(const QString &name)
183 {
184 1 impl->appName = name;
185 1 }
186
187 QString Process::getApplicationName() const
188 {
189 return impl->appName;
190 }
191
192 /****************************************************************************/
193
194 /** Starts to connect to a process.
195 */
196 18 void Process::connectToHost(const QString &address, quint16 port)
197 {
198 18 impl->connectToHost(address, port);
199 18 }
200
201 18 void Process::Impl::connectToHost(const QString &address, quint16 port)
202 {
203
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 url.setHost(address);
204
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 url.setPort(port);
205
4/6
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 6 times.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 url.setScheme(caMode == SslCaMode::NoTLS ? "msr" : "msrs");
206
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 setConnectionState(Connecting);
207
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 6 times.
18 if (caMode == SslCaMode::NoTLS) {
208
1/2
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
12 socket.connectToHost(address, port);
209 12 return;
210 }
211
3/4
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 6 times.
✓ Branch 8 taken 12 times.
12 auto ssl_config = defaultSslConfig;
212
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 ssl_config.setPeerVerifyMode(QSslSocket::VerifyPeer);
213
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
6 if (caMode == SslCaMode::CustomCAs) {
214
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 ssl_config.setCaCertificates(caCertificates);
215
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 } else if (caMode == SslCaMode::IgnoreCertificate) {
216
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ssl_config.setPeerVerifyMode(QSslSocket::VerifyNone);
217 }
218
7/10
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2 times.
✓ Branch 13 taken 4 times.
6 if (!privateCert.isNull() && !privateKey.isNull()) {
219
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ssl_config.setPrivateKey(privateKey);
220
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ssl_config.setLocalCertificate(privateCert);
221 }
222
1/2
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 socket.setSslConfiguration(ssl_config);
223
1/2
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
6 socket.connectToHostEncrypted(address, port);
224 }
225
226 /****************************************************************************/
227
228 /** Disconnects from a process.
229 */
230 18 void Process::disconnectFromHost()
231 {
232
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 15 times.
✓ Branch 6 taken 3 times.
18 switch (impl->connectionState) {
233 15 case Connecting:
234 case Connected:
235 15 impl->socketValid = false;
236 15 impl->setConnectionState(Disconnected);
237 15 impl->rxBytes = 0;
238 15 impl->txBytes = 0;
239 15 reset();
240 15 impl->socket.disconnectFromHost();
241 15 impl->socket.abort();
242 15 emit disconnected();
243 15 break;
244
245 3 default:
246 3 break;
247 }
248 18 }
249
250 /****************************************************************************/
251
252 /**
253 * \return The connection state.
254 */
255 15 Process::ConnectionState Process::getConnectionState() const
256 {
257
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
15 return impl->connectionState;
258 }
259
260 /****************************************************************************/
261
262 /**
263 * \return \a true, if the process is connected.
264 */
265 73 bool Process::isConnected() const
266 {
267
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 73 times.
73 return impl->connectionState == Connected;
268 }
269
270 /****************************************************************************/
271
272 /**
273 * \return Error reason after the error() signal was emitted.
274 */
275 const QString &Process::getErrorString() const
276 {
277 return impl->errorString;
278 }
279
280 /****************************************************************************/
281
282 /**
283 * \return Host name of the process.
284 */
285 QString Process::getPeerName() const
286 {
287 return impl->socket.peerName();
288 }
289
290 /****************************************************************************/
291
292 /** Wrapper function for Process::findVariable.
293 */
294
295 15 QFuture<PdCom::Variable> Process::find(const QString &path)
296 {
297
1/2
✓ Branch 9 taken 15 times.
✗ Branch 10 not taken.
15 impl->currentFindVariablePromise = {};
298 15 impl->currentFindVariablePromise.reportStarted();
299 15 auto ans = impl->currentFindVariablePromise.future();
300
4/6
✓ Branch 5 taken 15 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 15 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 9 times.
✓ Branch 14 taken 6 times.
15 if (!PdCom::Process::find(path.toStdString()))
301 // variable was not cached, findReply() will be called later
302
1/2
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
9 impl->findVariableQueue.append(impl->currentFindVariablePromise);
303
2/4
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 11 taken 15 times.
✗ Branch 12 not taken.
15 impl->currentFindVariablePromise = {};
304 15 return ans;
305 }
306
307 /****************************************************************************/
308
309 /** Wrapper function for Process::list.
310 */
311
312 1 QFuture<QtPdCom::VariableList> Process::list(const QString& path)
313 {
314
1/2
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
1 impl->currentListPromise = {};
315 1 impl->currentListPromise.reportStarted();
316 1 auto ans = impl->currentListPromise.future();
317
3/6
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
1 if (!PdCom::Process::list(path.toStdString()))
318
1/2
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 impl->listVariableQueue.append(impl->currentListPromise);
319
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
1 impl->currentListPromise = {};
320 1 return ans;
321 }
322
323 /****************************************************************************/
324
325 QFuture<std::vector<PdCom::ClientStatistics>>
326 2 QtPdCom::Process::getClientStatisticsQt()
327 {
328
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 ClientStatisticsPromise ans;
329
1/2
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 impl->clientStatisticsQueue.append(ans);
330
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ans.reportStarted();
331
1/2
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 PdCom::Process::getClientStatistics();
332
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 return ans.future();
333 }
334
335 /****************************************************************************/
336
337 /** Send a broadcast message.
338 */
339 2 void Process::sendBroadcast(const QString &msg, const QString &attr)
340 {
341
6/12
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 18 taken 2 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 2 times.
✗ Branch 26 not taken.
✓ Branch 29 taken 2 times.
✗ Branch 30 not taken.
6 broadcast(msg.toLocal8Bit().constData(),
342
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 attr.toLocal8Bit().constData());
343 2 }
344
345 /****************************************************************************/
346
347 1 quint64 Process::getRxBytes() const
348 {
349 1 return impl->rxBytes;
350 }
351
352 /****************************************************************************/
353
354 1 quint64 Process::getTxBytes() const
355 {
356 1 return impl->txBytes;
357 }
358
359 6 void QtPdCom::Process::setCaMode(SslCaMode mode)
360 {
361
1/2
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
6 if (impl->caMode != mode)
362 {
363 6 impl->caMode = mode;
364
2/4
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✓ Branch 9 taken 6 times.
✗ Branch 10 not taken.
6 impl->url.setScheme(mode == SslCaMode::NoTLS ? "msr" : "msrs");
365 6 emit sslCaModeChanged();
366 }
367 6 }
368
369 Process::SslCaMode Process::getCaMode() const
370 {
371 return impl->caMode;
372 }
373
374
375 2 void QtPdCom::Process::setClientCertificate(
376 const QSslCertificate &cert,
377 const QSslKey &key)
378 {
379 2 impl->privateKey = key;
380 2 impl->privateCert = cert;
381 2 }
382
383 4 void QtPdCom::Process::setCustomCAs(QList<QSslCertificate> cas)
384 {
385 4 impl->caCertificates = std::move(cas);
386 4 }
387
388 /****************************************************************************/
389
390 QtPdCom::Process *Process::getDefaultProcess()
391 {
392 return QtPdCom::Process::Impl::defaultProcess;
393 }
394
395 /****************************************************************************/
396
397 /** Set default process "manually"
398 */
399 void Process::setDefaultProcess(QtPdCom::Process *process)
400 {
401 QtPdCom::Process::Impl::defaultProcess = process;
402 }
403
404 /****************************************************************************/
405
406 PdCom::MessageManagerBase *Process::getMessageManager() const
407 {
408 return &impl->messageManager;
409 }
410
411 4 void QtPdCom::Process::setLoginManager(LoginManager *lm)
412 {
413 #ifdef PDCOM_USE_SASL
414
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (lm) {
415 4 setAuthManager(lm->d_ptr.data());
416 } else {
417 setAuthManager(nullptr);
418 }
419 #else
420 throw PdCom::InvalidArgument("build without sasl support");
421 #endif
422 4 }
423
424 QtPdCom::LoginManager *Process::getLoginManager() const
425 {
426 #ifdef PDCOM_USE_SASL
427 if (const auto p = getAuthManager()) {
428 auto *lm_p = static_cast<LoginManagerPrivate*>(p);
429 return lm_p->q_ptr;
430 }
431 return nullptr;
432 #else
433 throw PdCom::InvalidArgument("build without sasl support");
434 #endif
435 }
436
437 /*****************************************************************************
438 * private methods
439 ****************************************************************************/
440
441 14 std::string Process::applicationName() const
442 {
443
3/6
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 14 times.
✗ Branch 14 not taken.
14 return impl->appName.toLocal8Bit().constData();
444 }
445
446 /****************************************************************************/
447
448 14 std::string Process::hostname() const
449 {
450 14 char hostname[256];
451
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
14 if (!gethostname(hostname, sizeof(hostname))) {
452
1/2
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
14 return hostname;
453 }
454 return "";
455 }
456
457 /****************************************************************************/
458
459 /** Read data from the socket.
460 */
461 64 int Process::read(char *buf, int count)
462 {
463 64 qint64 ret(impl->socket.read(buf, count));
464
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 if (ret > 0) {
465 64 impl->rxBytes += ret;
466 }
467 64 return ret;
468 }
469
470 /****************************************************************************/
471
472 /** Sends data via the socket.
473 *
474 * This is the implementation of the virtual PdCom::Process
475 * method to enable the Process object to send data to the process.
476 */
477 3870 void Process::write(const char *buf, size_t count)
478 {
479 #if DEBUG_DATA
480 qDebug() << "Writing:" << count << QByteArray(buf, count);
481 #endif
482
2/2
✓ Branch 0 taken 1935 times.
✓ Branch 1 taken 1935 times.
5805 while (count > 0) {
483 1935 qint64 ret(impl->socket.write(buf, count));
484
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1935 times.
1935 if (ret <= 0) {
485 qWarning("write() failed.");
486 impl->socketValid = false;
487 impl->rxBytes = 0;
488 impl->txBytes = 0;
489 reset();
490 impl->socket.disconnectFromHost();
491 emit error();
492 return;
493 }
494 1935 count -= ret;
495 1935 buf += ret;
496 1935 impl->txBytes += ret;
497 }
498 }
499
500 /****************************************************************************/
501
502 /** Flushed the socket.
503 */
504 61 void Process::flush()
505 {
506 #if DEBUG_DATA
507 qDebug() << "Flushing not implemented.";
508 #endif
509 61 }
510
511 /****************************************************************************/
512
513 /** The process is connected and ready.
514 *
515 * This virtual function from PdCom::Process has to be overloaded to let
516 * subclasses know about this event.
517 */
518 14 void Process::connected()
519 {
520 14 impl->setConnectionState(Connected);
521 14 emit processConnected();
522 14 }
523
524 /****************************************************************************/
525
526 /** Broadcast Reply.
527 */
528 4 void Process::broadcastReply(
529 const std::string &message,
530 const std::string &attr,
531 std::chrono::nanoseconds time_ns,
532 const std::string &user)
533
534 {
535
1/2
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
20 emit broadcastReceived(
536
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
8 QString::fromStdString(message),
537
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
8 QString::fromStdString(attr),
538 4 time_ns.count(),
539 8 QString::fromStdString(user));
540 4 }
541
542 /****************************************************************************/
543
544 /** Ping Reply.
545 */
546 3 void Process::pingReply()
547 {
548
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 if (impl->pingQueue.empty())
549 return;
550
551
2/4
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
6 auto promise = impl->pingQueue.dequeue();
552
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 promise.reportFinished();
553 }
554
555 /****************************************************************************/
556
557 4 QFuture<void> Process::pingQt()
558 {
559
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 PingPromise promise;
560
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 promise.reportStarted();
561
1/2
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 impl->pingQueue.push_back(promise);
562
1/2
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 PdCom::Process::ping();
563
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 return promise.future();
564 }
565
566 /****************************************************************************/
567
568 /** Resets the PdCom process.
569 */
570 36 void Process::reset()
571 {
572 try {
573
1/2
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
36 PdCom::Process::reset();
574 } catch (std::exception &e) {
575 // do nothing
576 }
577
578 36 impl->messageManager.reset();
579 // cancel all pending futures
580
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 36 times.
38 while (!impl->pingQueue.empty())
581
1/2
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 impl->pingQueue.dequeue().reportCanceled();
582
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 36 times.
1 while (!impl->findVariableQueue.empty())
583
1/2
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 impl->findVariableQueue.dequeue().reportCanceled();
584
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 36 times.
36 if (impl->currentFindVariablePromise.isRunning()) {
585 impl->currentFindVariablePromise.reportCanceled();
586 impl->currentFindVariablePromise = {};
587 }
588
589
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 36 times.
38 while (!impl->listVariableQueue.empty())
590
1/2
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 impl->listVariableQueue.dequeue().reportCanceled();
591
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 36 times.
36 if (impl->currentListPromise.isRunning()) {
592 impl->currentListPromise.reportCanceled();
593 impl->currentListPromise = {};
594 }
595
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 36 times.
38 while (!impl->clientStatisticsQueue.empty())
596
1/2
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 impl->clientStatisticsQueue.dequeue().reportCanceled();
597 36 }
598
599 /****************************************************************************/
600
601 /** Socket connection established.
602 *
603 * This is called, when the pure socket connection was established and the
604 * Process object can start using it.
605 */
606 18 void Process::socketConnected()
607 {
608 18 impl->socketValid = true;
609
1/2
✓ Branch 9 taken 18 times.
✗ Branch 10 not taken.
18 impl->socket.setSocketOption(QAbstractSocket::KeepAliveOption, 1);
610 18 }
611
612 /****************************************************************************/
613
614 /** Socket disconnected.
615 *
616 * The socket was closed and the process has to be told, that it is
617 * disconnected.
618 */
619 18 void Process::socketDisconnected()
620 {
621
2/5
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 18 times.
18 switch (impl->connectionState) {
622 case Connecting:
623 impl->socketValid = false;
624 impl->setConnectionState(ConnectError);
625 impl->rxBytes = 0;
626 impl->txBytes = 0;
627 reset();
628 emit error();
629 break;
630
631 case Connected:
632 impl->socketValid = false;
633 impl->setConnectionState(Disconnected);
634 impl->rxBytes = 0;
635 impl->txBytes = 0;
636 reset();
637 emit disconnected();
638 break;
639
640 18 default:
641 18 break;
642 }
643 18 }
644
645 /****************************************************************************/
646
647 /** There was a socket error.
648 *
649 * The error could come up either while connecting the socket, or when the
650 * socket was already connected.
651 */
652 4 void Process::socketError()
653 {
654 4 impl->errorString = impl->socket.errorString();
655
656
3/5
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
4 switch (impl->connectionState) {
657 3 case Connecting:
658 3 impl->socketValid = false;
659 3 impl->setConnectionState(ConnectError);
660 3 impl->rxBytes = 0;
661 3 impl->txBytes = 0;
662 3 reset();
663 3 emit error();
664 3 break;
665
666 case Connected:
667 impl->socketValid = false;
668 impl->setConnectionState(ConnectedError);
669 impl->rxBytes = 0;
670 impl->txBytes = 0;
671 reset();
672 emit error();
673 break;
674
675 1 default:
676 1 break;
677 }
678 4 }
679
680 /****************************************************************************/
681
682 /** The socket has new data to read.
683 */
684 128 void Process::socketRead()
685 {
686 try {
687
3/4
✓ Branch 6 taken 128 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 64 times.
✓ Branch 9 taken 64 times.
192 while (impl->socket.bytesAvailable() > 0) {
688
1/2
✓ Branch 4 taken 64 times.
✗ Branch 5 not taken.
64 asyncData();
689 }
690 }
691 catch (std::exception &e) {
692 impl->errorString = "Exception during asyncData(): ";
693 impl->errorString += e.what();
694 qCritical() << impl->errorString;
695 impl->socketValid = false;
696 if (impl->connectionState == Connected) {
697 impl->setConnectionState(ConnectedError);
698 } else {
699 impl->setConnectionState(ConnectError);
700 }
701 reset();
702 impl->socket.disconnectFromHost();
703 emit error();
704 }
705 catch (...) {
706 impl->errorString = "Unknown exception during asyncData()";
707 qCritical() << impl->errorString;
708 impl->socketValid = false;
709 if (impl->connectionState == Connected) {
710 impl->setConnectionState(ConnectedError);
711 } else {
712 impl->setConnectionState(ConnectError);
713 }
714 reset();
715 impl->socket.disconnectFromHost();
716 emit error();
717 }
718 64 }
719
720 /****************************************************************************/
721
722
723 14 void Process::findReply(const PdCom::Variable &var)
724 {
725
2/2
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 8 times.
14 if (impl->currentFindVariablePromise.isRunning()) {
726 // variable is cached, result of PdCom::Process::find() will be true
727 6 impl->currentFindVariablePromise.reportResult(var);
728 6 impl->currentFindVariablePromise.reportFinished();
729 }
730
1/2
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 else if (!impl->findVariableQueue.empty()) {
731
1/2
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
16 auto promise = impl->findVariableQueue.dequeue();
732
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 promise.reportResult(var);
733
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 promise.reportFinished();
734 }
735 14 }
736
737 void Process::listReply(const std::vector<PdCom::Variable> vars,
738 const std::vector<std::string> dirs)
739 {
740 ListPromise promise;
741 if (impl->currentListPromise.isRunning())
742 {
743 // list is cached, result of PdCom::Process::list() will be true
744 promise = impl->currentListPromise;
745 } else if (impl->listVariableQueue.empty())
746 return;
747 else
748 promise = impl->listVariableQueue.dequeue();
749 QVector<QString> qt_dirs;
750 qt_dirs.reserve(dirs.size());
751 for (const auto& s : dirs)
752 qt_dirs.append(QString::fromStdString(s));
753 QVector<PdCom::Variable> qt_vars;
754 qt_vars.resize(vars.size());
755 std::copy(vars.begin(), vars.end(), qt_vars.begin());
756 promise.reportResult(VariableList{std::move(qt_dirs), std::move(qt_vars)});
757 promise.reportFinished();
758 }
759
760 QUrl Process::getUrl() const
761 {
762 return impl->url;
763 }
764
765 int Process::getPort() const
766 {
767 return impl->url.port();
768 }
769
770 QString Process::getHost() const
771 {
772 return impl->url.host();
773 }
774
775 1 void QtPdCom::Process::clientStatisticsReply(
776 std::vector<PdCom::ClientStatistics> statistics)
777 {
778
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 if (impl->clientStatisticsQueue.empty())
779 return;
780
781
2/4
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
2 auto promise = impl->clientStatisticsQueue.dequeue();
782
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 promise.reportResult(statistics);
783
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 promise.reportFinished();
784 }
785
786 namespace {
787
788 using QtPdCom::details::takes_obj_as_first_parameter;
789
790 struct A {};
791
792 struct F1 {
793 int operator()(A& a, int);
794 };
795 struct F2 {
796 void operator()(int);
797 };
798
799 struct F3 {
800 void operator()();
801 };
802
803 struct F4 {
804 void operator()(A&);
805 };
806
807 template<class Fn, class Arg, class Param, class = void>
808 struct is_well_formed : std::false_type
809 {};
810
811 template<class Fn, class Arg, class Param>
812 struct is_well_formed<
813 Fn, Arg, Param,
814 typename std::enable_if<takes_obj_as_first_parameter<Fn, Arg, Param>() || true>::type
815 > : std::true_type
816 {};
817
818 static_assert(takes_obj_as_first_parameter<F1, A, int>(), "F1 takes object ref and int");
819 static_assert(!takes_obj_as_first_parameter<F2, A, int>(), "F2 only takes int");
820
821
822 static_assert(!takes_obj_as_first_parameter<F3, A>(), "F3 takes no arguments");
823 static_assert(takes_obj_as_first_parameter<F4, A>(), "F4 takes obj ref as argument");
824
825 static_assert(is_well_formed<F1, A, int>::value, "");
826 static_assert(is_well_formed<F2, A, int>::value, "");
827 static_assert(!is_well_formed<F3, A, int>::value,
828 "Mismatch beween expected arguments and actual lambda arguments is detected");
829
830 }
831