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: 71.9% 69 / 0 / 96
Functions: 71.4% 5 / 0 / 7
Branches: 65.6% 99 / 0 / 151

data/FolderSuggestionStore.cpp
Line Branch Exec Source
1 #include "FolderSuggestionStore.h"
2
3 #include <QDateTime>
4 #include <QFileInfo>
5 #include <QSqlError>
6 #include <QSqlQuery>
7 #include <QUuid>
8
9 namespace {
10 ✗ bool fail(const QSqlQuery &query, QString *error) {
11 ✗ if (error) *error = query.lastError().text();
12 ✗ return false;
13 }
14 } // namespace
15
16 433 FolderSuggestionStore::~FolderSuggestionStore() { close(); }
17
18 866 void FolderSuggestionStore::close() {
19
2/2
✓ Branch 3 → 4 taken 433 times.
✓ Branch 3 → 5 taken 433 times.
866 if (m_connection.isEmpty()) return;
20 433 m_db.close();
21
2/2
✓ Branch 6 → 7 taken 433 times.
✓ Branch 7 → 8 taken 433 times.
433 m_db = QSqlDatabase();
22 433 QSqlDatabase::removeDatabase(m_connection);
23 433 m_connection.clear();
24 }
25
26 433 bool FolderSuggestionStore::open(const QString &predictorPath, QString *error) {
27
1/1
✓ Branch 2 → 3 taken 433 times.
433 close();
28 // The training session creates the predictor file; this store never does.
29
3/4
✓ Branch 3 → 4 taken 433 times.
✓ Branch 4 → 5 taken 433 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 16 taken 433 times.
433 if (!QFileInfo(predictorPath).isFile()) {
30 ✗ if (error) *error = QStringLiteral("Folder prediction database is unavailable");
31 ✗ return false;
32 }
33
3/3
✓ Branch 16 → 17 taken 433 times.
✓ Branch 17 → 18 taken 433 times.
✓ Branch 21 → 22 taken 433 times.
866 m_connection = QStringLiteral("folder_suggestions_") + QUuid::createUuid().toString(QUuid::WithoutBraces);
34
2/2
✓ Branch 30 → 31 taken 433 times.
✓ Branch 31 → 32 taken 433 times.
866 m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), m_connection);
35
1/1
✓ Branch 35 → 36 taken 433 times.
433 m_db.setDatabaseName(predictorPath);
36
1/1
✓ Branch 39 → 40 taken 433 times.
866 m_db.setConnectOptions(QStringLiteral("QSQLITE_BUSY_TIMEOUT=200"));
37
2/3
✓ Branch 42 → 43 taken 433 times.
✗ Branch 43 → 44 not taken.
✓ Branch 43 → 53 taken 433 times.
433 if (!m_db.open()) {
38 ✗ if (error) *error = m_db.lastError().text();
39 ✗ close();
40 ✗ return false;
41 }
42
1/1
✓ Branch 53 → 54 taken 433 times.
433 QSqlQuery query(m_db);
43
2/3
✓ Branch 57 → 58 taken 433 times.
✗ Branch 60 → 61 not taken.
✓ Branch 60 → 64 taken 433 times.
433 if (!query.exec(QStringLiteral(
44 "CREATE TABLE IF NOT EXISTS folder_suggestions ("
45 " account TEXT NOT NULL, folder_id INTEGER NOT NULL, uid_validity INTEGER NOT NULL, uid INTEGER NOT NULL,"
46 " primary_folder TEXT NOT NULL, primary_conf REAL NOT NULL, alternate_folder TEXT NOT NULL,"
47 " alternate_conf REAL NOT NULL, threshold REAL NOT NULL, reason INTEGER NOT NULL, support INTEGER NOT NULL,"
48 " total INTEGER NOT NULL, stored_at INTEGER NOT NULL,"
49 " PRIMARY KEY (account, folder_id, uid_validity, uid)) WITHOUT ROWID"))) {
50 ✗ fail(query, error);
51 ✗ close();
52 ✗ return false;
53 }
54 433 return true;
55 433 }
56
57 264 QHash<qint64, FolderSuggestion> FolderSuggestionStore::read(const QString &account, qint64 folderId,
58 quint32 uidValidity, const QList<qint64> &uids,
59 QString *error) const {
60 264 QHash<qint64, FolderSuggestion> stored;
61
4/7
✓ Branch 2 → 3 taken 264 times.
✓ Branch 3 → 4 taken 264 times.
✗ Branch 3 → 6 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 264 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 11 taken 264 times.
264 if (!m_db.isOpen() || uids.isEmpty()) return stored;
62 264 QStringList marks;
63
3/3
✓ Branch 15 → 16 taken 266 times.
✓ Branch 20 → 12 taken 266 times.
✓ Branch 20 → 21 taken 264 times.
530 for (int i = 0; i < uids.size(); ++i) marks.append(QStringLiteral("?"));
64
1/1
✓ Branch 21 → 22 taken 264 times.
264 QSqlQuery query(m_db);
65
1/1
✓ Branch 22 → 23 taken 264 times.
264 query.setForwardOnly(true);
66
1/1
✓ Branch 31 → 32 taken 264 times.
792 query.prepare(QStringLiteral("SELECT uid, primary_folder, primary_conf, alternate_folder, alternate_conf, threshold, "
67 "reason, support, total FROM folder_suggestions WHERE account = ? AND folder_id = ? AND uid_validity = ? "
68
2/2
✓ Branch 29 → 30 taken 264 times.
✓ Branch 30 → 31 taken 264 times.
792 "AND uid IN (%1)").arg(marks.join(QLatin1Char(','))));
69
1/1
✓ Branch 38 → 39 taken 264 times.
264 query.addBindValue(account);
70
1/1
✓ Branch 42 → 43 taken 264 times.
264 query.addBindValue(folderId);
71
1/1
✓ Branch 46 → 47 taken 264 times.
264 query.addBindValue(qint64(uidValidity));
72
3/3
✓ Branch 53 → 54 taken 266 times.
✓ Branch 57 → 50 taken 266 times.
✓ Branch 57 → 58 taken 264 times.
530 for (const qint64 uid : uids) query.addBindValue(uid);
73
2/3
✓ Branch 58 → 59 taken 264 times.
✗ Branch 59 → 60 not taken.
✓ Branch 59 → 62 taken 264 times.
264 if (!query.exec()) {
74 ✗ fail(query, error);
75 ✗ return {};
76 }
77
3/3
✓ Branch 101 → 102 taken 265 times.
✓ Branch 102 → 63 taken 1 time.
✓ Branch 102 → 103 taken 264 times.
265 while (query.next()) {
78 1 FolderSuggestion suggestion;
79
4/4
✓ Branch 63 → 64 taken 1 time.
✓ Branch 64 → 65 taken 1 time.
✓ Branch 65 → 66 taken 1 time.
✓ Branch 66 → 67 taken 1 time.
1 suggestion.primary = {query.value(1).toString(), query.value(2).toDouble()};
80
4/4
✓ Branch 73 → 74 taken 1 time.
✓ Branch 74 → 75 taken 1 time.
✓ Branch 75 → 76 taken 1 time.
✓ Branch 76 → 77 taken 1 time.
1 suggestion.alternate = {query.value(3).toString(), query.value(4).toDouble()};
81
2/2
✓ Branch 83 → 84 taken 1 time.
✓ Branch 84 → 85 taken 1 time.
1 suggestion.threshold = query.value(5).toDouble();
82
2/2
✓ Branch 86 → 87 taken 1 time.
✓ Branch 87 → 88 taken 1 time.
1 suggestion.reason = query.value(6).toInt();
83
2/2
✓ Branch 89 → 90 taken 1 time.
✓ Branch 90 → 91 taken 1 time.
1 suggestion.support = query.value(7).toInt();
84
2/2
✓ Branch 92 → 93 taken 1 time.
✓ Branch 93 → 94 taken 1 time.
1 suggestion.total = query.value(8).toInt();
85
3/3
✓ Branch 95 → 96 taken 1 time.
✓ Branch 96 → 97 taken 1 time.
✓ Branch 97 → 98 taken 1 time.
1 stored.insert(query.value(0).toLongLong(), suggestion);
86 1 }
87 264 return stored;
88 264 }
89
90 388 bool FolderSuggestionStore::write(const QString &account, qint64 folderId, quint32 uidValidity,
91 const QList<QPair<qint64, FolderSuggestion>> &suggestions, QString *error) {
92
6/7
✓ Branch 2 → 3 taken 388 times.
✓ Branch 3 → 4 taken 388 times.
✗ Branch 3 → 6 not taken.
✓ Branch 5 → 6 taken 3 times.
✓ Branch 5 → 7 taken 385 times.
✓ Branch 8 → 9 taken 3 times.
✓ Branch 8 → 10 taken 385 times.
388 if (!m_db.isOpen() || suggestions.isEmpty()) return true;
93
2/3
✓ Branch 10 → 11 taken 385 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 20 taken 385 times.
385 if (!m_db.transaction()) {
94 ✗ if (error) *error = m_db.lastError().text();
95 ✗ return false;
96 }
97
1/1
✓ Branch 20 → 21 taken 385 times.
385 QSqlQuery query(m_db);
98
1/1
✓ Branch 24 → 25 taken 385 times.
385 query.prepare(QStringLiteral("INSERT OR REPLACE INTO folder_suggestions (account, folder_id, uid_validity, uid, "
99 "primary_folder, primary_conf, alternate_folder, alternate_conf, threshold, reason, "
100 "support, total, stored_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
101 385 const qint64 now = QDateTime::currentSecsSinceEpoch();
102
2/2
✓ Branch 110 → 30 taken 615 times.
✓ Branch 110 → 111 taken 385 times.
1000 for (const auto &[uid, suggestion] : suggestions) {
103
1/1
✓ Branch 35 → 36 taken 615 times.
615 query.addBindValue(account);
104
1/1
✓ Branch 39 → 40 taken 615 times.
615 query.addBindValue(folderId);
105
1/1
✓ Branch 43 → 44 taken 615 times.
615 query.addBindValue(qint64(uidValidity));
106
1/1
✓ Branch 47 → 48 taken 615 times.
615 query.addBindValue(uid);
107 // A null QString binds as SQL NULL; "no folder" is stored as ''.
108
5/7
✓ Branch 51 → 52 taken 275 times.
✓ Branch 51 → 55 taken 340 times.
✓ Branch 57 → 58 taken 615 times.
✓ Branch 60 → 61 taken 275 times.
✓ Branch 60 → 62 taken 340 times.
✗ Branch 157 → 158 not taken.
✗ Branch 157 → 159 not taken.
890 query.addBindValue(suggestion.primary.first.isNull() ? QStringLiteral("") : suggestion.primary.first);
109
1/1
✓ Branch 64 → 65 taken 615 times.
615 query.addBindValue(suggestion.primary.second);
110
5/7
✓ Branch 68 → 69 taken 515 times.
✓ Branch 68 → 72 taken 100 times.
✓ Branch 74 → 75 taken 615 times.
✓ Branch 77 → 78 taken 515 times.
✓ Branch 77 → 79 taken 100 times.
✗ Branch 172 → 173 not taken.
✗ Branch 172 → 174 not taken.
1130 query.addBindValue(suggestion.alternate.first.isNull() ? QStringLiteral("") : suggestion.alternate.first);
111
1/1
✓ Branch 81 → 82 taken 615 times.
615 query.addBindValue(suggestion.alternate.second);
112
1/1
✓ Branch 85 → 86 taken 615 times.
615 query.addBindValue(suggestion.threshold);
113
1/1
✓ Branch 89 → 90 taken 615 times.
615 query.addBindValue(suggestion.reason);
114
1/1
✓ Branch 93 → 94 taken 615 times.
615 query.addBindValue(suggestion.support);
115
1/1
✓ Branch 97 → 98 taken 615 times.
615 query.addBindValue(suggestion.total);
116
1/1
✓ Branch 101 → 102 taken 615 times.
615 query.addBindValue(now);
117
2/3
✓ Branch 103 → 104 taken 615 times.
✗ Branch 104 → 105 not taken.
✓ Branch 104 → 108 taken 615 times.
615 if (!query.exec()) {
118 ✗ m_db.rollback();
119 ✗ return fail(query, error);
120 }
121 }
122
2/3
✓ Branch 111 → 113 taken 385 times.
✗ Branch 113 → 114 not taken.
✓ Branch 113 → 123 taken 385 times.
385 if (!m_db.commit()) {
123 ✗ if (error) *error = m_db.lastError().text();
124 ✗ m_db.rollback();
125 ✗ return false;
126 }
127 385 return true;
128 385 }
129
130 ✗ bool FolderSuggestionStore::prune(int maxAgeDays, QString *error) {
131 ✗ if (!m_db.isOpen()) return true;
132 ✗ QSqlQuery query(m_db);
133 ✗ query.prepare(QStringLiteral("DELETE FROM folder_suggestions WHERE stored_at < ?"));
134 ✗ query.addBindValue(QDateTime::currentSecsSinceEpoch() - qint64(maxAgeDays) * 86400);
135 ✗ return query.exec() || fail(query, error);
136 ✗ }
137