GCC Code Coverage Report


Directory: ./
File: src/Process.cpp
Date: 2025-02-27 10:29:23
Exec Total Coverage
Lines: 73 311 23.5%
Branches: 47 350 13.4%

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