GCC Code Coverage Report


Directory: ./
File: qtpdcom/src/Process.cpp
Date: 2024-11-17 04:08:36
Exec Total Coverage
Lines: 215 321 67.0%
Branches: 152 358 42.5%

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