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: 98.9% 185 / 0 / 187
Functions: 100.0% 15 / 0 / 15
Branches: 78.9% 258 / 0 / 327

app/core/AccountSession.cpp
Line Branch Exec Source
1 #include "AccountSession.h"
2 #include "MailSessionCore.h"
3 #include "controller/MailController.h"
4 #include "controller/UndoManager.h"
5 #include "data/CredentialStore.h"
6 #include "performance/PerformanceTrace.h"
7 #include "performance/StartupPerformanceTracker.h"
8 #include "service/ConnectionHealthMonitor.h"
9 #include "service/ImapService.h"
10 #include "ui/MailListModel.h"
11 #include "ui/MailThreadModel.h"
12 #include <QLoggingCategory>
13 #include <memory>
14 #include <algorithm>
15 #include <QFile>
16 #include <QFileInfo>
17 #include <QDir>
18 #include <QScopeGuard>
19 #include <QSaveFile>
20
21
4/7
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 8 taken 19 times.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 3 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
22 Q_LOGGING_CATEGORY(lcAccountSession, "mailjd.accountsession")
22
23 854 AccountSession::AccountSession(MailSessionCore &session, QObject *parent)
24 854 : QObject(parent), m_session(session) {}
25
26 1708 AccountSession::~AccountSession() {
27
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 853 times.
854 delete m_credentials.data();
28 1708 }
29
30 841 void AccountSession::load(const QString &accountsDirectory, bool connectToServer) {
31
4/4
✓ Branch 3 → 4 taken 25 times.
✓ Branch 3 → 8 taken 816 times.
✓ Branch 5 → 6 taken 24 times.
✓ Branch 5 → 8 taken 1 time.
841 QString selectedId = m_accountsDirectory == accountsDirectory && m_primary ? m_primary->id : QString{};
32
6/6
✓ Branch 10 → 11 taken 817 times.
✓ Branch 10 → 14 taken 24 times.
✓ Branch 12 → 13 taken 810 times.
✓ Branch 12 → 14 taken 7 times.
✓ Branch 15 → 16 taken 810 times.
✓ Branch 15 → 46 taken 31 times.
841 if (selectedId.isEmpty() && !accountsDirectory.isEmpty()) {
33
4/4
✓ Branch 16 → 17 taken 810 times.
✓ Branch 17 → 18 taken 810 times.
✓ Branch 18 → 19 taken 810 times.
✓ Branch 19 → 20 taken 810 times.
810 QFile choice(QDir(accountsDirectory).filePath("account-selection"));
34
11/16
✓ Branch 23 → 24 taken 810 times.
✓ Branch 24 → 25 taken 810 times.
✓ Branch 25 → 26 taken 810 times.
✗ Branch 25 → 32 not taken.
✓ Branch 26 → 27 taken 810 times.
✓ Branch 27 → 28 taken 810 times.
✗ Branch 27 → 32 not taken.
✓ Branch 29 → 30 taken 810 times.
✓ Branch 30 → 31 taken 4 times.
✓ Branch 30 → 32 taken 806 times.
✓ Branch 33 → 34 taken 810 times.
✗ Branch 33 → 35 not taken.
✓ Branch 35 → 36 taken 4 times.
✓ Branch 35 → 44 taken 806 times.
✗ Branch 79 → 80 not taken.
✗ Branch 79 → 81 not taken.
810 if (!QFileInfo(choice).isSymLink() && choice.size() <= 512 && choice.open(QIODevice::ReadOnly))
35
3/3
✓ Branch 36 → 37 taken 4 times.
✓ Branch 37 → 38 taken 4 times.
✓ Branch 38 → 39 taken 4 times.
4 selectedId = QString::fromUtf8(choice.readAll()).trimmed();
36 810 }
37 841 m_accountsDirectory = accountsDirectory;
38
1/1
✓ Branch 47 → 48 taken 841 times.
841 auto &tracker = MailJD::Performance::StartupPerformanceTracker::instance();
39
1/1
✓ Branch 48 → 49 taken 841 times.
841 const qint64 parseStart = MailJD::Performance::PerformanceTrace::monotonicNowNs();
40
3/3
✓ Branch 49 → 50 taken 841 times.
✓ Branch 51 → 52 taken 7 times.
✓ Branch 51 → 53 taken 834 times.
1682 auto accounts = accountsDirectory.trimmed().isEmpty() ? std::vector<AccountConfig>{}
41
1/1
✓ Branch 53 → 54 taken 834 times.
841 : AccountConfigLoader::loadAll(accountsDirectory, AccountConfigLoader::LoadMode::MetadataOnly);
42
2/2
✓ Branch 55 → 56 taken 841 times.
✓ Branch 59 → 60 taken 841 times.
1682 tracker.recordPhase(QStringLiteral("account_parse"), parseStart,
43 MailJD::Performance::PerformanceTrace::monotonicNowNs());
44
1/1
✓ Branch 64 → 65 taken 841 times.
841 activate(std::move(accounts), selectedId, connectToServer);
45 841 }
46
47 9 void AccountSession::configurationSaved(const QString &id) {
48
1/1
✓ Branch 2 → 3 taken 9 times.
9 auto accounts = AccountConfigLoader::loadAll(m_accountsDirectory, AccountConfigLoader::LoadMode::MetadataOnly);
49
6/6
✓ Branch 4 → 5 taken 6 times.
✓ Branch 4 → 8 taken 3 times.
✓ Branch 7 → 8 taken 3 times.
✓ Branch 7 → 9 taken 3 times.
✓ Branch 10 → 11 taken 6 times.
✓ Branch 10 → 17 taken 3 times.
9 if (!m_primary || m_primary->id == id) {
50
1/1
✓ Branch 13 → 14 taken 6 times.
6 activate(std::move(accounts), id, m_networkEnabled);
51
1/1
✓ Branch 15 → 16 taken 6 times.
6 emit configurationChanged(id);
52 6 return;
53 }
54 // Editing another account only updates selector metadata. The active
55 // transport, credential generation, selection and undo history stay live.
56
1/1
✓ Branch 17 → 18 taken 3 times.
3 updateConfigurationMetadata(accounts);
57
1/1
✓ Branch 18 → 19 taken 3 times.
3 m_availableAccounts.clear();
58
2/2
✓ Branch 35 → 21 taken 6 times.
✓ Branch 35 → 36 taken 3 times.
9 for (const auto &account : accounts)
59
3/5
✓ Branch 27 → 28 taken 6 times.
✓ Branch 31 → 32 taken 18 times.
✓ Branch 31 → 33 taken 6 times.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 55 not taken.
24 m_availableAccounts.append(QVariantMap{{"id", account.id}, {"name", account.name}, {"email", account.email}});
60
1/1
✓ Branch 36 → 37 taken 3 times.
3 emit availableAccountsChanged();
61
1/1
✓ Branch 37 → 38 taken 3 times.
3 emit configurationChanged(id);
62 15 }
63
64 11 void AccountSession::configurationRemoved(const QString &id) {
65 // Announced once the session reflects the removal, on every path below.
66 22 const auto announce = qScopeGuard([this, id] { emit accountRemoved(id); });
67
1/1
✓ Branch 5 → 6 taken 11 times.
11 auto accounts = AccountConfigLoader::loadAll(m_accountsDirectory, AccountConfigLoader::LoadMode::MetadataOnly);
68
2/2
✓ Branch 10 → 11 taken 11 times.
✓ Branch 12 → 13 taken 11 times.
11 accounts.erase(std::remove_if(accounts.begin(), accounts.end(),
69 9 [&id](const auto &account) { return account.id == id; }),
70 11 accounts.end());
71
5/6
✓ Branch 14 → 15 taken 11 times.
✗ Branch 14 → 19 not taken.
✓ Branch 17 → 18 taken 6 times.
✓ Branch 17 → 19 taken 5 times.
✓ Branch 20 → 21 taken 6 times.
✓ Branch 20 → 91 taken 5 times.
11 if (m_primary && m_primary->id == id) {
72 // Deleting the active account switches to the first remaining one, or
73 // to the unconfigured state. The choice persists exactly like a manual
74 // switch so a restart lands on the same account.
75 6 QString next;
76
2/2
✓ Branch 22 → 23 taken 3 times.
✓ Branch 22 → 25 taken 3 times.
6 if (!accounts.empty()) next = accounts.front().id;
77
3/3
✓ Branch 25 → 26 taken 6 times.
✓ Branch 26 → 27 taken 6 times.
✓ Branch 27 → 28 taken 6 times.
6 const auto path = QDir(m_accountsDirectory).filePath("account-selection");
78
2/2
✓ Branch 31 → 32 taken 3 times.
✓ Branch 31 → 40 taken 3 times.
6 if (next.isEmpty()) {
79
1/1
✓ Branch 32 → 33 taken 3 times.
3 QFile::remove(path);
80
1/1
✓ Branch 36 → 37 taken 3 times.
3 activate(std::move(accounts), {}, m_networkEnabled);
81 3 return;
82 }
83 // S106-190: the removed account never stays active — when the choice
84 // cannot be persisted the switch still happens (logged); a restart then
85 // falls back to the first account as well.
86 3 bool persisted = false;
87
11/17
✓ Branch 40 → 41 taken 3 times.
✓ Branch 41 → 42 taken 3 times.
✓ Branch 42 → 43 taken 2 times.
✓ Branch 42 → 47 taken 1 time.
✓ Branch 43 → 44 taken 2 times.
✓ Branch 45 → 46 taken 2 times.
✗ Branch 45 → 47 not taken.
✓ Branch 48 → 49 taken 2 times.
✓ Branch 48 → 50 taken 1 time.
✓ Branch 50 → 51 taken 3 times.
✗ Branch 50 → 52 not taken.
✓ Branch 52 → 53 taken 2 times.
✓ Branch 52 → 71 taken 1 time.
✗ Branch 138 → 139 not taken.
✗ Branch 138 → 140 not taken.
✗ Branch 142 → 143 not taken.
✗ Branch 142 → 144 not taken.
3 if (!QFileInfo(path).isSymLink() && next.toUtf8().size() <= 512) {
88
1/1
✓ Branch 53 → 54 taken 2 times.
2 QSaveFile choice(path);
89
1/1
✓ Branch 54 → 55 taken 2 times.
2 const auto bytes = next.toUtf8();
90
1/1
✓ Branch 56 → 57 taken 2 times.
2 persisted = choice.open(QIODevice::WriteOnly) &&
91
3/5
✓ Branch 59 → 60 taken 2 times.
✓ Branch 60 → 61 taken 2 times.
✗ Branch 60 → 67 not taken.
✓ Branch 63 → 64 taken 2 times.
✗ Branch 63 → 67 not taken.
4 choice.setPermissions(QFile::ReadOwner | QFile::WriteOwner) &&
92
4/6
✓ Branch 57 → 58 taken 2 times.
✗ Branch 57 → 67 not taken.
✓ Branch 61 → 62 taken 2 times.
✓ Branch 64 → 65 taken 2 times.
✓ Branch 65 → 66 taken 2 times.
✗ Branch 65 → 67 not taken.
6 choice.write(bytes) == bytes.size() && choice.commit();
93 2 }
94
2/2
✓ Branch 71 → 72 taken 1 time.
✓ Branch 71 → 83 taken 2 times.
3 if (!persisted)
95
5/5
✓ Branch 72 → 73 taken 1 time.
✓ Branch 76 → 77 taken 1 time.
✓ Branch 77 → 78 taken 1 time.
✓ Branch 81 → 74 taken 1 time.
✓ Branch 81 → 82 taken 1 time.
2 qCWarning(lcAccountSession) << "Account selection after a removal could not be saved";
96
1/1
✓ Branch 85 → 86 taken 3 times.
3 activate(std::move(accounts), next, m_networkEnabled);
97 3 return;
98 6 }
99
1/1
✓ Branch 91 → 92 taken 5 times.
5 updateConfigurationMetadata(accounts);
100
1/1
✓ Branch 92 → 93 taken 5 times.
5 m_availableAccounts.clear();
101
2/2
✓ Branch 109 → 95 taken 6 times.
✓ Branch 109 → 110 taken 5 times.
11 for (const auto &account : accounts)
102
3/5
✓ Branch 101 → 102 taken 6 times.
✓ Branch 105 → 106 taken 18 times.
✓ Branch 105 → 107 taken 6 times.
✗ Branch 170 → 171 not taken.
✗ Branch 170 → 172 not taken.
24 m_availableAccounts.append(QVariantMap{{"id", account.id}, {"name", account.name}, {"email", account.email}});
103
1/1
✓ Branch 110 → 111 taken 5 times.
5 emit availableAccountsChanged();
104 23 }
105
106 16 bool AccountSession::selectAccount(const QString &id) {
107
3/6
✓ Branch 3 → 4 taken 16 times.
✗ Branch 3 → 6 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 16 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 16 times.
16 if (id.isEmpty() || m_accountsDirectory.isEmpty()) return false;
108
5/6
✓ Branch 11 → 12 taken 16 times.
✗ Branch 11 → 16 not taken.
✓ Branch 14 → 15 taken 2 times.
✓ Branch 14 → 16 taken 14 times.
✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 19 taken 14 times.
16 if (m_primary && m_primary->id == id) return true;
109
1/1
✓ Branch 19 → 20 taken 14 times.
14 auto accounts = AccountConfigLoader::loadAll(m_accountsDirectory, AccountConfigLoader::LoadMode::MetadataOnly);
110 // Validate before disconnecting or clearing the current account's state.
111
3/3
✓ Branch 22 → 23 taken 14 times.
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 25 taken 13 times.
38 if (std::none_of(accounts.begin(), accounts.end(), [&id](const auto &account) { return account.id == id; }))
112 1 return false;
113
3/3
✓ Branch 25 → 26 taken 13 times.
✓ Branch 26 → 27 taken 13 times.
✓ Branch 27 → 28 taken 13 times.
13 const auto path = QDir(m_accountsDirectory).filePath("account-selection");
114
8/17
✓ Branch 30 → 31 taken 13 times.
✓ Branch 31 → 32 taken 13 times.
✓ Branch 32 → 33 taken 13 times.
✗ Branch 32 → 36 not taken.
✓ Branch 33 → 34 taken 13 times.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 13 times.
✓ Branch 38 → 39 taken 13 times.
✗ Branch 38 → 40 not taken.
✓ Branch 40 → 41 taken 13 times.
✗ Branch 40 → 42 not taken.
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 44 taken 13 times.
✗ Branch 91 → 92 not taken.
✗ Branch 91 → 93 not taken.
✗ Branch 95 → 96 not taken.
✗ Branch 95 → 97 not taken.
13 if (QFileInfo(path).isSymLink() || id.toUtf8().size() > 512) return false;
115
1/1
✓ Branch 44 → 45 taken 13 times.
13 QSaveFile choice(path);
116
1/1
✓ Branch 45 → 46 taken 13 times.
13 const auto bytes = id.toUtf8();
117
4/6
✓ Branch 47 → 48 taken 13 times.
✓ Branch 50 → 51 taken 12 times.
✓ Branch 51 → 52 taken 12 times.
✗ Branch 51 → 57 not taken.
✓ Branch 54 → 55 taken 12 times.
✗ Branch 54 → 57 not taken.
37 if (!choice.open(QIODevice::WriteOnly) || !choice.setPermissions(QFile::ReadOwner | QFile::WriteOwner) ||
118
7/8
✓ Branch 48 → 49 taken 12 times.
✓ Branch 48 → 57 taken 1 time.
✓ Branch 52 → 53 taken 12 times.
✓ Branch 55 → 56 taken 12 times.
✗ Branch 56 → 57 not taken.
✓ Branch 56 → 58 taken 12 times.
✓ Branch 59 → 60 taken 1 time.
✓ Branch 59 → 61 taken 12 times.
37 choice.write(bytes) != bytes.size() || !choice.commit()) return false;
119
1/1
✓ Branch 61 → 62 taken 12 times.
12 QPointer<AccountSession> guard(this);
120
1/1
✓ Branch 64 → 65 taken 12 times.
12 activate(std::move(accounts), id, m_networkEnabled);
121
3/6
✓ Branch 67 → 68 taken 12 times.
✗ Branch 67 → 76 not taken.
✓ Branch 70 → 71 taken 12 times.
✗ Branch 70 → 76 not taken.
✓ Branch 74 → 75 taken 12 times.
✗ Branch 74 → 76 not taken.
12 return guard && guard->m_primary && guard->m_primary->id == id;
122 14 }
123
124 865 void AccountSession::activate(std::vector<AccountConfig> loadedAccounts,
125 const QString &selectedId, bool connectToServer) {
126 865 const quint64 generation = ++m_generation;
127 865 m_networkEnabled = connectToServer;
128 865 m_resolvingCredentials = false;
129
2/2
✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 5 taken 859 times.
865 delete m_credentials.data();
130
1/1
✓ Branch 5 → 6 taken 865 times.
865 m_credentials = nullptr;
131
1/1
✓ Branch 7 → 8 taken 865 times.
865 m_session.health()->setActive(false);
132
1/1
✓ Branch 9 → 10 taken 865 times.
865 m_session.controller()->setNetworkEnabled(false);
133 // Undo closures capture the old controller/account context. A configuration
134 // reload invalidates that transient history, not durable journal intents.
135
1/1
✓ Branch 11 → 12 taken 865 times.
865 m_session.undo()->clear();
136
1/1
✓ Branch 13 → 14 taken 865 times.
865 m_session.imap()->disconnect(ImapService::DisconnectReason::AccountReload);
137
1/1
✓ Branch 15 → 16 taken 865 times.
865 m_session.controller()->clearDisplaySelection();
138
139
1/1
✓ Branch 16 → 17 taken 865 times.
865 auto &tracker = MailJD::Performance::StartupPerformanceTracker::instance();
140
1/1
✓ Branch 17 → 18 taken 865 times.
865 updateConfigurationMetadata(loadedAccounts);
141
1/1
✓ Branch 18 → 19 taken 865 times.
865 m_availableAccounts.clear();
142
2/2
✓ Branch 35 → 21 taken 887 times.
✓ Branch 35 → 36 taken 865 times.
1752 for (const auto &account : loadedAccounts)
143
3/5
✓ Branch 27 → 28 taken 887 times.
✓ Branch 31 → 32 taken 2661 times.
✓ Branch 31 → 33 taken 887 times.
✗ Branch 132 → 133 not taken.
✗ Branch 132 → 134 not taken.
3548 m_availableAccounts.append(QVariantMap{{"id", account.id}, {"name", account.name}, {"email", account.email}});
144
1/1
✓ Branch 38 → 39 taken 865 times.
865 const auto selected = std::find_if(loadedAccounts.begin(), loadedAccounts.end(),
145 877 [&selectedId](const auto &account) { return account.id == selectedId; });
146 // Resolve credentials only for the selected transport. Metadata for the
147 // selector contains no passwords and does not depend on keyring completion.
148
1/1
✓ Branch 39 → 40 taken 865 times.
865 auto accounts = std::make_shared<std::vector<AccountConfig>>();
149
5/5
✓ Branch 41 → 42 taken 851 times.
✓ Branch 41 → 52 taken 14 times.
✓ Branch 45 → 46 taken 802 times.
✓ Branch 45 → 48 taken 49 times.
✓ Branch 50 → 51 taken 851 times.
1716 if (!loadedAccounts.empty()) accounts->push_back(selected == loadedAccounts.end()
150 851 ? std::move(loadedAccounts.front()) : std::move(*selected));
151
2/2
✓ Branch 53 → 54 taken 53 times.
✓ Branch 53 → 56 taken 812 times.
865 const QString previousAccount = m_primary ? m_primary->name : QString{};
152
2/2
✓ Branch 59 → 60 taken 14 times.
✓ Branch 59 → 61 taken 851 times.
2581 m_primary = accounts->empty() ? std::nullopt
153 1716 : std::optional<AccountConfig>(accounts->front());
154
2/2
✓ Branch 67 → 68 taken 851 times.
✓ Branch 67 → 70 taken 14 times.
865 const QString accountName = m_primary ? m_primary->name : QString{};
155
2/2
✓ Branch 72 → 73 taken 833 times.
✓ Branch 72 → 77 taken 32 times.
865 if (previousAccount != accountName) {
156
1/1
✓ Branch 74 → 75 taken 833 times.
833 m_session.headers()->clear();
157
1/1
✓ Branch 76 → 77 taken 833 times.
833 m_session.threads()->clear();
158 }
159
1/1
✓ Branch 78 → 79 taken 865 times.
865 m_session.controller()->setAccount(accountName);
160
2/2
✓ Branch 80 → 81 taken 851 times.
✓ Branch 80 → 82 taken 14 times.
865 if (m_primary)
161
1/1
✓ Branch 81 → 83 taken 851 times.
851 tracker.markConfiguredAccount();
162 else
163
1/1
✓ Branch 82 → 83 taken 14 times.
14 tracker.markNoAccount();
164
165 // A local-restoration subscriber may synchronously replace the account or
166 // destroy its owner. Neither case may start the older credential request.
167
1/1
✓ Branch 83 → 84 taken 865 times.
865 QPointer<AccountSession> guard(this);
168
1/1
✓ Branch 84 → 85 taken 865 times.
865 emit accountsLoaded(generation);
169
9/10
✓ Branch 86 → 87 taken 865 times.
✗ Branch 86 → 91 not taken.
✓ Branch 87 → 88 taken 864 times.
✓ Branch 87 → 91 taken 1 time.
✓ Branch 89 → 90 taken 850 times.
✓ Branch 89 → 91 taken 14 times.
✓ Branch 90 → 91 taken 830 times.
✓ Branch 90 → 92 taken 20 times.
✓ Branch 93 → 94 taken 845 times.
✓ Branch 93 → 95 taken 20 times.
865 if (!guard || generation != m_generation || !m_primary || !m_networkEnabled)
170 845 return;
171
172 20 m_resolvingCredentials = true;
173
3/6
✓ Branch 95 → 96 taken 20 times.
✓ Branch 96 → 97 taken 20 times.
✗ Branch 97 → 98 not taken.
✓ Branch 97 → 99 taken 20 times.
✗ Branch 145 → 146 not taken.
✗ Branch 145 → 147 not taken.
20 auto *credentials = new CredentialStore(this);
174
1/1
✓ Branch 99 → 100 taken 20 times.
20 m_credentials = credentials;
175
1/1
✓ Branch 100 → 101 taken 20 times.
20 const qint64 keyringStart = MailJD::Performance::PerformanceTrace::monotonicNowNs();
176
1/1
✓ Branch 105 → 106 taken 20 times.
20 AccountConfigLoader::resolvePasswords(*accounts, credentials,
177
1/5
✓ Branch 103 → 104 taken 20 times.
✗ Branch 152 → 153 not taken.
✗ Branch 152 → 154 not taken.
✗ Branch 155 → 156 not taken.
✗ Branch 155 → 157 not taken.
40 [guard, accounts, credentials, generation, keyringStart] {
178
1/1
✓ Branch 2 → 3 taken 13 times.
13 credentials->deleteLater();
179
3/6
✓ Branch 4 → 5 taken 13 times.
✗ Branch 4 → 7 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 13 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 13 times.
13 if (!guard || generation != guard->m_generation)
180 ✗ return;
181
1/1
✓ Branch 12 → 13 taken 13 times.
13 guard->m_credentials = nullptr;
182 13 guard->m_resolvingCredentials = false;
183
2/3
✓ Branch 17 → 18 taken 13 times.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 30 taken 13 times.
13 if (!AccountConfigLoader::finishCredentialMigration(accounts->front(), guard))
184 ✗ qCWarning(lcAccountSession) << "Legacy credential migration retained its source for a later retry";
185 13 guard->m_primary = accounts->front();
186 13 const auto account = *guard->m_primary;
187
1/1
✓ Branch 37 → 38 taken 13 times.
13 auto &callbackTracker = MailJD::Performance::StartupPerformanceTracker::instance();
188
2/2
✓ Branch 38 → 39 taken 13 times.
✓ Branch 42 → 43 taken 13 times.
26 callbackTracker.recordPhase(QStringLiteral("keyring_callback"), keyringStart,
189 MailJD::Performance::PerformanceTrace::monotonicNowNs());
190
2/2
✓ Branch 46 → 47 taken 2 times.
✓ Branch 46 → 59 taken 11 times.
13 if (account.imap.password.isEmpty())
191
5/5
✓ Branch 47 → 48 taken 2 times.
✓ Branch 51 → 52 taken 2 times.
✓ Branch 52 → 53 taken 2 times.
✓ Branch 57 → 49 taken 2 times.
✓ Branch 57 → 58 taken 2 times.
4 qCWarning(lcAccountSession) << "IMAP password unavailable after keyring resolution for"
192
1/1
✓ Branch 53 → 54 taken 2 times.
2 << account.name;
193
1/1
✓ Branch 61 → 62 taken 13 times.
13 guard->m_session.controller()->setImapConfig(account.imap);
194
1/1
✓ Branch 64 → 65 taken 13 times.
13 guard->m_session.health()->setReconnectConfig(account.imap);
195
1/1
✓ Branch 67 → 68 taken 13 times.
13 guard->m_session.health()->setActive(true);
196
1/1
✓ Branch 68 → 69 taken 13 times.
13 callbackTracker.markAuthenticationStarted();
197
1/1
✓ Branch 71 → 72 taken 13 times.
13 guard->m_session.imap()->connectToServer(account.imap);
198
3/6
✓ Branch 73 → 74 taken 13 times.
✗ Branch 73 → 77 not taken.
✓ Branch 75 → 76 taken 13 times.
✗ Branch 75 → 77 not taken.
✓ Branch 78 → 79 taken 13 times.
✗ Branch 78 → 82 not taken.
13 if (guard && generation == guard->m_generation)
199
1/1
✓ Branch 81 → 82 taken 13 times.
13 guard->m_session.controller()->setNetworkEnabled(true);
200
3/6
✓ Branch 83 → 84 taken 13 times.
✗ Branch 83 → 87 not taken.
✓ Branch 85 → 86 taken 13 times.
✗ Branch 85 → 87 not taken.
✓ Branch 88 → 89 taken 13 times.
✗ Branch 88 → 91 not taken.
13 if (guard && generation == guard->m_generation)
201
1/1
✓ Branch 90 → 91 taken 13 times.
13 emit guard->credentialsResolved(generation);
202 13 });
203 4287 }
204
205 873 void AccountSession::updateConfigurationMetadata(const std::vector<AccountConfig> &accounts) {
206 873 m_configurations = accounts;
207
2/2
✓ Branch 13 → 5 taken 899 times.
✓ Branch 13 → 14 taken 873 times.
1772 for (auto &account : m_configurations) {
208
1/1
✓ Branch 6 → 7 taken 899 times.
899 account.imap.password.fill('\0');
209
1/1
✓ Branch 7 → 8 taken 899 times.
899 account.imap.password.clear();
210
1/1
✓ Branch 8 → 9 taken 899 times.
899 account.smtp.password.fill('\0');
211
1/1
✓ Branch 9 → 10 taken 899 times.
899 account.smtp.password.clear();
212 899 account.deferredCredentialMigration.reset();
213 }
214 873 m_configurationMetadata.clear();
215
2/2
✓ Branch 136 → 17 taken 899 times.
✓ Branch 136 → 137 taken 873 times.
1772 for (const auto &account : accounts) {
216
1/1
✓ Branch 92 → 93 taken 899 times.
899 m_configurationMetadata.append(QVariantMap{
217 1798 {QStringLiteral("id"), account.id},
218 1798 {QStringLiteral("name"), account.name},
219 1798 {QStringLiteral("email"), account.email},
220 1798 {QStringLiteral("imapHost"), account.imap.host},
221 1798 {QStringLiteral("imapPort"), account.imap.port},
222 1798 {QStringLiteral("imapSecurity"), account.imap.security},
223 1798 {QStringLiteral("imapUsername"), account.imap.username},
224 1798 {QStringLiteral("smtpHost"), account.smtp.host},
225 1798 {QStringLiteral("smtpPort"), account.smtp.port},
226 1798 {QStringLiteral("smtpSecurity"), account.smtp.security},
227 1798 {QStringLiteral("smtpUsername"), account.smtp.username},
228 1798 {QStringLiteral("smtpLogin"), account.smtp.login},
229 1798 {QStringLiteral("sieveMode"), account.sieve.mode},
230 1798 {QStringLiteral("sieveHost"), account.sieve.host},
231 1798 {QStringLiteral("sievePort"), account.sieve.port},
232 1798 {QStringLiteral("sieveSecurity"), account.sieve.security},
233 1798 {QStringLiteral("sieveLogin"), account.sieve.login},
234 1798 {QStringLiteral("sieveUsername"), account.sieve.username},
235
2/4
✓ Branch 96 → 97 taken 16182 times.
✓ Branch 96 → 98 taken 899 times.
✗ Branch 144 → 145 not taken.
✗ Branch 144 → 146 not taken.
17980 });
236 }
237 17954 }
238
239 1741 std::optional<AccountConfig> AccountSession::configuration(const QString &id) const {
240
2/2
✓ Branch 10 → 4 taken 946 times.
✓ Branch 10 → 11 taken 1578 times.
2524 for (const auto &account : m_configurations)
241
2/2
✓ Branch 6 → 7 taken 163 times.
✓ Branch 6 → 8 taken 783 times.
946 if (account.id == id) return account;
242 1578 return std::nullopt;
243 }
244