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: 93.6% 160 / 0 / 171
Functions: 100.0% 15 / 0 / 15
Branches: 68.3% 300 / 0 / 439

presentation/QuickMailBackendPredictions.cpp
Line Branch Exec Source
1 // Folder suggestions of QuickMailBackend (visible prediction, reason,
2 // demand batching on the prediction worker), split from QuickMailBackend.cpp
3 // for its line limit (Sprint 105 S105.3.8).
4 #include "QuickMailBackend.h"
5
6 #include "WorkspaceState.h"
7 #include "app/core/AccountSession.h"
8 #include "app/core/FolderPredictionSession.h"
9 #include "app/core/MailSessionCore.h"
10 #include "controller/UndoManager.h"
11 #include "data/MailCache.h"
12 #include "service/FolderPredictionService.h"
13 #include "service/ImapResponseParser.h"
14
15 #include <QElapsedTimer>
16 #include <QLoggingCategory>
17 #include <QPointer>
18
19 Q_DECLARE_LOGGING_CATEGORY(lcQuickBackend)
20
21 445866 FolderSuggestion::Candidate QuickMailBackend::visiblePrediction(const MailId &id) const {
22
8/13
✓ Branch 6 → 7 taken 187721 times.
✓ Branch 6 → 19 taken 249593 times.
✓ Branch 7 → 8 taken 187721 times.
✓ Branch 9 → 10 taken 170385 times.
✓ Branch 9 → 19 taken 17336 times.
✓ Branch 23 → 24 taken 445866 times.
✗ Branch 23 → 25 not taken.
✓ Branch 25 → 26 taken 436604 times.
✓ Branch 25 → 27 taken 9262 times.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 62 not taken.
✗ Branch 64 → 65 not taken.
✗ Branch 64 → 66 not taken.
883180 if (m_workspace.area() != "mail" || m_workspace.activeTab() != 0 || id.accountId != accountName() ||
23
8/8
✓ Branch 4 → 5 taken 437314 times.
✓ Branch 4 → 19 taken 8552 times.
✓ Branch 12 → 13 taken 106005 times.
✓ Branch 12 → 19 taken 64380 times.
✓ Branch 14 → 15 taken 9326 times.
✓ Branch 14 → 19 taken 96679 times.
✓ Branch 21 → 22 taken 187721 times.
✓ Branch 21 → 23 taken 258145 times.
892506 !m_session.predictions()->ready() || !m_predictions.contains(id) ||
24
3/3
✓ Branch 17 → 18 taken 9326 times.
✓ Branch 18 → 19 taken 64 times.
✓ Branch 18 → 20 taken 9262 times.
445930 m_predictionEpochs.value(id.folderId) != m_session.cache()->uidValidity(id.folderId)) return {};
25 // Hints are viewport-scoped: the selected mail always shows one, list rows
26 // only while the suggestion column is open and the row is watched. The
27 // cache behind this may retain more, so a revisited mail answers instantly.
28
2/2
✓ Branch 27 → 28 taken 9262 times.
✓ Branch 28 → 29 taken 9262 times.
9262 const int row = activeRowForIdentity(WorkspaceState::encode(id));
29
2/2
✓ Branch 30 → 31 taken 420 times.
✓ Branch 30 → 32 taken 8842 times.
9262 if (row < 0) return {};
30
2/2
✓ Branch 34 → 35 taken 2298 times.
✓ Branch 34 → 43 taken 6544 times.
11140 if (id != m_workspace.mailId() &&
31
3/4
✓ Branch 36 → 37 taken 1689 times.
✓ Branch 36 → 42 taken 609 times.
✓ Branch 37 → 38 taken 1689 times.
✗ Branch 37 → 42 not taken.
2298 !(suggestionColumnShown() && m_predictionFirst >= 0 &&
32
5/7
✓ Branch 38 → 39 taken 1689 times.
✗ Branch 38 → 42 not taken.
✓ Branch 39 → 40 taken 1689 times.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 1689 times.
✓ Branch 44 → 45 taken 609 times.
✓ Branch 44 → 46 taken 8233 times.
10531 row >= m_predictionFirst && row <= qMin(m_predictionLast, activeRowCount() - 1))) return {};
33 8233 const auto target = m_predictions.value(id).visible(m_predictionFolders.value(id.folderId), m_alternatePredictions.contains(id));
34
3/3
✓ Branch 52 → 53 taken 8233 times.
✓ Branch 53 → 54 taken 4906 times.
✓ Branch 53 → 55 taken 3327 times.
8233 return m_folders.isSelectable(target.first) ? target : FolderSuggestion::Candidate{};
35 8233 }
36 409187 QString QuickMailBackend::predictionText(const MailId &id) const {
37
1/1
✓ Branch 2 → 3 taken 409187 times.
409187 const auto target = visiblePrediction(id);
38
2/2
✓ Branch 4 → 5 taken 406346 times.
✓ Branch 4 → 6 taken 2841 times.
409187 if (target.first.isEmpty()) return {};
39
3/3
✓ Branch 6 → 7 taken 2841 times.
✓ Branch 8 → 9 taken 2841 times.
✓ Branch 9 → 10 taken 2841 times.
8523 return QString("%1 (%2%)%3").arg(ImapResponseParser::decodeMailboxName(target.first))
40
5/6
✓ Branch 11 → 12 taken 2841 times.
✓ Branch 14 → 15 taken 95 times.
✓ Branch 14 → 19 taken 2746 times.
✓ Branch 17 → 18 taken 95 times.
✗ Branch 17 → 19 not taken.
✓ Branch 20 → 21 taken 2841 times.
14300 .arg(int(target.second * 100)).arg(m_alternatePredictions.contains(id) &&
41
3/5
✓ Branch 21 → 22 taken 2841 times.
✓ Branch 24 → 25 taken 95 times.
✓ Branch 24 → 26 taken 2746 times.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 38 not taken.
5777 target == m_predictions.value(id).alternate ? " [ALT]" : "");
42 409187 }
43 18135 QVariantMap QuickMailBackend::suggestion() const {
44 18135 const auto id = m_workspace.mailId();
45
1/1
✓ Branch 4 → 5 taken 18135 times.
18135 const auto target = visiblePrediction(id);
46 ✗ return {{"target", target.first}, {"text", predictionText(id)}, {"confidence", target.second},
47
5/7
✓ Branch 12 → 13 taken 18135 times.
✓ Branch 14 → 15 taken 18135 times.
✓ Branch 15 → 16 taken 18135 times.
✓ Branch 19 → 20 taken 90675 times.
✓ Branch 19 → 21 taken 18135 times.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 35 not taken.
145080 {"identity", WorkspaceState::encode(id)}, {"reason", suggestionDetails(WorkspaceState::encode(id)).value("reason")}};
48 54405 }
49 18301 QVariantMap QuickMailBackend::suggestionDetails(const QVariantMap &identity) const {
50
1/1
✓ Branch 2 → 3 taken 18301 times.
18301 const auto id = WorkspaceState::decode(identity);
51
1/1
✓ Branch 3 → 4 taken 18301 times.
18301 const auto target = visiblePrediction(id);
52
2/2
✓ Branch 5 → 6 taken 17215 times.
✓ Branch 5 → 7 taken 1086 times.
18301 if (target.first.isEmpty()) return {};
53 1086 const auto suggestion = m_predictions.value(id);
54 // The reason explains the primary; a shown alternate says only that.
55
4/4
✓ Branch 9 → 10 taken 1002 times.
✓ Branch 9 → 11 taken 84 times.
✓ Branch 10 → 12 taken 1002 times.
✓ Branch 11 → 12 taken 84 times.
1086 return {{"reason", target == suggestion.primary ? suggestionReason(suggestion) : tr("Second choice")},
56
2/4
✓ Branch 16 → 17 taken 2172 times.
✓ Branch 16 → 18 taken 1086 times.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
3258 {"strong", target.second >= StrongSuggestion}};
57 20473 }
58 1002 QString QuickMailBackend::suggestionReason(const FolderSuggestion &suggestion) {
59 // The full path, as the suggestion column names it.
60
1/1
✓ Branch 2 → 3 taken 1002 times.
1002 const QString folder = ImapResponseParser::decodeMailboxName(suggestion.primary.first);
61
2/7
✓ Branch 3 → 4 taken 14 times.
✗ Branch 3 → 9 not taken.
✓ Branch 3 → 20 taken 988 times.
✗ Branch 3 → 31 not taken.
✗ Branch 3 → 36 not taken.
✗ Branch 3 → 41 not taken.
✗ Branch 3 → 43 not taken.
1002 switch (suggestion.reason) {
62
2/2
✓ Branch 4 → 5 taken 14 times.
✓ Branch 6 → 7 taken 14 times.
28 case FolderSuggestion::Thread: return tr("Earlier mails of this thread are in %1").arg(folder);
63 ✗ case FolderSuggestion::List:
64 ✗ return tr("Mails of this list: %1 of %2 in %3").arg(suggestion.support).arg(suggestion.total).arg(folder);
65 988 case FolderSuggestion::Sender:
66
4/4
✓ Branch 20 → 21 taken 988 times.
✓ Branch 22 → 23 taken 988 times.
✓ Branch 24 → 25 taken 988 times.
✓ Branch 26 → 27 taken 988 times.
3952 return tr("From this sender: %1 of %2 in %3").arg(suggestion.support).arg(suggestion.total).arg(folder);
67 ✗ case FolderSuggestion::Alias: return tr("Mails to this address went to %1").arg(folder);
68 ✗ case FolderSuggestion::Subject: return tr("Words of the subject point to %1").arg(folder);
69 ✗ case FolderSuggestion::SpamFilter: return tr("Your server's spam filter marked it as spam");
70 ✗ default: return tr("Words of the sender and subject point to %1").arg(folder);
71 }
72 1002 }
73 8300 void QuickMailBackend::invalidatePredictions() {
74 8300 ++m_predictionGeneration;
75 8300 FolderPredictionService::shared()->cancel(this);
76 8300 m_predictionRequests.clear();
77 // Per-mail predictions stay: they are fenced by the folder epoch in
78 // visiblePrediction(), so keeping them lets a revisited mail answer from
79 // cache instantly instead of paying a worker round trip per selection.
80 8300 m_predictionDebounce.stop();
81
13/15
✓ Branch 6 → 7 taken 8300 times.
✓ Branch 7 → 8 taken 8245 times.
✓ Branch 7 → 17 taken 55 times.
✓ Branch 10 → 11 taken 7524 times.
✓ Branch 10 → 17 taken 721 times.
✓ Branch 12 → 13 taken 6047 times.
✓ Branch 12 → 17 taken 1477 times.
✓ Branch 15 → 16 taken 1328 times.
✓ Branch 15 → 17 taken 4719 times.
✓ Branch 18 → 19 taken 8245 times.
✓ Branch 18 → 20 taken 55 times.
✓ Branch 20 → 21 taken 1328 times.
✓ Branch 20 → 22 taken 6972 times.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
8300 if (configured() && m_workspace.area() == "mail" && m_workspace.activeTab() == 0 && m_session.predictions()->ready())
82 1328 m_predictionDebounce.start();
83 8300 m_mail.suggestionsChanged();
84 8300 m_threadList.suggestionsChanged();
85 8300 emit suggestionsChanged();
86 8300 }
87 10 bool QuickMailBackend::canArmQuickMoveRetry(const MailId &selected) const {
88 // Arm only while a prediction for this exact selection is still in flight:
89 // the 20 ms debounce running or a queued batch pending. Pending
90 // transport/selection work must never pile onto an armed retry.
91
4/7
✓ Branch 4 → 5 taken 9 times.
✓ Branch 6 → 7 taken 9 times.
✗ Branch 6 → 21 not taken.
✓ Branch 25 → 26 taken 1 time.
✓ Branch 25 → 27 taken 9 times.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 39 not taken.
19 if (!selected.isValid() || selected.accountId != accountName() ||
92
4/8
✓ Branch 9 → 10 taken 9 times.
✗ Branch 9 → 21 not taken.
✓ Branch 10 → 11 taken 9 times.
✗ Branch 10 → 21 not taken.
✓ Branch 12 → 13 taken 9 times.
✗ Branch 12 → 21 not taken.
✓ Branch 15 → 16 taken 9 times.
✗ Branch 15 → 21 not taken.
18 m_workspace.mailId() != selected || m_actionPending || m_selection.pending() ||
93
4/4
✓ Branch 3 → 4 taken 9 times.
✓ Branch 3 → 21 taken 1 time.
✓ Branch 23 → 24 taken 9 times.
✓ Branch 23 → 25 taken 1 time.
28 m_session.undo()->busy() ||
94
5/7
✓ Branch 16 → 17 taken 9 times.
✓ Branch 17 → 18 taken 5 times.
✓ Branch 17 → 22 taken 4 times.
✓ Branch 19 → 20 taken 5 times.
✗ Branch 19 → 22 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 5 times.
9 !(m_predictionDebounce.isActive() || !m_predictionRequests.isEmpty() || m_predictionBatchesRunning > 0))
95 1 return false;
96 // Epoch invariant (UIDVALIDITY admission): arming is only safe while the
97 // recorded folder epoch still matches the cache. An epoch that moved
98 // without a resync signal must keep the direct manual-move fallback — a
99 // batch computed under a changed epoch never grounds a quick move.
100
1/2
✓ Branch 28 → 29 taken 9 times.
✗ Branch 28 → 34 not taken.
18 return m_predictionEpochs.contains(selected.folderId) &&
101
2/2
✓ Branch 32 → 33 taken 1 time.
✓ Branch 32 → 34 taken 8 times.
18 m_predictionEpochs.value(selected.folderId) == m_session.cache()->uidValidity(selected.folderId);
102 }
103 1 void QuickMailBackend::armQuickMoveRetry(const MailId &selected) {
104 1 m_quickMoveArmed = selected;
105 1 m_quickMoveFallback.start();
106 1 }
107 926 void QuickMailBackend::disarmQuickMove() {
108 926 m_quickMoveArmed = {};
109 926 m_quickMoveFallback.stop();
110 926 }
111 8571 void QuickMailBackend::tryArmedQuickMove() {
112
6/8
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 7 taken 8570 times.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 7 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1 time.
✓ Branch 9 → 10 taken 8570 times.
✓ Branch 9 → 11 taken 1 time.
8571 if (!m_quickMoveArmed.isValid() || m_actionPending || m_selection.pending()) return;
113 // The press belonged to one selection; a different mail disarms silently.
114
2/4
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 19 not taken.
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 19 not taken.
2 if (m_workspace.mailId() != m_quickMoveArmed || m_workspace.activeTab() != 0 ||
115
3/7
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 1 time.
✓ Branch 21 → 22 taken 1 time.
✗ Branch 21 → 23 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 26 taken 1 time.
✗ Branch 24 → 25 not taken.
2 m_workspace.area() != "mail") { disarmQuickMove(); return; }
116
1/1
✓ Branch 26 → 27 taken 1 time.
1 const auto target = visiblePrediction(m_quickMoveArmed).first;
117
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 1 time.
1 if (target.isEmpty()) return; // still waiting; the timeout owns the fallback
118
1/1
✓ Branch 32 → 33 taken 1 time.
1 disarmQuickMove();
119
1/1
✓ Branch 33 → 34 taken 1 time.
1 moveSelectedMail(target);
120 1 }
121 535 void QuickMailBackend::requestPredictions() {
122
5/10
✓ Branch 4 → 5 taken 535 times.
✗ Branch 4 → 10 not taken.
✓ Branch 7 → 8 taken 535 times.
✗ Branch 7 → 10 not taken.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 535 times.
✓ Branch 12 → 13 taken 535 times.
✗ Branch 12 → 14 not taken.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 36 taken 535 times.
535 if (!m_session.predictions()->ready() || m_workspace.area() != "mail" || m_workspace.activeTab() != 0) {
123 ✗ qCDebug(lcQuickBackend) << "predictions not requested: ready" << m_session.predictions()->ready() << "area"
124 ✗ << m_workspace.area() << "tab" << m_workspace.activeTab();
125 142 return;
126 }
127 // A cached entry fenced by the folder epoch answers instantly; only
128 // uncached identities pay the worker round trip.
129 898 const auto cached = [this](const MailId &id) {
130
2/2
✓ Branch 3 → 4 taken 112 times.
✓ Branch 3 → 9 taken 786 times.
1010 return m_predictions.contains(id) &&
131
2/2
✓ Branch 7 → 8 taken 105 times.
✓ Branch 7 → 9 taken 7 times.
1010 m_predictionEpochs.value(id.folderId) == m_session.cache()->uidValidity(id.folderId);
132 535 };
133 535 QList<MailId> demand;
134 // The demand budget accounts for the retained in-scope cache, so
135 // overlapping demands cannot push the projection past MaxRows. The scope
136 // prune on watch changes keeps that cache within the watched range.
137 535 const int budget = qMax(0, FolderPredictionService::MaxRows - m_predictions.size());
138 535 const auto selected = m_workspace.mailId();
139
6/9
✓ Branch 42 → 43 taken 470 times.
✓ Branch 42 → 52 taken 65 times.
✓ Branch 43 → 44 taken 470 times.
✓ Branch 45 → 46 taken 470 times.
✗ Branch 45 → 52 not taken.
✓ Branch 57 → 58 taken 380 times.
✓ Branch 57 → 59 taken 155 times.
✗ Branch 173 → 174 not taken.
✗ Branch 173 → 175 not taken.
1070 if (budget > 0 && selected.isValid() && selected.accountId == accountName() &&
140
12/15
✓ Branch 40 → 41 taken 535 times.
✗ Branch 40 → 52 not taken.
✓ Branch 46 → 47 taken 470 times.
✓ Branch 47 → 48 taken 470 times.
✓ Branch 48 → 49 taken 449 times.
✓ Branch 48 → 52 taken 21 times.
✓ Branch 49 → 50 taken 449 times.
✓ Branch 50 → 51 taken 380 times.
✓ Branch 50 → 52 taken 69 times.
✓ Branch 53 → 54 taken 470 times.
✓ Branch 53 → 55 taken 65 times.
✓ Branch 55 → 56 taken 470 times.
✓ Branch 55 → 57 taken 65 times.
✗ Branch 169 → 170 not taken.
✗ Branch 169 → 171 not taken.
1070 activeRowForIdentity(WorkspaceState::encode(selected)) >= 0 && !cached(selected)) {
141 // The selected mail rides first, so pressing s waits for one 1-uid
142 // classification, never for the viewport backlog.
143
1/1
✓ Branch 58 → 59 taken 380 times.
380 demand.append(selected);
144 }
145
5/6
✓ Branch 60 → 61 taken 33 times.
✓ Branch 60 → 63 taken 502 times.
✓ Branch 61 → 62 taken 33 times.
✗ Branch 61 → 63 not taken.
✓ Branch 64 → 65 taken 33 times.
✓ Branch 64 → 108 taken 502 times.
535 if (suggestionColumnShown() && m_predictionFirst >= 0) {
146
5/5
✓ Branch 100 → 101 taken 482 times.
✓ Branch 102 → 103 taken 451 times.
✓ Branch 102 → 106 taken 31 times.
✓ Branch 107 → 66 taken 449 times.
✓ Branch 107 → 108 taken 33 times.
933 for (int row = m_predictionFirst; row <= qMin(m_predictionLast, activeRowCount() - 1) &&
147
2/2
✓ Branch 104 → 105 taken 449 times.
✓ Branch 104 → 106 taken 2 times.
451 demand.size() < budget; ++row) {
148
3/5
✓ Branch 66 → 67 taken 449 times.
✓ Branch 67 → 68 taken 449 times.
✓ Branch 68 → 69 taken 449 times.
✗ Branch 181 → 182 not taken.
✗ Branch 181 → 183 not taken.
449 const MailId id{accountName(), activeKeyAt(row).folderId, activeKeyAt(row).uid};
149
8/9
✓ Branch 72 → 73 taken 449 times.
✗ Branch 72 → 77 not taken.
✓ Branch 73 → 74 taken 449 times.
✓ Branch 74 → 75 taken 413 times.
✓ Branch 74 → 77 taken 36 times.
✓ Branch 76 → 77 taken 17 times.
✓ Branch 76 → 78 taken 396 times.
✓ Branch 79 → 80 taken 53 times.
✓ Branch 79 → 81 taken 396 times.
449 if (!id.isValid() || cached(id) || id == selected) continue;
150 396 bool known = false;
151
4/4
✓ Branch 81 → 82 taken 396 times.
✓ Branch 82 → 83 taken 396 times.
✓ Branch 90 → 84 taken 18223 times.
✓ Branch 90 → 91 taken 396 times.
18619 for (const auto &pending : demand)
152
1/4
✗ Branch 85 → 86 not taken.
✓ Branch 85 → 88 taken 18223 times.
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 88 not taken.
18223 if (pending.uid == id.uid && pending.folderId == id.folderId) { known = true; break; }
153
2/3
✓ Branch 91 → 92 taken 396 times.
✗ Branch 91 → 93 not taken.
✓ Branch 92 → 93 taken 396 times.
396 if (!known) demand.append(id);
154 449 }
155 }
156
9/9
✓ Branch 108 → 109 taken 535 times.
✓ Branch 112 → 113 taken 535 times.
✓ Branch 113 → 114 taken 535 times.
✓ Branch 115 → 116 taken 535 times.
✓ Branch 116 → 117 taken 535 times.
✓ Branch 117 → 118 taken 535 times.
✓ Branch 118 → 119 taken 535 times.
✓ Branch 130 → 110 taken 535 times.
✓ Branch 130 → 131 taken 535 times.
1070 qCDebug(lcQuickBackend) << "prediction demand" << demand.size() << "rows" << m_predictionFirst << m_predictionLast
157
6/6
✓ Branch 119 → 120 taken 535 times.
✓ Branch 121 → 122 taken 535 times.
✓ Branch 122 → 123 taken 535 times.
✓ Branch 123 → 124 taken 535 times.
✓ Branch 124 → 125 taken 535 times.
✓ Branch 126 → 127 taken 535 times.
535 << "queued" << m_predictionRequests.size() << "running" << m_predictionBatchesRunning << "cached" << m_predictions.size();
158
2/2
✓ Branch 132 → 133 taken 142 times.
✓ Branch 132 → 134 taken 393 times.
535 if (demand.isEmpty()) return;
159
3/5
✓ Branch 137 → 138 taken 393 times.
✓ Branch 140 → 141 taken 393 times.
✓ Branch 140 → 142 taken 393 times.
✗ Branch 197 → 198 not taken.
✗ Branch 197 → 199 not taken.
786 appendPredictionRequests({demand.first()});
160 // Small chunks publish the column progressively: the first rows are usable
161 // while the remaining batches are still classifying on the single worker.
162
2/2
✓ Branch 148 → 143 taken 31 times.
✓ Branch 148 → 149 taken 393 times.
424 for (int begin = 1; begin < demand.size(); begin += ViewportPredictionChunk)
163
2/2
✓ Branch 143 → 144 taken 31 times.
✓ Branch 144 → 145 taken 31 times.
31 appendPredictionRequests(demand.mid(begin, ViewportPredictionChunk));
164
1/1
✓ Branch 149 → 150 taken 393 times.
393 nextPredictionBatch(m_predictionGeneration);
165 1070 }
166 424 void QuickMailBackend::appendPredictionRequests(const QList<MailId> &identities) {
167 424 QHash<qint64, QList<qint64>> byFolder;
168
2/2
✓ Branch 11 → 4 taken 776 times.
✓ Branch 11 → 12 taken 424 times.
1200 for (const auto &id : identities)
169
3/4
✓ Branch 6 → 7 taken 776 times.
✗ Branch 6 → 9 not taken.
✓ Branch 7 → 8 taken 776 times.
✓ Branch 8 → 9 taken 776 times.
776 if (id.isValid()) byFolder[id.folderId].append(id.uid);
170
2/2
✓ Branch 43 → 13 taken 424 times.
✓ Branch 43 → 44 taken 424 times.
848 for (auto it = byFolder.cbegin(); it != byFolder.cend(); ++it) {
171
1/1
✓ Branch 15 → 16 taken 424 times.
424 const auto path = m_session.cache()->folderPath(it.key());
172
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 424 times.
424 if (!m_availableFolders.contains(path)) continue;
173 424 m_predictionRequests.append({m_session.cache()->databasePath(), m_session.predictions()->databasePath(),
174
1/1
✓ Branch 29 → 30 taken 424 times.
424 accountName(), path, m_junkFolder, it.key(), m_session.cache()->uidValidity(it.key()), it.value()});
175 424 }
176 1272 }
177 661 void QuickMailBackend::nextPredictionBatch(quint64 generation) {
178
6/6
✓ Branch 2 → 3 taken 660 times.
✓ Branch 2 → 5 taken 1 time.
✓ Branch 4 → 5 taken 241 times.
✓ Branch 4 → 6 taken 419 times.
✓ Branch 7 → 8 taken 242 times.
✓ Branch 7 → 9 taken 419 times.
661 if (generation != m_predictionGeneration || m_predictionRequests.isEmpty()) return;
179
1/1
✓ Branch 9 → 10 taken 419 times.
419 const auto request = m_predictionRequests.takeFirst();
180 419 ++m_predictionRequestCount;
181 419 ++m_predictionBatchesRunning;
182 419 const auto accountGeneration = m_session.accounts()->generation();
183 419 QElapsedTimer started;
184 419 started.start();
185
3/5
✓ Branch 13 → 14 taken 419 times.
✓ Branch 15 → 16 taken 419 times.
✓ Branch 17 → 18 taken 419 times.
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 34 not taken.
419 FolderPredictionService::shared()->predict(this, request, [this, request, generation, accountGeneration, started](FolderPredictionResult result) {
186 268 --m_predictionBatchesRunning;
187
4/4
✓ Branch 2 → 3 taken 268 times.
✓ Branch 6 → 7 taken 268 times.
✓ Branch 32 → 4 taken 268 times.
✓ Branch 32 → 33 taken 268 times.
1072 qCDebug(lcQuickBackend).noquote() << QStringLiteral("prediction batch answered folder=%1 mails=%2 items=%3 ms=%4")
188
5/5
✓ Branch 12 → 13 taken 268 times.
✓ Branch 15 → 16 taken 268 times.
✓ Branch 18 → 19 taken 268 times.
✓ Branch 21 → 22 taken 268 times.
✓ Branch 22 → 23 taken 268 times.
1340 .arg(request.folder).arg(request.uids.size()).arg(result.items.size()).arg(started.elapsed());
189 // A new generation cleared the queue and started its own chain.
190
1/2
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 268 times.
273 if (generation != m_predictionGeneration) return;
191 // F100-59: a result that no longer fits (account, cache, folder epoch or
192 // path changed meanwhile) is dropped, but the queue goes on — returning
193 // here stopped every later batch until the next demand.
194
0/2
✗ Branch 271 → 272 not taken.
✗ Branch 271 → 273 not taken.
268 const QString stale = accountGeneration != m_session.accounts()->generation() ? QStringLiteral("account")
195
3/10
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 48 taken 268 times.
✓ Branch 87 → 88 taken 268 times.
✗ Branch 87 → 89 not taken.
✗ Branch 89 → 90 not taken.
✓ Branch 89 → 91 taken 268 times.
✗ Branch 263 → 264 not taken.
✗ Branch 263 → 265 not taken.
✗ Branch 267 → 268 not taken.
✗ Branch 267 → 269 not taken.
804 : request.cachePath != m_session.cache()->databasePath() ? QStringLiteral("cache")
196
4/11
✓ Branch 48 → 49 taken 268 times.
✗ Branch 50 → 51 not taken.
✓ Branch 50 → 54 taken 268 times.
✓ Branch 83 → 84 taken 268 times.
✗ Branch 83 → 85 not taken.
✗ Branch 85 → 86 not taken.
✓ Branch 85 → 87 taken 268 times.
✗ Branch 255 → 256 not taken.
✗ Branch 255 → 257 not taken.
✗ Branch 259 → 260 not taken.
✗ Branch 259 → 261 not taken.
804 : request.account != accountName() ? QStringLiteral("account name")
197
4/7
✓ Branch 55 → 56 taken 268 times.
✓ Branch 56 → 57 taken 5 times.
✓ Branch 56 → 60 taken 263 times.
✗ Branch 81 → 82 not taken.
✓ Branch 81 → 83 taken 268 times.
✗ Branch 251 → 252 not taken.
✗ Branch 251 → 253 not taken.
536 : request.uidValidity != m_session.cache()->uidValidity(request.folderId) ? QStringLiteral("folder epoch")
198
6/11
✓ Branch 61 → 62 taken 263 times.
✗ Branch 63 → 64 not taken.
✓ Branch 63 → 67 taken 263 times.
✓ Branch 77 → 78 taken 263 times.
✓ Branch 77 → 79 taken 5 times.
✓ Branch 79 → 80 taken 5 times.
✓ Branch 79 → 81 taken 263 times.
✗ Branch 243 → 244 not taken.
✗ Branch 243 → 245 not taken.
✗ Branch 247 → 248 not taken.
✗ Branch 247 → 249 not taken.
794 : m_session.cache()->folderPath(request.folderId) != request.folder ? QStringLiteral("folder path")
199
4/10
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 41 taken 268 times.
✗ Branch 68 → 69 not taken.
✓ Branch 68 → 72 taken 263 times.
✗ Branch 73 → 74 not taken.
✓ Branch 73 → 75 taken 268 times.
✗ Branch 75 → 76 not taken.
✓ Branch 75 → 77 taken 268 times.
✗ Branch 239 → 240 not taken.
✗ Branch 239 → 241 not taken.
1335 : !m_availableFolders.contains(request.folder) ? QStringLiteral("folder not available") : QString();
200
2/2
✓ Branch 92 → 93 taken 5 times.
✓ Branch 92 → 117 taken 263 times.
268 if (!stale.isEmpty()) {
201
4/4
✓ Branch 93 → 94 taken 5 times.
✓ Branch 97 → 98 taken 5 times.
✓ Branch 114 → 95 taken 5 times.
✓ Branch 114 → 115 taken 5 times.
20 qCInfo(lcQuickBackend).noquote() << QStringLiteral("prediction batch dropped reason=%1 folder=%2 mails=%3")
202
3/3
✓ Branch 102 → 103 taken 5 times.
✓ Branch 105 → 106 taken 5 times.
✓ Branch 106 → 107 taken 5 times.
15 .arg(stale, request.folder).arg(request.uids.size());
203
1/1
✓ Branch 115 → 116 taken 5 times.
5 nextPredictionBatch(generation);
204 5 return;
205 }
206
1/2
✗ Branch 118 → 119 not taken.
✓ Branch 118 → 138 taken 263 times.
263 if (!result.error.isEmpty())
207 ✗ qCWarning(lcQuickBackend).noquote() << QStringLiteral("prediction batch failed folder=%1 error=%2").arg(request.folder, result.error);
208
1/2
✓ Branch 139 → 140 taken 263 times.
✗ Branch 139 → 179 not taken.
263 if (result.error.isEmpty()) {
209
1/1
✓ Branch 140 → 141 taken 263 times.
263 m_predictionEpochs.insert(request.folderId, request.uidValidity);
210
1/1
✓ Branch 141 → 142 taken 263 times.
263 m_predictionFolders.insert(request.folderId, request.folder);
211
4/4
✓ Branch 142 → 143 taken 263 times.
✓ Branch 143 → 144 taken 263 times.
✓ Branch 157 → 145 taken 480 times.
✓ Branch 157 → 158 taken 263 times.
743 for (const auto &item : result.items) {
212
2/6
✗ Branch 147 → 148 not taken.
✓ Branch 147 → 151 taken 480 times.
✗ Branch 149 → 150 not taken.
✗ Branch 149 → 151 not taken.
✗ Branch 152 → 153 not taken.
✓ Branch 152 → 154 taken 480 times.
480 if (m_predictions.size() >= MaximumCachedPredictions && !m_predictions.contains(item.mailId))
213 ✗ m_predictions.clear();
214
1/1
✓ Branch 154 → 155 taken 480 times.
480 m_predictions.insert(item.mailId, item.suggestion);
215 }
216 // The viewport cache and alternate state never grow with scrolling.
217
4/4
✓ Branch 158 → 159 taken 263 times.
✓ Branch 175 → 176 taken 274 times.
✓ Branch 177 → 160 taken 11 times.
✓ Branch 177 → 178 taken 263 times.
274 for (auto it = m_alternatePredictions.begin(); it != m_alternatePredictions.end();)
218
6/7
✓ Branch 162 → 163 taken 8 times.
✓ Branch 162 → 168 taken 3 times.
✓ Branch 166 → 167 taken 8 times.
✗ Branch 166 → 168 not taken.
✓ Branch 169 → 170 taken 8 times.
✓ Branch 169 → 174 taken 3 times.
✓ Branch 171 → 172 taken 8 times.
11 if (!m_predictions.contains(*it) && *it != m_workspace.mailId()) it = m_alternatePredictions.erase(it); else ++it;
219 }
220
1/1
✓ Branch 179 → 180 taken 263 times.
263 const QPointer<QuickMailBackend> guard(this);
221
1/5
✗ Branch 181 → 182 not taken.
✓ Branch 181 → 189 taken 263 times.
✗ Branch 182 → 183 not taken.
✗ Branch 184 → 185 not taken.
✗ Branch 185 → 186 not taken.
263 if (!result.error.isEmpty()) setStatus(tr("Folder suggestions unavailable: %1").arg(result.error));
222
1/2
✗ Branch 190 → 191 not taken.
✓ Branch 190 → 192 taken 263 times.
263 if (!guard) return;
223
1/1
✓ Branch 192 → 193 taken 263 times.
263 m_mail.suggestionsChanged();
224
1/1
✓ Branch 193 → 194 taken 263 times.
263 m_threadList.suggestionsChanged();
225
1/2
✗ Branch 195 → 196 not taken.
✓ Branch 195 → 197 taken 263 times.
263 if (!guard) return;
226
1/1
✓ Branch 197 → 198 taken 263 times.
263 emit suggestionsChanged();
227
2/3
✓ Branch 199 → 200 taken 263 times.
✗ Branch 199 → 201 not taken.
✓ Branch 200 → 201 taken 263 times.
263 if (guard) nextPredictionBatch(generation);
228
3/4
✓ Branch 203 → 204 taken 263 times.
✗ Branch 203 → 205 not taken.
✓ Branch 207 → 208 taken 263 times.
✓ Branch 207 → 210 taken 5 times.
268 });
229 419 }
230