MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 99.5% 200 / 0 / 201
Functions: 96.4% 27 / 0 / 28
Branches: 83.0% 240 / 0 / 289

service/NetworkLifecycleCoordinator.cpp
Line Branch Exec Source
1 #include "NetworkLifecycleCoordinator.h"
2
3 #include <QCoreApplication>
4 #include <QThread>
5 #include <QCryptographicHash>
6 #include <QDBusConnection>
7 #include <QDBusMessage>
8 #include <QDBusPendingCallWatcher>
9 #include <QDBusPendingReply>
10 #include <QDBusObjectPath>
11
12 #include <QHostAddress>
13 #include <QHostInfo>
14 #include <QLoggingCategory>
15 #include <QNetworkInterface>
16 #include <QVariant>
17
18 #include <utility>
19
20
4/7
✓ Branch 2 → 3 taken 40 times.
✓ Branch 2 → 8 taken 24 times.
✓ Branch 4 → 5 taken 40 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 40 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
64 Q_LOGGING_CATEGORY(lcLifecycle, "mailjd.lifecycle")
21
22 59 NetworkLifecycleCoordinator::NetworkLifecycleCoordinator(
23 59 QObject *parent, bool installNativeHooks)
24
3/3
✓ Branch 3 → 4 taken 59 times.
✓ Branch 4 → 5 taken 59 times.
✓ Branch 6 → 7 taken 59 times.
59 : QObject(parent) {
25
1/1
✓ Branch 12 → 13 taken 59 times.
59 m_coalesceTimer.setSingleShot(true);
26
1/1
✓ Branch 13 → 14 taken 59 times.
59 m_coalesceTimer.setInterval(500);
27 59 connect(&m_coalesceTimer, &QTimer::timeout, this,
28
1/1
✓ Branch 14 → 15 taken 59 times.
59 [this]() { m_coalescing = false; });
29
1/1
✓ Branch 16 → 17 taken 59 times.
59 m_identityTimer.setInterval(2000);
30 59 connect(&m_identityTimer, &QTimer::timeout, this,
31
1/1
✓ Branch 17 → 18 taken 59 times.
59 &NetworkLifecycleCoordinator::refreshNetworkIdentity);
32
2/2
✓ Branch 19 → 20 taken 39 times.
✓ Branch 19 → 26 taken 20 times.
59 if (installNativeHooks) {
33
1/1
✓ Branch 20 → 21 taken 39 times.
39 m_platformBus = QDBusConnection::systemBus();
34
1/1
✓ Branch 23 → 24 taken 39 times.
39 installNativeSleepHook();
35
1/1
✓ Branch 24 → 25 taken 39 times.
39 refreshNetworkIdentity();
36
1/1
✓ Branch 25 → 26 taken 39 times.
39 m_identityTimer.start();
37 }
38 59 }
39
40 5034 NetworkLifecycleCoordinator *NetworkLifecycleCoordinator::instance() {
41 static NetworkLifecycleCoordinator *coordinator =
42
6/12
✓ Branch 2 → 3 taken 39 times.
✓ Branch 2 → 11 taken 4995 times.
✓ Branch 4 → 5 taken 39 times.
✗ Branch 4 → 11 not taken.
✓ Branch 5 → 6 taken 39 times.
✓ Branch 7 → 8 taken 39 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 39 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
5034 new NetworkLifecycleCoordinator(QCoreApplication::instance());
43 5034 return coordinator;
44 }
45
46 39 void NetworkLifecycleCoordinator::installNativeSleepHook() {
47 #ifdef Q_OS_LINUX
48
3/3
✓ Branch 2 → 3 taken 39 times.
✓ Branch 3 → 4 taken 39 times.
✓ Branch 16 → 17 taken 39 times.
78 const bool connected = QDBusConnection::systemBus().connect(
49 78 QStringLiteral("org.freedesktop.login1"),
50 78 QStringLiteral("/org/freedesktop/login1"),
51 78 QStringLiteral("org.freedesktop.login1.Manager"),
52 78 QStringLiteral("PrepareForSleep"), this,
53 SLOT(prepareForSleep(bool)));
54
5/5
✓ Branch 26 → 27 taken 39 times.
✓ Branch 30 → 31 taken 39 times.
✓ Branch 31 → 32 taken 39 times.
✓ Branch 39 → 28 taken 39 times.
✓ Branch 39 → 40 taken 39 times.
78 qCInfo(lcLifecycle) << "systemd-logind PrepareForSleep hook"
55
2/3
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 39 times.
✓ Branch 35 → 36 taken 39 times.
39 << (connected ? "active" : "unavailable");
56 #endif
57 39 }
58
59 9 void NetworkLifecycleCoordinator::prepareForSleep(bool sleeping) {
60
2/2
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 4 taken 7 times.
9 if (sleeping == m_suspending)
61 2 return;
62 7 m_suspending = sleeping;
63 7 emit suspendingChanged(sleeping);
64
2/2
✓ Branch 5 → 6 taken 3 times.
✓ Branch 5 → 7 taken 4 times.
7 if (!sleeping)
65 3 requestRecovery(Trigger::NativeResume);
66 }
67
68 quint64
69 31 NetworkLifecycleCoordinator::requestRecovery(
70 NetworkLifecycleCoordinator::Trigger trigger) {
71 // From another thread the request is queued; 0 = not yet assigned.
72
3/3
✓ Branch 3 → 4 taken 31 times.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 30 times.
32 if (forwardToOwnThread([this, trigger] { requestRecovery(trigger); }))
73 1 return 0;
74
2/2
✓ Branch 7 → 8 taken 5 times.
✓ Branch 7 → 9 taken 25 times.
30 if (m_coalescing)
75 5 return m_generation;
76 25 m_coalescing = true;
77 25 ++m_generation;
78
7/7
✓ Branch 9 → 10 taken 25 times.
✓ Branch 13 → 14 taken 25 times.
✓ Branch 14 → 15 taken 25 times.
✓ Branch 15 → 16 taken 25 times.
✓ Branch 16 → 17 taken 25 times.
✓ Branch 21 → 11 taken 25 times.
✓ Branch 21 → 22 taken 25 times.
50 qCInfo(lcLifecycle) << "Recovery generation" << m_generation << "trigger"
79
1/1
✓ Branch 17 → 18 taken 25 times.
25 << static_cast<int>(trigger);
80 25 emit recoveryRequested(m_generation, trigger);
81 // F-60 (SPRINT-104): bound the coalescing window from the moment the
82 // request is emitted — including an unsuccessful recovery. A generation
83 // that never reaches stability (outage continues, no monitor
84 // acknowledges) must not stay open forever: suspend/resume during such
85 // a window suppressed the resume fan-out while the health monitor's
86 // timers were stopped, leaving IMAP with no scheduled retry at all.
87 // markStable restarts the same window for the late-failure case.
88 25 m_coalesceTimer.start(2000);
89 25 return m_generation;
90 }
91
92 1295 bool NetworkLifecycleCoordinator::forwardToOwnThread(std::function<void()> call) {
93
2/2
✓ Branch 4 → 5 taken 1257 times.
✓ Branch 4 → 6 taken 38 times.
1295 if (QThread::currentThread() == thread())
94 1257 return false;
95 38 QMetaObject::invokeMethod(this, std::move(call), Qt::QueuedConnection);
96 38 return true;
97 }
98
99 86 void NetworkLifecycleCoordinator::markStable(quint64 generation) {
100
3/3
✓ Branch 3 → 4 taken 86 times.
✓ Branch 5 → 6 taken 25 times.
✓ Branch 5 → 7 taken 61 times.
111 if (forwardToOwnThread([this, generation] { markStable(generation); }))
101 25 return;
102
2/2
✓ Branch 7 → 8 taken 50 times.
✓ Branch 7 → 9 taken 11 times.
61 if (generation == m_generation) {
103 50 m_stableGeneration = generation;
104 // Keep the generation open briefly so main/body/search/sync restoration
105 // and late socket errors caused by the same handover cannot create a
106 // second recovery wave.
107 50 m_coalesceTimer.start(2000);
108 }
109 }
110
111 24 bool NetworkLifecycleCoordinator::updateNetworkIdentity(
112 const NetworkIdentity &identity) {
113
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 23 times.
24 if (!identity.isValid())
114 1 return false;
115
2/2
✓ Branch 6 → 7 taken 11 times.
✓ Branch 6 → 9 taken 12 times.
23 if (!m_networkIdentity.isValid()) {
116 11 m_networkIdentity = identity;
117 11 return false;
118 }
119
120 const bool changed =
121 12 identity.interfaceName != m_networkIdentity.interfaceName ||
122
2/2
✓ Branch 12 → 13 taken 8 times.
✓ Branch 12 → 17 taken 3 times.
11 identity.activeAccessPointHash !=
123
2/2
✓ Branch 14 → 15 taken 7 times.
✓ Branch 14 → 17 taken 1 time.
19 m_networkIdentity.activeAccessPointHash ||
124
4/4
✓ Branch 10 → 11 taken 11 times.
✓ Branch 10 → 17 taken 1 time.
✓ Branch 16 → 17 taken 4 times.
✓ Branch 16 → 18 taken 3 times.
31 identity.sourceAddresses != m_networkIdentity.sourceAddresses ||
125 7 identity.endpointAddresses != m_networkIdentity.endpointAddresses;
126 12 m_networkIdentity = identity;
127
2/2
✓ Branch 20 → 21 taken 9 times.
✓ Branch 20 → 22 taken 3 times.
12 if (changed)
128 9 requestRecovery(Trigger::NetworkIdentity);
129 12 return changed;
130 }
131
132 115 void NetworkLifecycleCoordinator::observeEndpoint(const QString &host) {
133
4/4
✓ Branch 3 → 4 taken 115 times.
✓ Branch 4 → 5 taken 115 times.
✓ Branch 7 → 8 taken 12 times.
✓ Branch 7 → 9 taken 103 times.
127 if (forwardToOwnThread([this, host] { observeEndpoint(host); }))
134 100 return;
135
2/2
✓ Branch 9 → 10 taken 103 times.
✓ Branch 10 → 11 taken 103 times.
103 const QString normalized = host.trimmed().toLower();
136
6/6
✓ Branch 13 → 14 taken 97 times.
✓ Branch 13 → 16 taken 6 times.
✓ Branch 15 → 16 taken 82 times.
✓ Branch 15 → 17 taken 15 times.
✓ Branch 18 → 19 taken 88 times.
✓ Branch 18 → 20 taken 15 times.
103 if (normalized.isEmpty() || m_endpointHosts.contains(normalized))
137 88 return;
138
1/1
✓ Branch 20 → 21 taken 15 times.
15 m_endpointHosts.insert(normalized);
139
1/1
✓ Branch 21 → 22 taken 15 times.
15 refreshEndpointAddresses();
140 103 }
141
142 68 void NetworkLifecycleCoordinator::readNetworkManagerProperties(
143 const QString &path, const QString &interface,
144 std::function<void(QVariantMap)> callback) {
145 auto message = QDBusMessage::createMethodCall(
146 136 QStringLiteral("org.freedesktop.NetworkManager"), path,
147
1/1
✓ Branch 11 → 12 taken 68 times.
272 QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("GetAll"));
148
1/1
✓ Branch 19 → 20 taken 68 times.
68 message << interface;
149 // Construct messages directly: QDBusInterface can synchronously introspect.
150
4/7
✓ Branch 21 → 22 taken 68 times.
✓ Branch 22 → 23 taken 68 times.
✓ Branch 23 → 24 taken 68 times.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 68 times.
✗ Branch 58 → 59 not taken.
✗ Branch 58 → 60 not taken.
68 auto *watcher = new QDBusPendingCallWatcher(m_platformBus.asyncCall(message, 500), this);
151
1/1
✓ Branch 29 → 30 taken 68 times.
68 connect(watcher, &QDBusPendingCallWatcher::finished, this,
152 136 [watcher, callback = std::move(callback)] {
153
1/1
✓ Branch 2 → 3 taken 28 times.
28 const QDBusPendingReply<QVariantMap> reply = *watcher;
154
1/1
✓ Branch 3 → 4 taken 28 times.
28 watcher->deleteLater();
155
5/5
✓ Branch 4 → 5 taken 28 times.
✓ Branch 5 → 6 taken 3 times.
✓ Branch 5 → 7 taken 25 times.
✓ Branch 7 → 8 taken 25 times.
✓ Branch 8 → 9 taken 28 times.
28 callback(reply.isError() ? QVariantMap{} : reply.value());
156 28 });
157 68 }
158
159 51 void NetworkLifecycleCoordinator::readPlatformIdentity() {
160 #ifdef Q_OS_LINUX
161
1/1
✓ Branch 9 → 10 taken 51 times.
51 readNetworkManagerProperties(QStringLiteral("/org/freedesktop/NetworkManager"),
162 153 QStringLiteral("org.freedesktop.NetworkManager"), [this](const QVariantMap &root) {
163
2/2
✓ Branch 6 → 7 taken 11 times.
✓ Branch 7 → 8 taken 11 times.
22 const auto primary = qdbus_cast<QDBusObjectPath>(root.value(QStringLiteral("PrimaryConnection")));
164
9/10
✓ Branch 14 → 15 taken 8 times.
✓ Branch 14 → 19 taken 3 times.
✓ Branch 18 → 19 taken 1 time.
✓ Branch 18 → 20 taken 7 times.
✓ Branch 21 → 22 taken 8 times.
✓ Branch 21 → 23 taken 3 times.
✓ Branch 23 → 24 taken 11 times.
✗ Branch 23 → 25 not taken.
✓ Branch 25 → 26 taken 4 times.
✓ Branch 25 → 31 taken 7 times.
11 if (primary.path().isEmpty() || primary.path() == QLatin1String("/")) {
165 4 publishPlatformIdentity({});
166 4 return;
167 }
168
1/1
✓ Branch 36 → 37 taken 7 times.
7 readNetworkManagerProperties(primary.path(),
169 21 QStringLiteral("org.freedesktop.NetworkManager.Connection.Active"), [this](const QVariantMap &active) {
170
2/2
✓ Branch 6 → 7 taken 7 times.
✓ Branch 7 → 8 taken 7 times.
14 const auto devices = qdbus_cast<QList<QDBusObjectPath>>(active.value(QStringLiteral("Devices")));
171
2/2
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 19 taken 6 times.
7 if (devices.isEmpty()) {
172 1 publishPlatformIdentity({});
173 1 return;
174 }
175 6 const auto devicePath = devices.first().path();
176
2/2
✓ Branch 22 → 23 taken 6 times.
✓ Branch 26 → 27 taken 6 times.
12 readNetworkManagerProperties(devicePath,
177 18 QStringLiteral("org.freedesktop.NetworkManager.Device"), [this, devicePath](const QVariantMap &device) {
178 6 NetworkIdentity identity;
179
2/2
✓ Branch 7 → 8 taken 6 times.
✓ Branch 8 → 9 taken 6 times.
12 identity.interfaceName = device.value(QStringLiteral("Interface")).toString();
180 6 identity.backend = QStringLiteral("NetworkManager");
181
16/24
✓ Branch 22 → 23 taken 5 times.
✓ Branch 22 → 30 taken 1 time.
✓ Branch 27 → 28 taken 5 times.
✓ Branch 28 → 29 taken 5 times.
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 31 taken 4 times.
✓ Branch 32 → 33 taken 5 times.
✓ Branch 32 → 34 taken 1 time.
✓ Branch 34 → 35 taken 5 times.
✓ Branch 34 → 36 taken 1 time.
✓ Branch 36 → 37 taken 5 times.
✓ Branch 36 → 38 taken 1 time.
✓ Branch 38 → 39 taken 5 times.
✓ Branch 38 → 40 taken 1 time.
✓ Branch 40 → 41 taken 2 times.
✓ Branch 40 → 46 taken 4 times.
✗ Branch 77 → 78 not taken.
✗ Branch 77 → 79 not taken.
✗ Branch 81 → 82 not taken.
✗ Branch 81 → 83 not taken.
✗ Branch 85 → 86 not taken.
✗ Branch 85 → 87 not taken.
✗ Branch 89 → 90 not taken.
✗ Branch 89 → 91 not taken.
11 if (identity.interfaceName.isEmpty() || device.value(QStringLiteral("DeviceType")).toUInt() != 2) {
182
1/1
✓ Branch 43 → 44 taken 2 times.
2 publishPlatformIdentity(std::move(identity));
183 2 return;
184 }
185
2/2
✓ Branch 48 → 49 taken 4 times.
✓ Branch 52 → 53 taken 4 times.
8 readNetworkManagerProperties(devicePath,
186 8 QStringLiteral("org.freedesktop.NetworkManager.Device.Wireless"),
187 8 [this, identity = std::move(identity)](const QVariantMap &wireless) mutable {
188
2/2
✓ Branch 6 → 7 taken 4 times.
✓ Branch 7 → 8 taken 4 times.
8 const auto accessPoint = qdbus_cast<QDBusObjectPath>(wireless.value(QStringLiteral("ActiveAccessPoint")));
189
7/10
✓ Branch 14 → 15 taken 4 times.
✗ Branch 14 → 20 not taken.
✓ Branch 18 → 19 taken 3 times.
✓ Branch 18 → 20 taken 1 time.
✓ Branch 21 → 22 taken 4 times.
✗ Branch 21 → 23 not taken.
✓ Branch 23 → 24 taken 4 times.
✗ Branch 23 → 25 not taken.
✓ Branch 25 → 26 taken 3 times.
✓ Branch 25 → 35 taken 1 time.
4 if (!accessPoint.path().isEmpty() && accessPoint.path() != QLatin1String("/"))
190
2/2
✓ Branch 27 → 28 taken 3 times.
✓ Branch 29 → 30 taken 3 times.
3 identity.activeAccessPointHash = QCryptographicHash::hash(accessPoint.path().toUtf8(), QCryptographicHash::Sha256);
191
1/1
✓ Branch 37 → 38 taken 4 times.
4 publishPlatformIdentity(std::move(identity));
192 4 });
193
2/2
✓ Branch 59 → 60 taken 4 times.
✓ Branch 59 → 62 taken 2 times.
6 });
194
3/11
✓ Branch 16 → 17 taken 1 time.
✓ Branch 34 → 35 taken 6 times.
✓ Branch 34 → 37 taken 1 time.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 55 not taken.
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 58 not taken.
✗ Branch 59 → 60 not taken.
✗ Branch 59 → 61 not taken.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
8 });
195
3/11
✓ Branch 28 → 29 taken 4 times.
✓ Branch 43 → 44 taken 7 times.
✓ Branch 43 → 46 taken 4 times.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 67 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 68 → 70 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 73 not taken.
15 });
196 #else
197 publishPlatformIdentity({});
198 #endif
199 51 }
200
201 20 NetworkIdentity NetworkLifecycleCoordinator::completePlatformIdentity(NetworkIdentity identity) const {
202
2/2
✓ Branch 3 → 4 taken 15 times.
✓ Branch 3 → 32 taken 5 times.
20 if (identity.interfaceName.isEmpty()) {
203 // Portable bounded fallback: choose one active non-loopback interface.
204 // It cannot identify every default-route change, but avoids reconnecting
205 // on address churn from unrelated inactive interfaces.
206 15 for (const QNetworkInterface &candidate :
207
4/5
✓ Branch 4 → 5 taken 15 times.
✓ Branch 5 → 6 taken 15 times.
✓ Branch 6 → 7 taken 15 times.
✓ Branch 29 → 8 taken 30 times.
✗ Branch 29 → 30 not taken.
45 QNetworkInterface::allInterfaces()) {
208
1/1
✓ Branch 9 → 10 taken 30 times.
30 const auto flags = candidate.flags();
209
5/6
✓ Branch 11 → 12 taken 30 times.
✗ Branch 11 → 14 not taken.
✓ Branch 13 → 14 taken 15 times.
✓ Branch 13 → 15 taken 15 times.
✓ Branch 16 → 17 taken 15 times.
✓ Branch 16 → 18 taken 15 times.
60 if (!flags.testFlag(QNetworkInterface::IsUp) ||
210 30 flags.testFlag(QNetworkInterface::IsLoopBack))
211 15 continue;
212 15 identity.backend = QStringLiteral("QtNetworkInterface");
213
1/1
✓ Branch 24 → 25 taken 15 times.
15 identity.interfaceName = candidate.name();
214 15 break;
215 15 }
216 }
217
218
1/2
✓ Branch 33 → 34 taken 20 times.
✗ Branch 33 → 61 not taken.
20 if (!identity.interfaceName.isEmpty()) {
219 const QNetworkInterface active =
220
1/1
✓ Branch 34 → 35 taken 20 times.
20 QNetworkInterface::interfaceFromName(identity.interfaceName);
221
5/5
✓ Branch 35 → 36 taken 20 times.
✓ Branch 36 → 37 taken 20 times.
✓ Branch 37 → 38 taken 20 times.
✓ Branch 57 → 39 taken 15 times.
✓ Branch 57 → 58 taken 20 times.
35 for (const QNetworkAddressEntry &entry : active.addressEntries()) {
222
1/1
✓ Branch 40 → 41 taken 15 times.
15 const QHostAddress address = entry.ip();
223
5/8
✓ Branch 41 → 42 taken 15 times.
✓ Branch 42 → 43 taken 15 times.
✗ Branch 42 → 48 not taken.
✓ Branch 43 → 44 taken 15 times.
✓ Branch 44 → 45 taken 15 times.
✗ Branch 44 → 48 not taken.
✓ Branch 49 → 50 taken 15 times.
✗ Branch 49 → 54 not taken.
30 if (!address.isNull() && !address.isLoopback() &&
224
2/3
✓ Branch 45 → 46 taken 15 times.
✓ Branch 46 → 47 taken 15 times.
✗ Branch 46 → 48 not taken.
15 !address.isLinkLocal()) {
225
2/2
✓ Branch 50 → 51 taken 15 times.
✓ Branch 51 → 52 taken 15 times.
15 identity.sourceAddresses.insert(address.toString());
226 }
227 35 }
228 20 }
229 20 for (auto it = m_resolvedEndpoints.cbegin();
230
2/2
✓ Branch 67 → 62 taken 9 times.
✓ Branch 67 → 68 taken 20 times.
29 it != m_resolvedEndpoints.cend(); ++it) {
231
1/1
✓ Branch 63 → 64 taken 9 times.
9 identity.endpointAddresses.unite(it.value());
232 }
233 20 return identity;
234 }
235
236 1079 void NetworkLifecycleCoordinator::refreshEndpointAddresses() {
237
2/2
✓ Branch 16 → 5 taken 44 times.
✓ Branch 16 → 17 taken 1079 times.
1123 for (const QString &host : std::as_const(m_endpointHosts)) {
238
2/2
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 43 times.
44 if (m_endpointLookups.contains(host))
239 1 continue;
240
1/1
✓ Branch 9 → 10 taken 43 times.
43 m_endpointLookups.insert(host);
241
1/1
✓ Branch 11 → 12 taken 43 times.
43 QHostInfo::lookupHost(host, this, [this, host](const QHostInfo &info) {
242
1/1
✓ Branch 2 → 3 taken 40 times.
40 m_endpointLookups.remove(host);
243
3/3
✓ Branch 3 → 4 taken 40 times.
✓ Branch 4 → 5 taken 25 times.
✓ Branch 4 → 6 taken 15 times.
40 if (info.error() != QHostInfo::NoError)
244 25 return;
245 15 QSet<QString> addresses;
246
5/5
✓ Branch 7 → 8 taken 15 times.
✓ Branch 8 → 9 taken 15 times.
✓ Branch 9 → 10 taken 15 times.
✓ Branch 17 → 11 taken 15 times.
✓ Branch 17 → 18 taken 15 times.
30 for (const QHostAddress &address : info.addresses())
247
2/2
✓ Branch 12 → 13 taken 15 times.
✓ Branch 13 → 14 taken 15 times.
30 addresses.insert(address.toString());
248
1/1
✓ Branch 19 → 20 taken 15 times.
15 applyEndpointResolution(host, addresses);
249 15 });
250 }
251 1079 }
252
253 21 void NetworkLifecycleCoordinator::applyEndpointResolution(
254 const QString &host, const QSet<QString> &addresses) {
255 21 const bool hadResolution = m_resolvedEndpoints.contains(host);
256
8/8
✓ Branch 3 → 4 taken 10 times.
✓ Branch 3 → 8 taken 11 times.
✓ Branch 6 → 7 taken 8 times.
✓ Branch 6 → 8 taken 2 times.
✓ Branch 9 → 10 taken 10 times.
✓ Branch 9 → 11 taken 11 times.
✓ Branch 11 → 12 taken 8 times.
✓ Branch 11 → 13 taken 13 times.
21 if (hadResolution && addresses == m_resolvedEndpoints.value(host))
257 19 return;
258
259
1/1
✓ Branch 13 → 14 taken 13 times.
13 m_resolvedEndpoints.insert(host, addresses);
260 // DNS changes only the endpoint part of the current platform snapshot.
261 // It must not synchronously query NetworkManager for every resolved host.
262 13 NetworkIdentity identity = m_networkIdentity;
263 13 identity.endpointAddresses.clear();
264
3/3
✓ Branch 17 → 18 taken 9 times.
✓ Branch 17 → 25 taken 4 times.
✓ Branch 20 → 21 taken 9 times.
13 if (!identity.isValid()) identity = completePlatformIdentity(std::move(identity));
265
2/2
✓ Branch 32 → 28 taken 4 times.
✓ Branch 32 → 33 taken 4 times.
8 else for (const auto &resolved : std::as_const(m_resolvedEndpoints))
266
1/1
✓ Branch 29 → 30 taken 4 times.
4 identity.endpointAddresses.unite(resolved);
267
2/2
✓ Branch 34 → 35 taken 11 times.
✓ Branch 34 → 40 taken 2 times.
13 if (!hadResolution) {
268 // The first DNS answer establishes the endpoint baseline. Treating the
269 // empty-to-resolved transition as a handover tears down the connection
270 // that initiated this lookup during every application start.
271
2/2
✓ Branch 36 → 37 taken 9 times.
✓ Branch 36 → 38 taken 2 times.
11 if (!m_networkIdentity.isValid())
272 9 m_networkIdentity = identity;
273 else
274 2 m_networkIdentity.endpointAddresses = identity.endpointAddresses;
275 11 return;
276 }
277
1/1
✓ Branch 40 → 41 taken 2 times.
2 updateNetworkIdentity(identity);
278 13 }
279
280 1063 void NetworkLifecycleCoordinator::refreshNetworkIdentity() {
281
2/3
✓ Branch 3 → 4 taken 1063 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1063 times.
1063 if (forwardToOwnThread([this] { refreshNetworkIdentity(); }))
282 ✗ return;
283 1063 refreshEndpointAddresses();
284
2/2
✓ Branch 8 → 9 taken 1012 times.
✓ Branch 8 → 10 taken 51 times.
1063 if (m_identityReading) {
285 1012 m_identityRefreshPending = true;
286 1012 return;
287 }
288 51 m_identityReading = true;
289 51 readPlatformIdentity();
290 }
291
292 11 void NetworkLifecycleCoordinator::publishPlatformIdentity(NetworkIdentity identity) {
293 11 m_identityReading = false;
294
2/2
✓ Branch 4 → 5 taken 11 times.
✓ Branch 5 → 6 taken 11 times.
11 updateNetworkIdentity(completePlatformIdentity(std::move(identity)));
295
2/2
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 12 taken 10 times.
11 if (std::exchange(m_identityRefreshPending, false))
296
1/1
✓ Branch 10 → 11 taken 1 time.
1 QTimer::singleShot(0, this, &NetworkLifecycleCoordinator::refreshNetworkIdentity);
297 11 }
298