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: 89.8% 717 / 0 / 798
Functions: 100.0% 22 / 0 / 22
Branches: 68.9% 950 / 0 / 1378

performance/FixtureGenerator.cpp
Line Branch Exec Source
1 #include "performance/FixtureGenerator.h"
2 #include "performance/ThreadFixture.h"
3
4 #include "data/CalendarStore.h"
5 #include "data/ContactStore.h"
6 #include "performance/ContactShowcase.h"
7 #include "data/ContactQueries.h"
8 #include "data/VCardCodec.h"
9 #include "data/MailCache.h"
10 #include "ui/ThemeManager.h"
11
12 #include <QCryptographicHash>
13 #include <QDate>
14 #include <QDir>
15 #include <QDirIterator>
16 #include <QElapsedTimer>
17 #include <QFile>
18 #include <QFileInfo>
19 #include <QJsonArray>
20 #include <QJsonDocument>
21 #include <QSaveFile>
22 #include <QSqlDatabase>
23 #include <QSqlError>
24 #include <QSqlQuery>
25 #include <QTime>
26 #include <QTimeZone>
27 #include <QUuid>
28
29 #include <algorithm>
30
31 namespace MailJD::Performance {
32
33 namespace {
34
35 constexpr auto OwnerMarker = ".mailjd-performance-fixture-v1";
36 constexpr auto AccountId = "performance-account";
37
38 class DeterministicBytes {
39 public:
40
2/2
✓ Branch 2 → 3 taken 30 times.
✓ Branch 2 → 4 taken 1 time.
31 explicit DeterministicBytes(quint64 seed) : m_state(seed ? seed : 1) {}
41
42 31792268 quint64 next() {
43 31792268 m_state ^= m_state << 13;
44 31792268 m_state ^= m_state >> 7;
45 31792268 m_state ^= m_state << 17;
46 31792268 return m_state;
47 }
48
49 75 QByteArray bytes(qsizetype size) {
50 75 QByteArray result(size, Qt::Uninitialized);
51
2/2
✓ Branch 7 → 4 taken 31580160 times.
✓ Branch 7 → 8 taken 75 times.
31580235 for (qsizetype i = 0; i < size; ++i)
52
1/1
✓ Branch 5 → 6 taken 31580160 times.
31580160 result[i] = static_cast<char>(next() & 0xff);
53 75 return result;
54 ✗ }
55
56 private:
57 quint64 m_state;
58 };
59
60 217 QByteArray sha256(const QByteArray &data) {
61
2/2
✓ Branch 3 → 4 taken 217 times.
✓ Branch 4 → 5 taken 217 times.
217 return QCryptographicHash::hash(data, QCryptographicHash::Sha256).toHex();
62 }
63
64 903 QByteArray fileSha256(const QString &path) {
65
1/1
✓ Branch 2 → 3 taken 903 times.
903 QFile file(path);
66
2/3
✓ Branch 4 → 5 taken 903 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 903 times.
903 if (!file.open(QIODevice::ReadOnly))
67 ✗ return {};
68
1/1
✓ Branch 7 → 8 taken 903 times.
903 QCryptographicHash hash(QCryptographicHash::Sha256);
69
2/3
✓ Branch 8 → 9 taken 903 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 903 times.
903 if (!hash.addData(&file))
70 ✗ return {};
71
2/2
✓ Branch 11 → 12 taken 903 times.
✓ Branch 12 → 13 taken 903 times.
903 return hash.result().toHex();
72 903 }
73
74 178 bool writeBytes(const QString &path, const QByteArray &data, QString *error) {
75
1/1
✓ Branch 2 → 3 taken 178 times.
178 QSaveFile file(path);
76
7/8
✓ Branch 4 → 5 taken 178 times.
✓ Branch 5 → 6 taken 176 times.
✓ Branch 5 → 11 taken 2 times.
✓ Branch 6 → 7 taken 176 times.
✓ Branch 8 → 9 taken 176 times.
✗ Branch 8 → 11 not taken.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 26 taken 176 times.
354 if (!file.open(QIODevice::WriteOnly) || file.write(data) != data.size() ||
77
2/3
✓ Branch 9 → 10 taken 176 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 176 times.
176 !file.commit()) {
78
2/2
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 25 taken 1 time.
2 if (error)
79
1/1
✓ Branch 19 → 20 taken 1 time.
2 *error = QStringLiteral("Cannot write fixture file: %1").arg(path);
80 2 return false;
81 }
82 176 return true;
83 178 }
84
85 58 bool writeJson(const QString &path, const QJsonObject &object, QString *error) {
86
3/3
✓ Branch 2 → 3 taken 58 times.
✓ Branch 3 → 4 taken 58 times.
✓ Branch 4 → 5 taken 58 times.
116 return writeBytes(path, QJsonDocument(object).toJson(QJsonDocument::Indented),
87 116 error);
88 }
89
90 29 QByteArray minimalPdf() {
91 const QList<QByteArray> objects = {
92 "<< /Type /Catalog /Pages 2 0 R >>",
93 "<< /Type /Pages /Kids [3 0 R] /Count 1 >>",
94 "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 300 144] /Resources << "
95 "/Font << /F1 5 0 R >> >> /Contents 4 0 R >>",
96 "<< /Length 55 >>\nstream\nBT /F1 18 Tf 36 72 Td (Synthetic PDF fixture) "
97 "Tj ET\nendstream",
98 "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>",
99
2/4
✓ Branch 9 → 10 taken 145 times.
✓ Branch 9 → 11 taken 29 times.
✗ Branch 70 → 71 not taken.
✗ Branch 70 → 72 not taken.
174 };
100
1/1
✓ Branch 11 → 12 taken 29 times.
29 QByteArray pdf("%PDF-1.4\n");
101 29 QList<qint64> offsets;
102
2/2
✓ Branch 27 → 13 taken 145 times.
✓ Branch 27 → 28 taken 29 times.
174 for (qsizetype i = 0; i < objects.size(); ++i) {
103
1/1
✓ Branch 14 → 15 taken 145 times.
145 offsets.append(pdf.size());
104 pdf +=
105
5/5
✓ Branch 16 → 17 taken 145 times.
✓ Branch 17 → 18 taken 145 times.
✓ Branch 18 → 19 taken 145 times.
✓ Branch 19 → 20 taken 145 times.
✓ Branch 20 → 21 taken 145 times.
145 QByteArray::number(i + 1) + " 0 obj\n" + objects.at(i) + "\nendobj\n";
106 }
107 29 const qint64 xref = pdf.size();
108
2/2
✓ Branch 30 → 31 taken 29 times.
✓ Branch 31 → 32 taken 29 times.
58 pdf += "xref\n0 " + QByteArray::number(objects.size() + 1) +
109
2/2
✓ Branch 32 → 33 taken 29 times.
✓ Branch 33 → 34 taken 29 times.
29 "\n0000000000 65535 f \n";
110
4/4
✓ Branch 37 → 38 taken 29 times.
✓ Branch 38 → 39 taken 29 times.
✓ Branch 50 → 40 taken 145 times.
✓ Branch 50 → 51 taken 29 times.
174 for (qint64 offset : offsets)
111
4/4
✓ Branch 41 → 42 taken 145 times.
✓ Branch 42 → 43 taken 145 times.
✓ Branch 43 → 44 taken 145 times.
✓ Branch 44 → 45 taken 145 times.
145 pdf += QByteArray::number(offset).rightJustified(10, '0') + " 00000 n \n";
112
2/2
✓ Branch 53 → 54 taken 29 times.
✓ Branch 54 → 55 taken 29 times.
58 pdf += "trailer\n<< /Size " + QByteArray::number(objects.size() + 1) +
113
3/3
✓ Branch 51 → 52 taken 29 times.
✓ Branch 55 → 56 taken 29 times.
✓ Branch 56 → 57 taken 29 times.
116 " /Root 1 0 R >>\nstartxref\n" + QByteArray::number(xref) +
114
2/2
✓ Branch 57 → 58 taken 29 times.
✓ Branch 58 → 59 taken 29 times.
29 "\n%%EOF\n";
115 29 return pdf;
116 58 }
117
118 76 QString folderName(int index) {
119
1/1
✓ Branch 7 → 8 taken 76 times.
76 return QStringLiteral("Synthetic-%1").arg(index + 1, 3, 10, QLatin1Char('0'));
120 }
121
122 36 bool removeOwnedOutput(const QString &directory, QString *error) {
123
1/1
✓ Branch 2 → 3 taken 36 times.
36 const QDir dir(directory);
124
3/3
✓ Branch 3 → 4 taken 36 times.
✓ Branch 4 → 5 taken 21 times.
✓ Branch 4 → 6 taken 15 times.
36 if (!dir.exists())
125 21 return true;
126
5/5
✓ Branch 7 → 8 taken 15 times.
✓ Branch 8 → 9 taken 15 times.
✓ Branch 9 → 10 taken 15 times.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 25 taken 14 times.
15 if (!QFileInfo::exists(dir.filePath(QString::fromLatin1(OwnerMarker)))) {
127
1/2
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 24 not taken.
1 if (error)
128 *error =
129 2 QStringLiteral("Refusing to replace an unowned fixture directory: %1")
130
1/1
✓ Branch 18 → 19 taken 1 time.
2 .arg(directory);
131 1 return false;
132 }
133 const QStringList files = {
134 ✗ QStringLiteral("mail.sqlite"),
135 14 QStringLiteral("mail.sqlite-wal"),
136 14 QStringLiteral("mail.sqlite-shm"),
137 14 QStringLiteral("calendar.sqlite"),
138 14 QStringLiteral("calendar.sqlite-wal"),
139 14 QStringLiteral("calendar.sqlite-shm"),
140 14 QStringLiteral("contacts.sqlite"),
141 14 QStringLiteral("contacts.sqlite-wal"),
142 14 QStringLiteral("contacts.sqlite-shm"),
143 14 QStringLiteral("workloads.json"),
144 14 QStringLiteral("manifest.json"),
145
2/4
✓ Branch 60 → 61 taken 154 times.
✓ Branch 60 → 62 taken 14 times.
✗ Branch 152 → 153 not taken.
✗ Branch 152 → 154 not taken.
168 };
146
2/2
✓ Branch 81 → 75 taken 154 times.
✓ Branch 81 → 82 taken 14 times.
168 for (const QString &file : files)
147
2/2
✓ Branch 76 → 77 taken 154 times.
✓ Branch 77 → 78 taken 154 times.
154 QFile::remove(dir.filePath(file));
148 28 for (const QString &child :
149
2/2
✓ Branch 123 → 92 taken 28 times.
✓ Branch 123 → 124 taken 14 times.
126 {QStringLiteral("payloads"), QStringLiteral("migrations")}) {
150
2/2
✓ Branch 92 → 93 taken 28 times.
✓ Branch 93 → 94 taken 28 times.
28 QDir childDir(dir.filePath(child));
151
6/8
✓ Branch 95 → 96 taken 28 times.
✓ Branch 96 → 97 taken 20 times.
✓ Branch 96 → 100 taken 8 times.
✓ Branch 97 → 98 taken 20 times.
✗ Branch 98 → 99 not taken.
✓ Branch 98 → 100 taken 20 times.
✗ Branch 101 → 102 not taken.
✓ Branch 101 → 116 taken 28 times.
28 if (childDir.exists() && !childDir.removeRecursively()) {
152 ✗ if (error)
153 ✗ *error = QStringLiteral("Cannot clear generated fixture child: %1")
154 ✗ .arg(childDir.path());
155 ✗ return false;
156 }
157 70 }
158 14 return true;
159 204 }
160
161 87 bool mutateSqlite(const QString &path, const QStringList &statements,
162 QString *error) {
163 174 const QString connection = QStringLiteral("fixture_snapshot_") +
164
3/3
✓ Branch 2 → 3 taken 87 times.
✓ Branch 3 → 4 taken 87 times.
✓ Branch 7 → 8 taken 87 times.
261 QUuid::createUuid().toString(QUuid::WithoutBraces);
165 87 bool ok = true;
166 {
167 QSqlDatabase db =
168
1/1
✓ Branch 14 → 15 taken 87 times.
87 QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), connection);
169
1/1
✓ Branch 17 → 18 taken 87 times.
87 db.setDatabaseName(path);
170
2/3
✓ Branch 18 → 19 taken 87 times.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 28 taken 87 times.
87 if (!db.open()) {
171 ✗ if (error)
172 ✗ *error = db.lastError().text();
173 ✗ ok = false;
174 } else {
175
1/1
✓ Branch 28 → 29 taken 87 times.
87 QSqlQuery query(db);
176
2/2
✓ Branch 51 → 31 taken 667 times.
✓ Branch 51 → 52 taken 87 times.
754 for (const QString &statement : statements) {
177
2/3
✓ Branch 32 → 33 taken 667 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 49 taken 667 times.
667 if (!query.exec(statement)) {
178 ✗ if (error)
179 ✗ *error = QStringLiteral("Snapshot SQL failed: %1: %2")
180 ✗ .arg(statement, query.lastError().text());
181 ✗ ok = false;
182 ✗ break;
183 }
184 }
185
1/1
✓ Branch 52 → 53 taken 87 times.
87 db.close();
186 87 }
187 87 }
188
1/1
✓ Branch 56 → 57 taken 87 times.
87 QSqlDatabase::removeDatabase(connection);
189 87 return ok;
190 87 }
191
192 29 QJsonObject artifactManifest(const QString &directory) {
193
1/1
✓ Branch 2 → 3 taken 29 times.
29 QJsonObject artifacts;
194
3/3
✓ Branch 3 → 4 taken 29 times.
✓ Branch 4 → 5 taken 29 times.
✓ Branch 5 → 6 taken 29 times.
29 QDir root(QFileInfo(directory).absoluteFilePath());
195
1/1
✓ Branch 10 → 11 taken 29 times.
58 QDirIterator it(root.absolutePath(), QDir::Files,
196
1/1
✓ Branch 11 → 12 taken 29 times.
58 QDirIterator::Subdirectories);
197
3/3
✓ Branch 72 → 73 taken 320 times.
✓ Branch 73 → 14 taken 291 times.
✓ Branch 73 → 74 taken 29 times.
320 while (it.hasNext()) {
198
1/1
✓ Branch 14 → 15 taken 291 times.
291 const QString absolute = it.next();
199
1/1
✓ Branch 15 → 16 taken 291 times.
291 const QString relative = root.relativeFilePath(absolute);
200
4/12
✓ Branch 20 → 21 taken 291 times.
✗ Branch 20 → 25 not taken.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 291 times.
✓ Branch 31 → 32 taken 291 times.
✗ Branch 31 → 33 not taken.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 291 times.
✗ Branch 94 → 95 not taken.
✗ Branch 94 → 96 not taken.
✗ Branch 98 → 99 not taken.
✗ Branch 98 → 100 not taken.
1164 if (relative == QStringLiteral("manifest.json") ||
201
3/7
✓ Branch 22 → 23 taken 291 times.
✓ Branch 27 → 28 taken 291 times.
✗ Branch 27 → 29 not taken.
✓ Branch 29 → 30 taken 291 times.
✗ Branch 29 → 31 not taken.
✗ Branch 89 → 90 not taken.
✗ Branch 89 → 91 not taken.
582 relative == QString::fromLatin1(OwnerMarker))
202 ✗ continue;
203
1/1
✓ Branch 35 → 36 taken 291 times.
291 const QFileInfo info(absolute);
204
1/1
✓ Branch 49 → 50 taken 291 times.
291 artifacts.insert(relative,
205
1/1
✓ Branch 39 → 40 taken 291 times.
873 QJsonObject{{QStringLiteral("bytes"), info.size()},
206 582 {QStringLiteral("sha256"),
207
4/6
✓ Branch 44 → 45 taken 291 times.
✓ Branch 45 → 46 taken 291 times.
✓ Branch 53 → 54 taken 582 times.
✓ Branch 53 → 55 taken 291 times.
✗ Branch 107 → 108 not taken.
✗ Branch 107 → 109 not taken.
1164 QString::fromLatin1(fileSha256(absolute))}});
208 291 }
209 29 return artifacts;
210 902 }
211
212 2 QJsonObject readJsonObject(const QString &path) {
213
1/1
✓ Branch 2 → 3 taken 2 times.
2 QFile file(path);
214
2/3
✓ Branch 4 → 5 taken 2 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 2 times.
2 if (!file.open(QIODevice::ReadOnly))
215 ✗ return {};
216 2 QJsonParseError error;
217 const QJsonDocument document =
218
2/2
✓ Branch 8 → 9 taken 2 times.
✓ Branch 9 → 10 taken 2 times.
2 QJsonDocument::fromJson(file.readAll(), &error);
219
2/3
✓ Branch 12 → 13 taken 2 times.
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 16 not taken.
2 return error.error == QJsonParseError::NoError && document.isObject()
220
1/2
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 16 not taken.
2 ? document.object()
221
1/2
✓ Branch 14 → 15 taken 2 times.
✗ Branch 16 → 17 not taken.
2 : QJsonObject{};
222 2 }
223
224 131 bool validateSqlite(const QString &path, int expectedSchema,
225 int expectedFtsRows, QString *error) {
226 262 const QString connection = QStringLiteral("fixture_verify_") +
227
3/3
✓ Branch 2 → 3 taken 131 times.
✓ Branch 3 → 4 taken 131 times.
✓ Branch 7 → 8 taken 131 times.
393 QUuid::createUuid().toString(QUuid::WithoutBraces);
228 131 bool valid = true;
229 {
230 QSqlDatabase db =
231
1/1
✓ Branch 14 → 15 taken 131 times.
131 QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), connection);
232
1/1
✓ Branch 17 → 18 taken 131 times.
131 db.setDatabaseName(path);
233
2/3
✓ Branch 18 → 19 taken 131 times.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 36 taken 131 times.
131 if (!db.open()) {
234 ✗ if (error)
235 ✗ *error = QStringLiteral("Cannot validate fixture database: %1")
236 ✗ .arg(db.lastError().text());
237 ✗ valid = false;
238 } else {
239
1/1
✓ Branch 36 → 37 taken 131 times.
131 QSqlQuery query(db);
240
6/13
✓ Branch 40 → 41 taken 131 times.
✓ Branch 41 → 42 taken 131 times.
✗ Branch 41 → 44 not taken.
✓ Branch 46 → 47 taken 131 times.
✗ Branch 46 → 48 not taken.
✓ Branch 48 → 49 taken 131 times.
✗ Branch 48 → 50 not taken.
✓ Branch 50 → 51 taken 2 times.
✓ Branch 50 → 63 taken 129 times.
✗ Branch 167 → 168 not taken.
✗ Branch 167 → 169 not taken.
✗ Branch 171 → 172 not taken.
✗ Branch 171 → 173 not taken.
393 if (!query.exec(QStringLiteral("PRAGMA foreign_key_check")) ||
241
3/3
✓ Branch 42 → 43 taken 131 times.
✓ Branch 43 → 44 taken 2 times.
✓ Branch 43 → 45 taken 129 times.
131 query.next()) {
242
2/2
✓ Branch 51 → 52 taken 1 time.
✓ Branch 51 → 62 taken 1 time.
2 if (error)
243 *error =
244 2 QStringLiteral("Fixture database has a foreign-key violation: %1")
245
1/1
✓ Branch 56 → 57 taken 1 time.
2 .arg(path);
246 2 valid = false;
247 }
248
4/4
✓ Branch 63 → 64 taken 129 times.
✓ Branch 63 → 76 taken 2 times.
✓ Branch 64 → 65 taken 90 times.
✓ Branch 64 → 76 taken 39 times.
221 if (valid && expectedSchema >= 0 &&
249
6/11
✓ Branch 68 → 69 taken 90 times.
✓ Branch 69 → 70 taken 90 times.
✗ Branch 69 → 75 not taken.
✓ Branch 81 → 82 taken 90 times.
✓ Branch 81 → 83 taken 41 times.
✓ Branch 83 → 84 taken 2 times.
✓ Branch 83 → 96 taken 129 times.
✗ Branch 187 → 188 not taken.
✗ Branch 187 → 189 not taken.
✗ Branch 191 → 192 not taken.
✗ Branch 191 → 193 not taken.
221 (!query.exec(QStringLiteral("PRAGMA user_version")) ||
250
10/13
✓ Branch 70 → 71 taken 90 times.
✓ Branch 71 → 72 taken 90 times.
✗ Branch 71 → 75 not taken.
✓ Branch 72 → 73 taken 90 times.
✓ Branch 73 → 74 taken 90 times.
✓ Branch 74 → 75 taken 2 times.
✓ Branch 74 → 76 taken 88 times.
✓ Branch 77 → 78 taken 90 times.
✓ Branch 77 → 79 taken 41 times.
✓ Branch 79 → 80 taken 90 times.
✓ Branch 79 → 81 taken 41 times.
✗ Branch 183 → 184 not taken.
✗ Branch 183 → 185 not taken.
221 !query.next() || query.value(0).toInt() != expectedSchema)) {
251
2/2
✓ Branch 84 → 85 taken 1 time.
✓ Branch 84 → 95 taken 1 time.
2 if (error)
252 2 *error = QStringLiteral(
253 "Fixture database schema version is unexpected: %1")
254
1/1
✓ Branch 89 → 90 taken 1 time.
2 .arg(path);
255 2 valid = false;
256 }
257
4/4
✓ Branch 96 → 97 taken 127 times.
✓ Branch 96 → 109 taken 4 times.
✓ Branch 97 → 98 taken 47 times.
✓ Branch 97 → 109 taken 80 times.
178 if (valid && expectedFtsRows >= 0 &&
258
6/11
✓ Branch 101 → 102 taken 47 times.
✓ Branch 102 → 103 taken 47 times.
✗ Branch 102 → 108 not taken.
✓ Branch 114 → 115 taken 47 times.
✓ Branch 114 → 116 taken 84 times.
✓ Branch 116 → 117 taken 2 times.
✓ Branch 116 → 129 taken 129 times.
✗ Branch 207 → 208 not taken.
✗ Branch 207 → 209 not taken.
✗ Branch 211 → 212 not taken.
✗ Branch 211 → 213 not taken.
178 (!query.exec(QStringLiteral("SELECT COUNT(*) FROM mail_fts")) ||
259
10/13
✓ Branch 103 → 104 taken 47 times.
✓ Branch 104 → 105 taken 47 times.
✗ Branch 104 → 108 not taken.
✓ Branch 105 → 106 taken 47 times.
✓ Branch 106 → 107 taken 47 times.
✓ Branch 107 → 108 taken 2 times.
✓ Branch 107 → 109 taken 45 times.
✓ Branch 110 → 111 taken 47 times.
✓ Branch 110 → 112 taken 84 times.
✓ Branch 112 → 113 taken 47 times.
✓ Branch 112 → 114 taken 84 times.
✗ Branch 203 → 204 not taken.
✗ Branch 203 → 205 not taken.
178 !query.next() || query.value(0).toInt() != expectedFtsRows)) {
260
2/2
✓ Branch 117 → 118 taken 1 time.
✓ Branch 117 → 128 taken 1 time.
2 if (error)
261 2 *error = QStringLiteral("Fixture FTS row count is unexpected: %1")
262
1/1
✓ Branch 122 → 123 taken 1 time.
2 .arg(path);
263 2 valid = false;
264 }
265
1/1
✓ Branch 129 → 130 taken 131 times.
131 db.close();
266 131 }
267 131 }
268
1/1
✓ Branch 133 → 134 taken 131 times.
131 QSqlDatabase::removeDatabase(connection);
269 131 return valid;
270 131 }
271
272 29 qint64 artifactBytes(const QJsonObject &artifacts) {
273 29 qint64 total = 0;
274
3/3
✓ Branch 15 → 16 taken 320 times.
✓ Branch 17 → 3 taken 291 times.
✓ Branch 17 → 18 taken 29 times.
320 for (auto it = artifacts.constBegin(); it != artifacts.constEnd(); ++it)
275
3/3
✓ Branch 4 → 5 taken 291 times.
✓ Branch 8 → 9 taken 291 times.
✓ Branch 9 → 10 taken 291 times.
582 total += it.value().toObject().value(QStringLiteral("bytes")).toInteger();
276 29 return total;
277 }
278
279 29 QJsonObject workloadManifest(const FixtureProfile &profile) {
280 QJsonObject manifest = {
281 58 {QStringLiteral("schema_version"), 1},
282
2/4
✓ Branch 154 → 155 taken 116 times.
✓ Branch 154 → 156 taken 29 times.
✗ Branch 553 → 554 not taken.
✗ Branch 553 → 555 not taken.
174 {QStringLiteral("mutation_sets"), QJsonArray{1, 10, 100, 1000}},
283
2/4
✓ Branch 148 → 149 taken 87 times.
✓ Branch 148 → 150 taken 29 times.
✗ Branch 534 → 535 not taken.
✗ Branch 534 → 536 not taken.
145 {QStringLiteral("delivery_bursts"), QJsonArray{1, 10, 100}},
284
2/4
✓ Branch 142 → 143 taken 87 times.
✓ Branch 142 → 144 taken 29 times.
✗ Branch 515 → 516 not taken.
✗ Branch 515 → 517 not taken.
145 {QStringLiteral("attachment_counts"), QJsonArray{1, 10, 50}},
285
2/4
✓ Branch 136 → 137 taken 87 times.
✓ Branch 136 → 138 taken 29 times.
✗ Branch 496 → 497 not taken.
✗ Branch 496 → 498 not taken.
145 {QStringLiteral("contact_scales"), QJsonArray{100, 2000, 20000}},
286
2/4
✓ Branch 130 → 131 taken 116 times.
✓ Branch 130 → 132 taken 29 times.
✗ Branch 477 → 478 not taken.
✗ Branch 477 → 479 not taken.
174 {QStringLiteral("folder_row_scales"), QJsonArray{0, 100, 10000, 100000}},
287 58 {QStringLiteral("draft_revisions"), 5},
288 58 {QStringLiteral("contention_schedule"),
289 58 QJsonArray{QStringLiteral("mail_open_during_header_sync"),
290 58 QStringLiteral("search_during_fts"),
291 58 QStringLiteral("calendar_during_caldav"),
292
2/4
✓ Branch 114 → 115 taken 116 times.
✓ Branch 114 → 116 taken 29 times.
✗ Branch 427 → 428 not taken.
✗ Branch 427 → 429 not taken.
203 QStringLiteral("autosave_during_attachment")}},
293 58 {QStringLiteral("profile_counts"),
294 87 QJsonObject{{QStringLiteral("headers"), profile.headers},
295 58 {QStringLiteral("events"), profile.events},
296 58 {QStringLiteral("tasks"), profile.tasks},
297
2/4
✓ Branch 100 → 101 taken 116 times.
✓ Branch 100 → 102 taken 29 times.
✗ Branch 384 → 385 not taken.
✗ Branch 384 → 386 not taken.
203 {QStringLiteral("contacts"), profile.contacts}}},
298
2/4
✓ Branch 96 → 97 taken 261 times.
✓ Branch 96 → 98 taken 29 times.
✗ Branch 378 → 379 not taken.
✗ Branch 378 → 380 not taken.
319 };
299
2/2
✓ Branch 166 → 167 taken 1 time.
✓ Branch 166 → 250 taken 28 times.
29 if (profile.name == QStringLiteral("thread-dense")) {
300
2/2
✓ Branch 170 → 171 taken 1 time.
✓ Branch 174 → 175 taken 1 time.
2 manifest.insert(QStringLiteral("thread_oracle"), QStringLiteral("thread-oracle.jsonl"));
301
2/2
✓ Branch 180 → 181 taken 1 time.
✓ Branch 184 → 185 taken 1 time.
2 manifest.insert(QStringLiteral("thread_block_size"), 100);
302
1/1
✓ Branch 225 → 226 taken 1 time.
2 manifest.insert(QStringLiteral("thread_shapes"), QJsonArray{
303 4 QStringLiteral("singleton"), QStringLiteral("deep"), QStringLiteral("wide"),
304 3 QStringLiteral("missing-parent"), QStringLiteral("cycle"),
305 3 QStringLiteral("late-parent"), QStringLiteral("duplicate-message-id"),
306
2/4
✓ Branch 231 → 232 taken 8 times.
✓ Branch 231 → 233 taken 1 time.
✗ Branch 617 → 618 not taken.
✗ Branch 617 → 619 not taken.
11 QStringLiteral("filtered-descendant")});
307
2/2
✓ Branch 256 → 257 taken 1 time.
✓ Branch 256 → 295 taken 27 times.
28 } else if (profile.name == QStringLiteral("large-body")) {
308
1/1
✓ Branch 278 → 279 taken 1 time.
2 manifest.insert(QStringLiteral("large_body_shapes"),
309 3 QJsonArray{QStringLiteral("plain-1m-raw"),
310 2 QStringLiteral("plain-10m-raw"),
311 2 QStringLiteral("html-1m-raw"),
312
2/4
✓ Branch 284 → 285 taken 4 times.
✓ Branch 284 → 286 taken 1 time.
✗ Branch 687 → 688 not taken.
✗ Branch 687 → 689 not taken.
7 QStringLiteral("html-10m-raw-attachment")});
313
2/2
✓ Branch 301 → 302 taken 1 time.
✓ Branch 301 → 336 taken 26 times.
27 } else if (profile.name == QStringLiteral("tab-churn")) {
314
1/1
✓ Branch 315 → 316 taken 1 time.
2 manifest.insert(
315 2 QStringLiteral("tab_sequence"),
316
2/4
✓ Branch 321 → 322 taken 2 times.
✓ Branch 321 → 323 taken 1 time.
✗ Branch 733 → 734 not taken.
✗ Branch 733 → 735 not taken.
5 QJsonArray{QStringLiteral("plain"), QStringLiteral("html")});
317
2/2
✓ Branch 327 → 328 taken 1 time.
✓ Branch 331 → 332 taken 1 time.
2 manifest.insert(QStringLiteral("tab_cycles"), 50);
318
2/2
✓ Branch 342 → 343 taken 1 time.
✓ Branch 342 → 375 taken 25 times.
26 } else if (profile.name == QStringLiteral("contact-dense")) {
319
1/1
✓ Branch 360 → 361 taken 1 time.
2 manifest.insert(QStringLiteral("contact_collision_families"),
320 3 QJsonArray{QStringLiteral("case"),
321 2 QStringLiteral("diacritic"),
322
2/4
✓ Branch 366 → 367 taken 3 times.
✓ Branch 366 → 368 taken 1 time.
✗ Branch 776 → 777 not taken.
✗ Branch 776 → 778 not taken.
6 QStringLiteral("stable-sort")});
323 }
324 29 return manifest;
325 543 }
326
327 29 bool createMigrationSnapshots(const QString &directory, QString *error) {
328
1/1
✓ Branch 2 → 3 taken 29 times.
29 QDir dir(directory);
329
2/3
✓ Branch 6 → 7 taken 29 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 19 taken 29 times.
29 if (!dir.mkpath(QStringLiteral("migrations"))) {
330 ✗ if (error)
331 ✗ *error = QStringLiteral("Cannot create migration snapshot directory");
332 ✗ return false;
333 }
334
1/1
✓ Branch 22 → 23 taken 29 times.
29 const QString migrations = dir.filePath(QStringLiteral("migrations"));
335
336 const QString mail =
337
2/2
✓ Branch 25 → 26 taken 29 times.
✓ Branch 29 → 30 taken 29 times.
58 QDir(migrations).filePath(QStringLiteral("mail-v16.sqlite"));
338
6/16
✓ Branch 36 → 37 taken 29 times.
✓ Branch 37 → 38 taken 29 times.
✓ Branch 38 → 39 taken 29 times.
✗ Branch 38 → 51 not taken.
✓ Branch 67 → 68 taken 29 times.
✗ Branch 67 → 69 not taken.
✓ Branch 69 → 70 taken 29 times.
✗ Branch 69 → 71 not taken.
✗ Branch 71 → 72 not taken.
✓ Branch 71 → 73 taken 29 times.
✗ Branch 275 → 276 not taken.
✗ Branch 275 → 277 not taken.
✗ Branch 279 → 280 not taken.
✗ Branch 279 → 281 not taken.
✗ Branch 283 → 284 not taken.
✗ Branch 283 → 285 not taken.
116 if (!QFile::copy(dir.filePath(QStringLiteral("mail.sqlite")), mail) ||
339
6/15
✓ Branch 49 → 50 taken 29 times.
✗ Branch 50 → 51 not taken.
✓ Branch 50 → 52 taken 29 times.
✓ Branch 53 → 54 taken 29 times.
✗ Branch 53 → 55 not taken.
✓ Branch 55 → 56 taken 29 times.
✗ Branch 55 → 59 not taken.
✓ Branch 57 → 58 taken 87 times.
✓ Branch 57 → 59 taken 29 times.
✗ Branch 247 → 248 not taken.
✗ Branch 247 → 249 not taken.
✗ Branch 251 → 252 not taken.
✗ Branch 251 → 255 not taken.
✗ Branch 253 → 254 not taken.
✗ Branch 253 → 255 not taken.
145 !mutateSqlite(
340 mail,
341
1/4
✓ Branch 65 → 66 taken 29 times.
✗ Branch 65 → 67 not taken.
✗ Branch 265 → 266 not taken.
✗ Branch 265 → 267 not taken.
29 {QStringLiteral("ALTER TABLE attachments DROP COLUMN presentation"),
342
1/4
✓ Branch 63 → 64 taken 29 times.
✗ Branch 63 → 65 not taken.
✗ Branch 261 → 262 not taken.
✗ Branch 261 → 263 not taken.
29 QStringLiteral("DROP TABLE mutation_journal"),
343
2/6
✓ Branch 59 → 60 taken 29 times.
✗ Branch 59 → 61 not taken.
✓ Branch 61 → 62 taken 29 times.
✗ Branch 61 → 63 not taken.
✗ Branch 257 → 258 not taken.
✗ Branch 257 → 259 not taken.
29 QStringLiteral("PRAGMA user_version=16")},
344 error))
345 ✗ return false;
346
347 const QString calendar =
348
2/2
✓ Branch 73 → 74 taken 29 times.
✓ Branch 77 → 78 taken 29 times.
58 QDir(migrations).filePath(QStringLiteral("calendar-v4.sqlite"));
349
6/16
✓ Branch 84 → 85 taken 29 times.
✓ Branch 85 → 86 taken 29 times.
✓ Branch 86 → 87 taken 29 times.
✗ Branch 86 → 144 not taken.
✓ Branch 190 → 191 taken 29 times.
✗ Branch 190 → 192 not taken.
✓ Branch 192 → 193 taken 29 times.
✗ Branch 192 → 194 not taken.
✗ Branch 194 → 195 not taken.
✓ Branch 194 → 196 taken 29 times.
✗ Branch 384 → 385 not taken.
✗ Branch 384 → 386 not taken.
✗ Branch 388 → 389 not taken.
✗ Branch 388 → 390 not taken.
✗ Branch 392 → 393 not taken.
✗ Branch 392 → 394 not taken.
116 if (!QFile::copy(dir.filePath(QStringLiteral("calendar.sqlite")), calendar) ||
350
6/15
✓ Branch 142 → 143 taken 29 times.
✗ Branch 143 → 144 not taken.
✓ Branch 143 → 145 taken 29 times.
✓ Branch 146 → 147 taken 29 times.
✗ Branch 146 → 148 not taken.
✓ Branch 148 → 149 taken 29 times.
✗ Branch 148 → 152 not taken.
✓ Branch 150 → 151 taken 522 times.
✓ Branch 150 → 152 taken 29 times.
✗ Branch 296 → 297 not taken.
✗ Branch 296 → 298 not taken.
✗ Branch 300 → 301 not taken.
✗ Branch 300 → 304 not taken.
✗ Branch 302 → 303 not taken.
✗ Branch 302 → 304 not taken.
580 !mutateSqlite(
351 calendar,
352
1/4
✓ Branch 188 → 189 taken 29 times.
✗ Branch 188 → 190 not taken.
✗ Branch 374 → 375 not taken.
✗ Branch 374 → 376 not taken.
29 {QStringLiteral("DROP INDEX IF EXISTS idx_tasks_parent"),
353
1/4
✓ Branch 186 → 187 taken 29 times.
✗ Branch 186 → 188 not taken.
✗ Branch 370 → 371 not taken.
✗ Branch 370 → 372 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN ical_raw"),
354
1/4
✓ Branch 184 → 185 taken 29 times.
✗ Branch 184 → 186 not taken.
✗ Branch 366 → 367 not taken.
✗ Branch 366 → 368 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN tz_due"),
355
1/4
✓ Branch 182 → 183 taken 29 times.
✗ Branch 182 → 184 not taken.
✗ Branch 362 → 363 not taken.
✗ Branch 362 → 364 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN tz_start"),
356
1/4
✓ Branch 180 → 181 taken 29 times.
✗ Branch 180 → 182 not taken.
✗ Branch 358 → 359 not taken.
✗ Branch 358 → 360 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN start_all_day"),
357
1/4
✓ Branch 178 → 179 taken 29 times.
✗ Branch 178 → 180 not taken.
✗ Branch 354 → 355 not taken.
✗ Branch 354 → 356 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN parent_uid"),
358
1/4
✓ Branch 176 → 177 taken 29 times.
✗ Branch 176 → 178 not taken.
✗ Branch 350 → 351 not taken.
✗ Branch 350 → 352 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN rrule"),
359
1/4
✓ Branch 174 → 175 taken 29 times.
✗ Branch 174 → 176 not taken.
✗ Branch 346 → 347 not taken.
✗ Branch 346 → 348 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN tags"),
360
1/4
✓ Branch 172 → 173 taken 29 times.
✗ Branch 172 → 174 not taken.
✗ Branch 342 → 343 not taken.
✗ Branch 342 → 344 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN alarms"),
361
1/4
✓ Branch 170 → 171 taken 29 times.
✗ Branch 170 → 172 not taken.
✗ Branch 338 → 339 not taken.
✗ Branch 338 → 340 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN sort_order"),
362
1/4
✓ Branch 168 → 169 taken 29 times.
✗ Branch 168 → 170 not taken.
✗ Branch 334 → 335 not taken.
✗ Branch 334 → 336 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN previous_status"),
363
1/4
✓ Branch 166 → 167 taken 29 times.
✗ Branch 166 → 168 not taken.
✗ Branch 330 → 331 not taken.
✗ Branch 330 → 332 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN previous_percent"),
364
1/4
✓ Branch 164 → 165 taken 29 times.
✗ Branch 164 → 166 not taken.
✗ Branch 326 → 327 not taken.
✗ Branch 326 → 328 not taken.
29 QStringLiteral("ALTER TABLE calendars DROP COLUMN supported_components"),
365
1/4
✓ Branch 162 → 163 taken 29 times.
✗ Branch 162 → 164 not taken.
✗ Branch 322 → 323 not taken.
✗ Branch 322 → 324 not taken.
29 QStringLiteral("ALTER TABLE calendar_outbox DROP COLUMN component_type"),
366
1/4
✓ Branch 160 → 161 taken 29 times.
✗ Branch 160 → 162 not taken.
✗ Branch 318 → 319 not taken.
✗ Branch 318 → 320 not taken.
29 QStringLiteral("ALTER TABLE calendar_outbox DROP COLUMN dependency_key"),
367
1/4
✓ Branch 158 → 159 taken 29 times.
✗ Branch 158 → 160 not taken.
✗ Branch 314 → 315 not taken.
✗ Branch 314 → 316 not taken.
29 QStringLiteral("ALTER TABLE tasks DROP COLUMN due_all_day"),
368
1/4
✓ Branch 156 → 157 taken 29 times.
✗ Branch 156 → 158 not taken.
✗ Branch 310 → 311 not taken.
✗ Branch 310 → 312 not taken.
29 QStringLiteral("ALTER TABLE events DROP COLUMN series_end"),
369
2/6
✓ Branch 152 → 153 taken 29 times.
✗ Branch 152 → 154 not taken.
✓ Branch 154 → 155 taken 29 times.
✗ Branch 154 → 156 not taken.
✗ Branch 306 → 307 not taken.
✗ Branch 306 → 308 not taken.
29 QStringLiteral("PRAGMA user_version=4")},
370 error))
371 ✗ return false;
372
373 const QString contacts =
374
2/2
✓ Branch 196 → 197 taken 29 times.
✓ Branch 200 → 201 taken 29 times.
58 QDir(migrations).filePath(QStringLiteral("contacts-legacy.sqlite"));
375
3/5
✓ Branch 211 → 212 taken 29 times.
✓ Branch 215 → 216 taken 58 times.
✓ Branch 215 → 217 taken 29 times.
✗ Branch 408 → 409 not taken.
✗ Branch 408 → 410 not taken.
116 return mutateSqlite(
376 contacts,
377 ✗ {QStringLiteral(
378 "CREATE TABLE contacts (id INTEGER PRIMARY KEY AUTOINCREMENT, "
379 "display_name TEXT NOT NULL DEFAULT '', email TEXT NOT NULL, "
380 "source TEXT NOT NULL DEFAULT 'local', carddav_uid TEXT DEFAULT '', "
381 "carddav_etag TEXT DEFAULT '', last_used TEXT, "
382 "use_count INTEGER NOT NULL DEFAULT 0, "
383 "created_at TEXT NOT NULL DEFAULT (datetime('now')), "
384 "updated_at TEXT NOT NULL DEFAULT (datetime('now')), "
385 "UNIQUE(email, source))"),
386 29 QStringLiteral(
387 "INSERT INTO contacts(display_name,email,source,use_count) VALUES "
388 "('Synthetic Legacy','legacy@example.invalid','local',1)")},
389 29 error);
390 783 }
391
392 } // namespace
393
394 109 QStringList FixtureGenerator::profileNames() {
395 109 return {QStringLiteral("empty"), QStringLiteral("small"),
396 109 QStringLiteral("typical"), QStringLiteral("large"),
397 109 QStringLiteral("stress"), QStringLiteral("dense-folder"),
398 109 QStringLiteral("large-body"), QStringLiteral("tab-churn"),
399 109 QStringLiteral("contact-dense"), QStringLiteral("contacts-showcase"),
400 109 QStringLiteral("million-folder"), QStringLiteral("million-account"),
401
2/4
✓ Branch 43 → 44 taken 1417 times.
✓ Branch 43 → 45 taken 109 times.
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 63 not taken.
1635 QStringLiteral("thread-dense")};
402 1526 }
403
404 114 FixtureProfile FixtureGenerator::profile(const QString &name) {
405
2/2
✓ Branch 8 → 9 taken 75 times.
✓ Branch 8 → 11 taken 39 times.
114 if (name == QStringLiteral("empty"))
406 75 return {name, 0, 0, 0, 0, 0, 0};
407
2/2
✓ Branch 17 → 18 taken 20 times.
✓ Branch 17 → 20 taken 19 times.
39 if (name == QStringLiteral("small"))
408 20 return {name, 12, 1000, 100, 100, 50, 100};
409
1/2
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 29 taken 19 times.
19 if (name == QStringLiteral("typical"))
410 ✗ return {name, 30, 10000, 1000, 1000, 500, 2000};
411
2/2
✓ Branch 35 → 36 taken 1 time.
✓ Branch 35 → 38 taken 18 times.
19 if (name == QStringLiteral("large"))
412 1 return {name, 100, 100000, 10000, 10000, 5000, 20000};
413
1/2
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 47 taken 18 times.
18 if (name == QStringLiteral("stress"))
414 ✗ return {name, 250, 500000, 25000, 50000, 25000, 100000};
415
2/2
✓ Branch 53 → 54 taken 1 time.
✓ Branch 53 → 56 taken 17 times.
18 if (name == QStringLiteral("dense-folder"))
416 1 return {name, 1, 100000, 1000, 0, 0, 0};
417
2/2
✓ Branch 62 → 63 taken 3 times.
✓ Branch 62 → 65 taken 14 times.
17 if (name == QStringLiteral("large-body"))
418 3 return {name, 1, 4, 4, 0, 0, 0};
419
2/2
✓ Branch 71 → 72 taken 3 times.
✓ Branch 71 → 74 taken 11 times.
14 if (name == QStringLiteral("tab-churn"))
420 3 return {name, 1, 50, 50, 0, 0, 0};
421
2/2
✓ Branch 80 → 81 taken 3 times.
✓ Branch 80 → 83 taken 8 times.
11 if (name == QStringLiteral("contact-dense"))
422 3 return {name, 0, 0, 0, 0, 0, 20000};
423 // Sprint 97 S97.0.5: realistic contacts for captures (ContactShowcase).
424
1/2
✗ Branch 89 → 90 not taken.
✓ Branch 89 → 92 taken 8 times.
8 if (name == QStringLiteral("contacts-showcase"))
425 ✗ return {name, 0, 0, 0, 0, 0, 116};
426
1/2
✗ Branch 98 → 99 not taken.
✓ Branch 98 → 101 taken 8 times.
8 if (name == QStringLiteral("million-folder"))
427 ✗ return {name, 1, 1000000, 10000, 0, 0, 0};
428
1/2
✗ Branch 107 → 108 not taken.
✓ Branch 107 → 110 taken 8 times.
8 if (name == QStringLiteral("million-account"))
429 ✗ return {name, 250, 1000000, 10000, 0, 0, 0};
430
2/2
✓ Branch 116 → 117 taken 3 times.
✓ Branch 116 → 119 taken 5 times.
8 if (name == QStringLiteral("thread-dense"))
431 3 return {name, 1, 100000, 1000, 0, 0, 0};
432 5 return {};
433 109 }
434
435 QByteArray
436 101 FixtureGenerator::logicalChecksum(const QJsonObject &logicalManifest) {
437
3/3
✓ Branch 2 → 3 taken 101 times.
✓ Branch 3 → 4 taken 101 times.
✓ Branch 4 → 5 taken 101 times.
101 return sha256(QJsonDocument(logicalManifest).toJson(QJsonDocument::Compact));
438 }
439
440 39 bool FixtureGenerator::generate(const QString &outputRoot,
441 const QString &profileName, quint64 seed,
442 bool force, QString *error) const {
443
1/1
✓ Branch 2 → 3 taken 39 times.
39 const FixtureProfile spec = profile(profileName);
444
3/3
✓ Branch 3 → 4 taken 39 times.
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 19 taken 37 times.
39 if (!profileNames().contains(profileName)) {
445
2/2
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 18 taken 1 time.
2 if (error)
446
1/1
✓ Branch 12 → 13 taken 1 time.
2 *error = QStringLiteral("Unknown fixture profile: %1").arg(profileName);
447 2 return false;
448 }
449
2/2
✓ Branch 19 → 20 taken 37 times.
✓ Branch 20 → 21 taken 37 times.
37 const QString directory = QDir(outputRoot).filePath(profileName);
450 const QString manifestPath =
451
2/2
✓ Branch 22 → 23 taken 37 times.
✓ Branch 26 → 27 taken 37 times.
74 QDir(directory).filePath(QStringLiteral("manifest.json"));
452
10/10
✓ Branch 30 → 31 taken 20 times.
✓ Branch 30 → 36 taken 17 times.
✓ Branch 31 → 32 taken 20 times.
✓ Branch 32 → 33 taken 3 times.
✓ Branch 32 → 36 taken 17 times.
✓ Branch 33 → 34 taken 3 times.
✓ Branch 34 → 35 taken 2 times.
✓ Branch 34 → 36 taken 1 time.
✓ Branch 37 → 38 taken 2 times.
✓ Branch 37 → 85 taken 35 times.
37 if (!force && QFileInfo::exists(manifestPath) && verify(directory, nullptr)) {
453
1/1
✓ Branch 38 → 39 taken 2 times.
2 const QJsonObject logical = readJsonObject(manifestPath)
454
1/1
✓ Branch 42 → 43 taken 2 times.
4 .value(QStringLiteral("logical"))
455
1/1
✓ Branch 43 → 44 taken 2 times.
2 .toObject();
456
8/20
✓ Branch 51 → 52 taken 2 times.
✓ Branch 52 → 53 taken 2 times.
✓ Branch 54 → 55 taken 2 times.
✗ Branch 54 → 62 not taken.
✓ Branch 71 → 72 taken 2 times.
✗ Branch 71 → 73 not taken.
✓ Branch 73 → 74 taken 2 times.
✗ Branch 73 → 75 not taken.
✓ Branch 75 → 76 taken 2 times.
✗ Branch 75 → 77 not taken.
✓ Branch 77 → 78 taken 1 time.
✓ Branch 77 → 79 taken 1 time.
✗ Branch 1741 → 1742 not taken.
✗ Branch 1741 → 1743 not taken.
✗ Branch 1745 → 1746 not taken.
✗ Branch 1745 → 1747 not taken.
✗ Branch 1749 → 1750 not taken.
✗ Branch 1749 → 1751 not taken.
✗ Branch 1753 → 1754 not taken.
✗ Branch 1753 → 1755 not taken.
8 if (logical.value(QStringLiteral("profile")).toString() == profileName &&
457
6/16
✓ Branch 58 → 59 taken 2 times.
✓ Branch 59 → 60 taken 2 times.
✓ Branch 63 → 64 taken 2 times.
✗ Branch 63 → 65 not taken.
✓ Branch 65 → 66 taken 2 times.
✗ Branch 65 → 67 not taken.
✓ Branch 67 → 68 taken 2 times.
✗ Branch 67 → 69 not taken.
✓ Branch 69 → 70 taken 2 times.
✗ Branch 69 → 71 not taken.
✗ Branch 1729 → 1730 not taken.
✗ Branch 1729 → 1731 not taken.
✗ Branch 1733 → 1734 not taken.
✗ Branch 1733 → 1735 not taken.
✗ Branch 1737 → 1738 not taken.
✗ Branch 1737 → 1739 not taken.
4 logical.value(QStringLiteral("seed")).toInteger() ==
458
2/2
✓ Branch 60 → 61 taken 1 time.
✓ Branch 60 → 62 taken 1 time.
2 static_cast<qint64>(seed))
459 1 return true;
460 2 }
461
3/3
✓ Branch 85 → 86 taken 36 times.
✓ Branch 86 → 87 taken 1 time.
✓ Branch 86 → 88 taken 35 times.
36 if (!removeOwnedOutput(directory, error))
462 1 return false;
463
4/4
✓ Branch 89 → 90 taken 35 times.
✓ Branch 90 → 91 taken 35 times.
✓ Branch 93 → 94 taken 2 times.
✓ Branch 93 → 106 taken 33 times.
35 if (!QDir().mkpath(directory)) {
464
2/2
✓ Branch 94 → 95 taken 1 time.
✓ Branch 94 → 105 taken 1 time.
2 if (error)
465 *error =
466
1/1
✓ Branch 99 → 100 taken 1 time.
2 QStringLiteral("Cannot create fixture directory: %1").arg(directory);
467 2 return false;
468 }
469
4/4
✓ Branch 108 → 109 taken 33 times.
✓ Branch 110 → 111 taken 33 times.
✓ Branch 111 → 112 taken 33 times.
✓ Branch 112 → 113 taken 33 times.
33 if (!writeBytes(QDir(directory).filePath(QString::fromLatin1(OwnerMarker)),
470
2/2
✓ Branch 118 → 119 taken 2 times.
✓ Branch 118 → 120 taken 31 times.
66 QByteArrayLiteral("MailJD performance fixture v1\n"), error))
471 2 return false;
472
473 31 QElapsedTimer generationTimer;
474 31 generationTimer.start();
475 31 DeterministicBytes random(seed);
476
4/4
✓ Branch 122 → 123 taken 31 times.
✓ Branch 123 → 124 taken 31 times.
✓ Branch 124 → 125 taken 31 times.
✓ Branch 125 → 126 taken 31 times.
31 const QDateTime epoch(QDate(2026, 1, 1), QTime(0, 0), QTimeZone::utc());
477
478
1/1
✓ Branch 127 → 128 taken 31 times.
31 MailCache mail;
479
5/5
✓ Branch 128 → 129 taken 31 times.
✓ Branch 132 → 133 taken 31 times.
✓ Branch 133 → 134 taken 31 times.
✓ Branch 138 → 139 taken 2 times.
✓ Branch 138 → 145 taken 29 times.
62 if (!mail.open(QDir(directory).filePath(QStringLiteral("mail.sqlite")))) {
480
2/2
✓ Branch 139 → 140 taken 1 time.
✓ Branch 139 → 144 taken 1 time.
2 if (error)
481
1/1
✓ Branch 140 → 141 taken 1 time.
1 *error = mail.lastError();
482 2 return false;
483 }
484
5/11
✓ Branch 148 → 149 taken 29 times.
✓ Branch 149 → 150 taken 29 times.
✗ Branch 149 → 153 not taken.
✓ Branch 152 → 153 taken 1 time.
✓ Branch 152 → 154 taken 28 times.
✓ Branch 157 → 158 taken 29 times.
✗ Branch 157 → 159 not taken.
✗ Branch 1809 → 1810 not taken.
✗ Branch 1809 → 1811 not taken.
✗ Branch 1813 → 1814 not taken.
✗ Branch 1813 → 1815 not taken.
116 const bool streamHeaders = profileName.startsWith(QStringLiteral("million-")) ||
485
1/2
✓ Branch 155 → 156 taken 29 times.
✗ Branch 155 → 157 not taken.
58 profileName == QLatin1String("thread-dense");
486 29 int globalHeader = 0;
487 29 int storedBodies = 0;
488
1/1
✓ Branch 159 → 160 taken 29 times.
29 QJsonArray bodyHitIdentities;
489
1/1
✓ Branch 160 → 161 taken 29 times.
29 QJsonArray bodyMissIdentities;
490
2/2
✓ Branch 591 → 162 taken 75 times.
✓ Branch 591 → 592 taken 29 times.
104 for (int folderIndex = 0; folderIndex < spec.folders; ++folderIndex) {
491
1/1
✓ Branch 162 → 163 taken 75 times.
75 const QString path = folderName(folderIndex);
492 const qint64 folderId =
493
2/2
✓ Branch 164 → 165 taken 75 times.
✓ Branch 165 → 166 taken 75 times.
75 mail.ensureFolder(QString::fromLatin1(AccountId), path);
494
1/2
✗ Branch 167 → 168 not taken.
✓ Branch 167 → 177 taken 75 times.
75 if (folderId <= 0) {
495 ✗ if (error)
496 ✗ *error = QStringLiteral("Cannot create synthetic folder");
497 ✗ return false;
498 }
499 150 const int count = spec.headers / spec.folders +
500
2/2
✓ Branch 177 → 178 taken 24 times.
✓ Branch 177 → 179 taken 51 times.
75 (folderIndex < spec.headers % spec.folders ? 1 : 0);
501 // Preserve the frozen generation order; new million profiles stream setup.
502 75 const int folderGlobalStart = globalHeader;
503
2/2
✓ Branch 180 → 181 taken 1 time.
✓ Branch 180 → 182 taken 74 times.
75 const int batchLimit = streamHeaders ? 1000 : std::max(1, count);
504
2/2
✓ Branch 584 → 185 taken 174 times.
✓ Branch 584 → 585 taken 75 times.
249 for (int batchStart = 0; batchStart < count; batchStart += batchLimit) {
505 174 const int batchEnd = std::min(count, batchStart + batchLimit);
506 174 QList<MailHeader> headers;
507
1/1
✓ Branch 186 → 187 taken 174 times.
174 headers.reserve(batchEnd - batchStart);
508
2/2
✓ Branch 308 → 188 taken 106054 times.
✓ Branch 308 → 309 taken 174 times.
106228 for (int local = batchStart; local < batchEnd; ++local) {
509 106054 ++globalHeader;
510 106054 MailHeader header;
511 106054 header.uid = local + 1;
512 106054 header.folderId = folderId;
513 212108 header.subject = QStringLiteral("Synthetic mail %1")
514
1/1
✓ Branch 194 → 195 taken 106054 times.
212108 .arg(globalHeader, 7, 10, QLatin1Char('0'));
515 header.from =
516
1/1
✓ Branch 203 → 204 taken 106054 times.
212108 QStringLiteral("sender-%1@example.invalid").arg(globalHeader % 257);
517 106054 header.to = QStringLiteral("recipient@example.invalid");
518
1/1
✓ Branch 215 → 216 taken 106054 times.
106054 header.date = epoch.addSecs(globalHeader * 61 + random.next() % 17);
519
2/2
✓ Branch 218 → 219 taken 35348 times.
✓ Branch 218 → 220 taken 70706 times.
106054 header.flags = globalHeader % 3 == 0 ? MailFlag::Seen : MailFlag::None;
520
2/2
✓ Branch 221 → 222 taken 9634 times.
✓ Branch 221 → 223 taken 96420 times.
106054 if (globalHeader % 11 == 0)
521 9634 header.flags |= MailFlag::Flagged;
522
2/2
✓ Branch 223 → 224 taken 3653 times.
✓ Branch 223 → 225 taken 102401 times.
106054 if (globalHeader % 29 == 0)
523 3653 header.flags |= MailFlag::Answered;
524
2/2
✓ Branch 225 → 226 taken 1044 times.
✓ Branch 225 → 227 taken 105010 times.
106054 if (globalHeader % 101 == 0)
525 1044 header.flags |= MailFlag::Draft;
526 106054 header.size = 512 + static_cast<qint64>(random.next() % 65536);
527 212108 header.hasAttachments = profileName == QStringLiteral("large-body")
528
2/2
✓ Branch 232 → 233 taken 4 times.
✓ Branch 232 → 234 taken 106050 times.
106054 ? globalHeader == spec.headers
529 106050 : globalHeader % 100 == 0;
530
2/2
✓ Branch 237 → 238 taken 8151 times.
✓ Branch 237 → 251 taken 97903 times.
106054 if (globalHeader % 13 == 0)
531
2/4
✓ Branch 246 → 247 taken 16302 times.
✓ Branch 246 → 248 taken 8151 times.
✗ Branch 1840 → 1841 not taken.
✗ Branch 1840 → 1842 not taken.
24453 header.labels = {QStringLiteral("perf-a"), QStringLiteral("perf-b")};
532
2/2
✓ Branch 251 → 252 taken 105122 times.
✓ Branch 251 → 270 taken 932 times.
106054 if (globalHeader % 113 != 0) {
533 header.messageId =
534 105122 globalHeader % 97 == 0
535
3/5
✓ Branch 252 → 253 taken 1081 times.
✓ Branch 252 → 256 taken 104041 times.
✓ Branch 260 → 261 taken 104041 times.
✗ Branch 1864 → 1865 not taken.
✗ Branch 1864 → 1866 not taken.
420488 ? QStringLiteral("duplicate@example.invalid")
536
6/10
✓ Branch 263 → 264 taken 104041 times.
✓ Branch 263 → 265 taken 1081 times.
✓ Branch 265 → 266 taken 104041 times.
✓ Branch 265 → 267 taken 1081 times.
✓ Branch 267 → 268 taken 1081 times.
✓ Branch 267 → 269 taken 104041 times.
✗ Branch 1856 → 1857 not taken.
✗ Branch 1856 → 1858 not taken.
✗ Branch 1860 → 1861 not taken.
✗ Branch 1860 → 1862 not taken.
314285 : QStringLiteral("mail-%1@example.invalid").arg(globalHeader);
537 }
538
4/4
✓ Branch 270 → 271 taken 79541 times.
✓ Branch 270 → 301 taken 26513 times.
✓ Branch 271 → 272 taken 79532 times.
✓ Branch 271 → 301 taken 9 times.
106054 if (globalHeader % 4 != 0 && globalHeader > 1) {
539 header.inReplyTo =
540
1/1
✓ Branch 276 → 277 taken 79532 times.
159064 QStringLiteral("mail-%1@example.invalid").arg(globalHeader - 1);
541
2/4
✓ Branch 284 → 285 taken 79532 times.
✓ Branch 284 → 286 taken 79532 times.
✗ Branch 1878 → 1879 not taken.
✗ Branch 1878 → 1880 not taken.
159064 header.inReplyToIds = {header.inReplyTo};
542
2/4
✓ Branch 295 → 296 taken 159064 times.
✓ Branch 295 → 298 taken 79532 times.
✗ Branch 1888 → 1889 not taken.
✗ Branch 1888 → 1891 not taken.
397660 header.references = {QStringLiteral("mail-%1@example.invalid")
543 79532 .arg(std::max(1, globalHeader - 3)),
544 238596 header.inReplyTo};
545 }
546
2/2
✓ Branch 303 → 304 taken 100000 times.
✓ Branch 303 → 305 taken 6054 times.
106054 if (profileName == QLatin1String("thread-dense"))
547
1/1
✓ Branch 304 → 305 taken 100000 times.
100000 applyThreadFixture(header);
548
1/1
✓ Branch 305 → 306 taken 106054 times.
106054 headers.append(header);
549 106054 }
550
2/3
✓ Branch 309 → 310 taken 174 times.
✗ Branch 310 → 311 not taken.
✓ Branch 310 → 317 taken 174 times.
174 if (!mail.storeHeaders(folderId, headers)) {
551 ✗ if (error) *error = mail.lastError();
552 ✗ return false;
553 }
554
2/2
✓ Branch 317 → 318 taken 75 times.
✓ Branch 317 → 319 taken 99 times.
174 if (batchStart == 0)
555
1/1
✓ Branch 318 → 319 taken 75 times.
75 mail.storeBadge(folderId, (count * 2) / 3);
556
557
4/4
✓ Branch 319 → 320 taken 174 times.
✓ Branch 320 → 321 taken 174 times.
✓ Branch 577 → 322 taken 106054 times.
✓ Branch 577 → 578 taken 174 times.
106228 for (const MailHeader &header : headers) {
558 const QJsonObject identity = {
559 212108 {QStringLiteral("folder"), path},
560 212108 {QStringLiteral("uid"), static_cast<qint64>(header.uid)},
561
2/4
✓ Branch 333 → 334 taken 212108 times.
✓ Branch 333 → 335 taken 106054 times.
✗ Branch 1912 → 1913 not taken.
✗ Branch 1912 → 1914 not taken.
318162 };
562
2/2
✓ Branch 339 → 340 taken 104400 times.
✓ Branch 339 → 347 taken 1654 times.
106054 if (storedBodies >= spec.bodies) {
563
3/3
✓ Branch 340 → 341 taken 104400 times.
✓ Branch 341 → 342 taken 28 times.
✓ Branch 341 → 346 taken 104372 times.
104400 if (bodyMissIdentities.size() < 4)
564
2/2
✓ Branch 342 → 343 taken 28 times.
✓ Branch 343 → 344 taken 28 times.
28 bodyMissIdentities.append(identity);
565 104400 continue;
566 }
567 1654 ++storedBodies;
568
3/3
✓ Branch 347 → 348 taken 1654 times.
✓ Branch 348 → 349 taken 36 times.
✓ Branch 348 → 353 taken 1618 times.
1654 if (bodyHitIdentities.size() < 4)
569
2/2
✓ Branch 349 → 350 taken 36 times.
✓ Branch 350 → 351 taken 36 times.
36 bodyHitIdentities.append(identity);
570 1654 MailBody body;
571 1654 body.uid = header.uid;
572
2/2
✓ Branch 359 → 360 taken 4 times.
✓ Branch 359 → 415 taken 1650 times.
1654 if (profileName == QStringLiteral("large-body")) {
573 4 const int payloadBytes =
574
2/2
✓ Branch 360 → 361 taken 2 times.
✓ Branch 360 → 362 taken 2 times.
4 storedBodies % 2 == 0 ? 10 * 1024 * 1024 : 1024 * 1024;
575
1/1
✓ Branch 365 → 366 taken 4 times.
4 const QString payload(payloadBytes, QLatin1Char('x'));
576
2/2
✓ Branch 366 → 367 taken 2 times.
✓ Branch 366 → 379 taken 2 times.
4 if (storedBodies <= 2) {
577 body.textPlain =
578
1/1
✓ Branch 371 → 372 taken 2 times.
6 QStringLiteral("PERF-LARGE-PLAIN-%1 ").arg(storedBodies) +
579
1/1
✓ Branch 372 → 373 taken 2 times.
2 payload;
580 } else {
581 4 body.textHtml = QStringLiteral("<html><body><p>PERF-LARGE-HTML-%1 ")
582
1/1
✓ Branch 386 → 387 taken 2 times.
4 .arg(storedBodies) +
583
2/2
✓ Branch 387 → 388 taken 2 times.
✓ Branch 388 → 389 taken 2 times.
6 payload + QStringLiteral("</p></body></html>");
584 }
585
1/1
✓ Branch 401 → 402 taken 4 times.
8 body.rawSource = QByteArray("PERF-LARGE-RAW-") +
586
4/4
✓ Branch 399 → 400 taken 4 times.
✓ Branch 400 → 401 taken 4 times.
✓ Branch 402 → 403 taken 4 times.
✓ Branch 403 → 404 taken 4 times.
16 QByteArray::number(storedBodies) + QByteArray("\r\n") +
587
2/2
✓ Branch 398 → 399 taken 4 times.
✓ Branch 404 → 405 taken 4 times.
12 QByteArray(payloadBytes, 'r');
588
2/2
✓ Branch 421 → 422 taken 50 times.
✓ Branch 421 → 455 taken 1600 times.
1654 } else if (profileName == QStringLiteral("tab-churn")) {
589
2/2
✓ Branch 422 → 423 taken 25 times.
✓ Branch 422 → 433 taken 25 times.
50 if (storedBodies % 2 == 1)
590 body.textPlain =
591
1/1
✓ Branch 427 → 428 taken 25 times.
50 QStringLiteral("PERF-TAB-PLAIN-%1").arg(storedBodies);
592 else
593 body.textHtml =
594
1/1
✓ Branch 437 → 438 taken 25 times.
50 QStringLiteral("<p>PERF-TAB-HTML-%1</p>").arg(storedBodies);
595
1/1
✓ Branch 445 → 446 taken 50 times.
100 body.rawSource = QByteArray("PERF-TAB-RAW-") +
596
4/4
✓ Branch 443 → 444 taken 50 times.
✓ Branch 444 → 445 taken 50 times.
✓ Branch 446 → 447 taken 50 times.
✓ Branch 447 → 448 taken 50 times.
150 QByteArray::number(storedBodies) + QByteArray("\r\n");
597 } else {
598 const qint64 marker = streamHeaders
599
2/2
✓ Branch 455 → 456 taken 1000 times.
✓ Branch 455 → 457 taken 600 times.
1600 ? folderGlobalStart + static_cast<qint64>(header.uid)
600 1600 : globalHeader - headers.size() + storedBodies;
601 3200 body.textPlain = QStringLiteral("PERF-BODY-%1 synthetic-search-token\n")
602
1/1
✓ Branch 463 → 464 taken 1600 times.
1600 .arg(marker);
603
2/2
✓ Branch 468 → 469 taken 400 times.
✓ Branch 468 → 479 taken 1200 times.
1600 if (storedBodies % 4 == 0) {
604 body.textHtml =
605 800 QStringLiteral("<html><body><p>PERF-HTML-%1 "
606 "synthetic-search-token</p></body></html>")
607
1/1
✓ Branch 473 → 474 taken 400 times.
800 .arg(storedBodies);
608 }
609 1600 body.rawSource = QByteArrayLiteral("Synthetic fixture MIME source\r\n");
610 }
611 // S106-215: a failed write fails the generation instead of leaving a
612 // fixture with silently missing bodies or attachments.
613
2/3
✓ Branch 485 → 486 taken 1654 times.
✗ Branch 486 → 487 not taken.
✓ Branch 486 → 501 taken 1654 times.
1654 if (!mail.storeBody(folderId, header.uid, body)) {
614 ✗ if (error)
615 ✗ *error = QStringLiteral("Cannot store a fixture body: %1").arg(mail.lastError());
616 ✗ return false;
617 }
618
2/2
✓ Branch 501 → 502 taken 17 times.
✓ Branch 501 → 564 taken 1637 times.
1654 if (header.hasAttachments) {
619 17 Attachment attachment;
620 attachment.filename =
621
1/1
✓ Branch 506 → 507 taken 17 times.
34 QStringLiteral("synthetic-%1.bin").arg(storedBodies);
622 17 attachment.contentType = QStringLiteral("application/octet-stream");
623 17 attachment.size =
624
2/2
✓ Branch 521 → 522 taken 1 time.
✓ Branch 521 → 523 taken 16 times.
17 profileName == QStringLiteral("large-body") ? 1024 * 1024 : 256;
625
5/9
✓ Branch 531 → 532 taken 17 times.
✓ Branch 534 → 535 taken 17 times.
✓ Branch 534 → 536 taken 17 times.
✓ Branch 538 → 539 taken 17 times.
✓ Branch 538 → 540 taken 17 times.
✗ Branch 2062 → 2063 not taken.
✗ Branch 2062 → 2064 not taken.
✗ Branch 2075 → 2076 not taken.
✗ Branch 2075 → 2077 not taken.
68 mail.attachments().storeAttachments(folderId, header.uid, {attachment},
626 {random.bytes(attachment.size)});
627
2/3
✓ Branch 540 → 541 taken 17 times.
✗ Branch 543 → 544 not taken.
✓ Branch 543 → 558 taken 17 times.
17 if (!mail.lastError().isEmpty()) {
628 ✗ if (error)
629 ✗ *error = QStringLiteral("Cannot store a fixture attachment: %1").arg(mail.lastError());
630 ✗ return false;
631 }
632 17 }
633 106054 }
634 174 }
635 75 }
636
5/7
✓ Branch 592 → 593 taken 9 times.
✓ Branch 592 → 597 taken 20 times.
✓ Branch 594 → 595 taken 9 times.
✗ Branch 595 → 596 not taken.
✓ Branch 595 → 597 taken 9 times.
✗ Branch 598 → 599 not taken.
✓ Branch 598 → 608 taken 29 times.
29 if (spec.headers > 0 && !mail.search().rebuildSearchIndex()) {
637 ✗ if (error)
638 ✗ *error = QStringLiteral("Cannot build deterministic FTS index");
639 ✗ return false;
640 }
641
1/1
✓ Branch 608 → 609 taken 29 times.
29 mail.close();
642
643
1/1
✓ Branch 609 → 610 taken 29 times.
29 CalendarStore calendar;
644
1/1
✓ Branch 615 → 616 taken 29 times.
29 if (!calendar.open(
645
3/4
✓ Branch 610 → 611 taken 29 times.
✓ Branch 614 → 615 taken 29 times.
✗ Branch 620 → 621 not taken.
✓ Branch 620 → 630 taken 29 times.
87 QDir(directory).filePath(QStringLiteral("calendar.sqlite")))) {
646 ✗ if (error)
647 ✗ *error = QStringLiteral("Cannot open generated calendar database");
648 ✗ return false;
649 }
650
1/2
✗ Branch 631 → 632 not taken.
✓ Branch 631 → 634 taken 23 times.
23 const int calendarCount = spec.events == 0 && spec.tasks == 0
651
2/2
✓ Branch 630 → 631 taken 23 times.
✓ Branch 630 → 632 taken 6 times.
52 ? 0
652
1/1
✓ Branch 632 → 633 taken 6 times.
6 : std::clamp(spec.folders / 6, 1, 10);
653
1/1
✓ Branch 635 → 636 taken 29 times.
29 const QStringList calendarPalette = ThemeManager::calendarPalette();
654
2/2
✓ Branch 674 → 637 taken 12 times.
✓ Branch 674 → 675 taken 29 times.
41 for (int i = 0; i < calendarCount; ++i) {
655 12 CalendarInfo info;
656
1/1
✓ Branch 641 → 642 taken 12 times.
24 info.path = QStringLiteral("/performance/calendar-%1/").arg(i + 1);
657
1/1
✓ Branch 647 → 648 taken 12 times.
12 info.accountId = QString::fromLatin1(AccountId);
658
1/1
✓ Branch 654 → 655 taken 12 times.
24 info.displayName = QStringLiteral("Synthetic calendar %1").arg(i + 1);
659 12 info.color = calendarPalette.at(i % calendarPalette.size());
660
1/1
✓ Branch 666 → 667 taken 12 times.
24 info.ctag = QStringLiteral("ctag-%1").arg(i + 1);
661
1/1
✓ Branch 671 → 672 taken 12 times.
12 calendar.upsertCalendar(info, info.accountId);
662 12 }
663
2/2
✓ Branch 675 → 676 taken 6 times.
✓ Branch 675 → 677 taken 23 times.
29 if (calendarCount > 0)
664
1/1
✓ Branch 676 → 677 taken 6 times.
6 calendar.beginTransaction();
665
2/2
✓ Branch 766 → 678 taken 600 times.
✓ Branch 766 → 767 taken 29 times.
629 for (int i = 0; i < spec.events; ++i) {
666 600 CalendarEvent event;
667
1/1
✓ Branch 683 → 684 taken 600 times.
1200 event.uid = QStringLiteral("event-%1@example.invalid").arg(i + 1);
668
1/1
✓ Branch 689 → 690 taken 600 times.
600 event.accountId = QString::fromLatin1(AccountId);
669 event.calendarPath =
670
1/1
✓ Branch 696 → 697 taken 600 times.
1200 QStringLiteral("/performance/calendar-%1/").arg(i % calendarCount + 1);
671 event.resourceHref =
672
2/2
✓ Branch 704 → 705 taken 600 times.
✓ Branch 705 → 706 taken 600 times.
600 event.calendarPath + event.uid + QStringLiteral(".ics");
673
1/1
✓ Branch 715 → 716 taken 600 times.
1200 event.summary = QStringLiteral("Synthetic event %1").arg(i + 1);
674 600 event.description = QStringLiteral("Synthetic performance fixture");
675 600 event.location = QStringLiteral("Synthetic location");
676
1/1
✓ Branch 732 → 733 taken 600 times.
600 event.dtStart = epoch.addSecs((i % 365) * 86400 + (i % 12) * 1800);
677
1/1
✓ Branch 735 → 736 taken 600 times.
600 event.dtEnd = event.dtStart.addSecs(1800 + (i % 4) * 1800);
678 600 event.allDay = i % 17 == 0;
679
2/2
✓ Branch 738 → 739 taken 90 times.
✓ Branch 738 → 746 taken 510 times.
600 if (i % 7 == 0)
680 90 event.rrule = QStringLiteral("FREQ=WEEKLY;COUNT=12");
681
2/2
✓ Branch 746 → 747 taken 18 times.
✓ Branch 746 → 753 taken 582 times.
600 if (i % 35 == 0)
682
2/4
✓ Branch 750 → 751 taken 18 times.
✓ Branch 750 → 752 taken 18 times.
✗ Branch 2195 → 2196 not taken.
✗ Branch 2195 → 2197 not taken.
36 event.exdates = {event.dtStart.addDays(7)};
683
1/1
✓ Branch 757 → 758 taken 600 times.
1200 event.etag = QStringLiteral("event-etag-%1").arg(i + 1);
684 600 event.lastModified = epoch;
685
1/1
✓ Branch 763 → 764 taken 600 times.
600 calendar.upsertEvent(event);
686 600 }
687
2/2
✓ Branch 858 → 768 taken 300 times.
✓ Branch 858 → 859 taken 29 times.
329 for (int i = 0; i < spec.tasks; ++i) {
688 300 CalendarTask task;
689
1/1
✓ Branch 773 → 774 taken 300 times.
600 task.uid = QStringLiteral("task-%1@example.invalid").arg(i + 1);
690
1/1
✓ Branch 779 → 780 taken 300 times.
300 task.accountId = QString::fromLatin1(AccountId);
691 task.calendarPath =
692
1/1
✓ Branch 786 → 787 taken 300 times.
600 QStringLiteral("/performance/calendar-%1/").arg(i % calendarCount + 1);
693
2/2
✓ Branch 794 → 795 taken 300 times.
✓ Branch 795 → 796 taken 300 times.
300 task.resourceHref = task.calendarPath + task.uid + QStringLiteral(".ics");
694
1/1
✓ Branch 805 → 806 taken 300 times.
600 task.summary = QStringLiteral("Synthetic task %1").arg(i + 1);
695 300 task.description = QStringLiteral("Synthetic performance fixture");
696
1/1
✓ Branch 816 → 817 taken 300 times.
300 task.dtStart = epoch.addDays(i % 30);
697
1/1
✓ Branch 819 → 820 taken 300 times.
300 task.due = epoch.addDays((i % 120) - 30);
698 300 task.dueAllDay = i % 5 == 0;
699 300 task.priority = i % 10;
700
2/2
✓ Branch 822 → 823 taken 78 times.
✓ Branch 822 → 826 taken 222 times.
1200 task.status = i % 4 == 0 ? QStringLiteral("COMPLETED")
701
4/4
✓ Branch 831 → 832 taken 222 times.
✓ Branch 831 → 833 taken 78 times.
✓ Branch 833 → 834 taken 78 times.
✓ Branch 833 → 835 taken 222 times.
600 : QStringLiteral("NEEDS-ACTION");
702
2/2
✓ Branch 839 → 840 taken 78 times.
✓ Branch 839 → 841 taken 222 times.
300 task.percentComplete = task.status == QStringLiteral("COMPLETED") ? 100 : 0;
703 300 task.created = epoch;
704 300 task.lastModified = epoch;
705
1/1
✓ Branch 850 → 851 taken 300 times.
600 task.etag = QStringLiteral("task-etag-%1").arg(i + 1);
706
1/1
✓ Branch 855 → 856 taken 300 times.
300 calendar.upsertTask(task);
707 300 }
708
2/2
✓ Branch 859 → 860 taken 6 times.
✓ Branch 859 → 861 taken 23 times.
29 if (calendarCount > 0)
709
1/1
✓ Branch 860 → 861 taken 6 times.
6 calendar.commitTransaction();
710
1/1
✓ Branch 861 → 862 taken 29 times.
29 calendar.close();
711
712
1/1
✓ Branch 862 → 863 taken 29 times.
29 ContactStore contacts;
713
1/1
✓ Branch 868 → 869 taken 29 times.
29 if (!contacts.open(
714
3/4
✓ Branch 863 → 864 taken 29 times.
✓ Branch 867 → 868 taken 29 times.
✗ Branch 873 → 874 not taken.
✓ Branch 873 → 883 taken 29 times.
87 QDir(directory).filePath(QStringLiteral("contacts.sqlite")))) {
715 ✗ if (error)
716 ✗ *error = QStringLiteral("Cannot open generated contacts database");
717 ✗ return false;
718 }
719
4/15
✗ Branch 887 → 888 not taken.
✓ Branch 887 → 891 taken 29 times.
✗ Branch 888 → 889 not taken.
✗ Branch 889 → 890 not taken.
✗ Branch 889 → 891 not taken.
✓ Branch 892 → 893 taken 29 times.
✗ Branch 892 → 894 not taken.
✓ Branch 894 → 895 taken 29 times.
✗ Branch 894 → 896 not taken.
✗ Branch 896 → 897 not taken.
✓ Branch 896 → 898 taken 29 times.
✗ Branch 2276 → 2277 not taken.
✗ Branch 2276 → 2278 not taken.
✗ Branch 2280 → 2281 not taken.
✗ Branch 2280 → 2282 not taken.
58 if (profileName == QStringLiteral("contacts-showcase") && !ContactShowcase::seed(contacts, error))
720 ✗ return false;
721 // Sprint 97: one card per person; every third contact lives in a
722 // CardDAV-like book, the rest on this computer; usage feeds the ranking.
723
1/1
✓ Branch 904 → 905 taken 29 times.
58 const qint64 remoteBook = contacts.ensureBook(QStringLiteral("fixture"), QStringLiteral("/dav/addressbooks/fixture/"));
724
1/1
✓ Branch 909 → 910 taken 29 times.
29 contacts.beginTransaction();
725
7/10
✓ Branch 1117 → 1118 taken 20629 times.
✗ Branch 1117 → 1120 not taken.
✓ Branch 1118 → 1119 taken 20600 times.
✓ Branch 1118 → 1120 taken 29 times.
✓ Branch 1121 → 1122 taken 20629 times.
✗ Branch 1121 → 1123 not taken.
✓ Branch 1123 → 1124 taken 20629 times.
✗ Branch 1123 → 1125 not taken.
✓ Branch 1125 → 911 taken 20600 times.
✓ Branch 1125 → 1126 taken 29 times.
41258 for (int i = 0; profileName != QStringLiteral("contacts-showcase") && i < spec.contacts; ++i) {
726
1/1
✓ Branch 911 → 912 taken 20600 times.
20600 ContactCard card;
727
2/2
✓ Branch 918 → 919 taken 20000 times.
✓ Branch 918 → 986 taken 600 times.
20600 if (profileName == QStringLiteral("contact-dense")) {
728 static const QStringList collisionNames = {
729
1/1
✓ Branch 926 → 927 taken 1 time.
2 QString(QChar(0x00c4)) + QStringLiteral("nne Alpha"),
730
1/1
✓ Branch 932 → 933 taken 1 time.
2 QString(QChar(0x00e4)) + QStringLiteral("nne alpha"),
731 1 QStringLiteral("ANNE ALPHA"),
732
1/1
✓ Branch 944 → 945 taken 1 time.
3 QStringLiteral("M") + QChar(0x00fc) + QStringLiteral("ller, Zoe"),
733
5/10
✓ Branch 919 → 920 taken 1 time.
✓ Branch 919 → 969 taken 19999 times.
✓ Branch 921 → 922 taken 1 time.
✗ Branch 921 → 969 not taken.
✓ Branch 953 → 954 taken 5 times.
✓ Branch 953 → 955 taken 1 time.
✗ Branch 2297 → 2298 not taken.
✗ Branch 2297 → 2299 not taken.
✗ Branch 2348 → 2349 not taken.
✗ Branch 2348 → 2350 not taken.
20007 QStringLiteral("Muller, Zoe")};
734 card.formattedName =
735 40000 QStringLiteral("%1 %2")
736
1/1
✓ Branch 975 → 976 taken 20000 times.
40000 .arg(collisionNames.at(i % collisionNames.size()))
737
1/1
✓ Branch 979 → 980 taken 20000 times.
40000 .arg(i / collisionNames.size(), 5, 10, QLatin1Char('0'));
738 } else {
739
1/1
✓ Branch 991 → 992 taken 600 times.
600 card.formattedName = QStringLiteral("Synthetic Contact %1").arg(i + 1, 7, 10, QLatin1Char('0'));
740 }
741
1/1
✓ Branch 1001 → 1002 taken 20600 times.
41200 card.uid = QStringLiteral("contact-uid-%1").arg(i + 1);
742 20600 ContactValue address;
743
1/1
✓ Branch 1010 → 1011 taken 20600 times.
41200 address.value = QStringLiteral("contact-%1@example.invalid").arg(i + 1);
744
2/4
✓ Branch 1018 → 1019 taken 20600 times.
✓ Branch 1018 → 1020 taken 20600 times.
✗ Branch 2390 → 2391 not taken.
✗ Branch 2390 → 2392 not taken.
41200 card.emails = {address};
745 20600 bool stored = false;
746
2/2
✓ Branch 1020 → 1021 taken 6871 times.
✓ Branch 1020 → 1054 taken 13729 times.
20600 if (i % 3 == 0) {
747 6871 StoredCard remote;
748 6871 remote.bookId = remoteBook;
749 6871 remote.uid = card.uid;
750
1/1
✓ Branch 1026 → 1027 taken 6871 times.
13742 remote.href = QStringLiteral("/dav/addressbooks/fixture/%1.vcf").arg(card.uid);
751
1/1
✓ Branch 1035 → 1036 taken 6871 times.
13742 remote.etag = QStringLiteral("\"contact-etag-%1\"").arg(i + 1);
752
1/1
✓ Branch 1044 → 1045 taken 6871 times.
13742 remote.raw = VCardCodec::create(card);
753 6871 remote.baseRaw = remote.raw;
754
1/1
✓ Branch 1051 → 1052 taken 6871 times.
6871 stored = contacts.putCard(remote) > 0;
755 6871 } else {
756
1/1
✓ Branch 1054 → 1055 taken 13729 times.
13729 stored = contacts.addLocalCard(card) > 0;
757 }
758
2/2
✓ Branch 1056 → 1057 taken 20600 times.
✓ Branch 1057 → 1058 taken 20600 times.
20600 QSqlQuery usage(contacts.database());
759
1/1
✓ Branch 1062 → 1063 taken 20600 times.
20600 usage.prepare(QStringLiteral("INSERT OR REPLACE INTO address_usage (address_key, use_count, last_used) VALUES (?, ?, ?)"));
760
1/1
✓ Branch 1067 → 1068 taken 20600 times.
20600 usage.addBindValue(address.value);
761
1/1
✓ Branch 1071 → 1072 taken 20600 times.
20600 usage.addBindValue(i % 101);
762
3/3
✓ Branch 1074 → 1075 taken 20600 times.
✓ Branch 1075 → 1076 taken 20600 times.
✓ Branch 1077 → 1078 taken 20600 times.
20600 usage.addBindValue(epoch.addDays(-(i % 365)).toString(Qt::ISODate));
763
4/7
✓ Branch 1081 → 1082 taken 20600 times.
✗ Branch 1081 → 1084 not taken.
✓ Branch 1082 → 1083 taken 20600 times.
✗ Branch 1083 → 1084 not taken.
✓ Branch 1083 → 1085 taken 20600 times.
✗ Branch 1086 → 1087 not taken.
✓ Branch 1086 → 1099 taken 20600 times.
20600 if (!stored || !usage.exec()) {
764 ✗ if (error)
765 ✗ *error = QStringLiteral("Cannot store synthetic contact %1").arg(i + 1);
766 ✗ return false;
767 }
768 20600 }
769
1/1
✓ Branch 1126 → 1127 taken 29 times.
29 contacts.commitTransaction();
770
1/1
✓ Branch 1127 → 1128 taken 29 times.
29 contacts.close();
771
772
1/1
✓ Branch 1128 → 1129 taken 29 times.
29 QDir dir(directory);
773
2/3
✓ Branch 1132 → 1133 taken 29 times.
✗ Branch 1135 → 1136 not taken.
✓ Branch 1135 → 1145 taken 29 times.
29 if (!dir.mkpath(QStringLiteral("payloads"))) {
774 ✗ if (error)
775 ✗ *error = QStringLiteral("Cannot create payload directory");
776 ✗ return false;
777 }
778
1/1
✓ Branch 1145 → 1146 taken 29 times.
29 const QByteArray smallPayload = random.bytes(4096);
779
1/1
✓ Branch 1146 → 1147 taken 29 times.
29 const QByteArray largePayload = random.bytes(1024 * 1024);
780
1/1
✓ Branch 1147 → 1148 taken 29 times.
29 const QByteArray pdfPayload = minimalPdf();
781
4/13
✓ Branch 1151 → 1152 taken 29 times.
✓ Branch 1182 → 1183 taken 29 times.
✗ Branch 1182 → 1184 not taken.
✓ Branch 1184 → 1185 taken 29 times.
✗ Branch 1184 → 1186 not taken.
✗ Branch 1186 → 1187 not taken.
✓ Branch 1186 → 1188 taken 29 times.
✗ Branch 2500 → 2501 not taken.
✗ Branch 2500 → 2502 not taken.
✗ Branch 2504 → 2505 not taken.
✗ Branch 2504 → 2506 not taken.
✗ Branch 2508 → 2509 not taken.
✗ Branch 2508 → 2510 not taken.
58 if (!writeBytes(dir.filePath(QStringLiteral("payloads/small.bin")),
782 58 smallPayload, error) ||
783
6/16
✓ Branch 1157 → 1158 taken 29 times.
✓ Branch 1158 → 1159 taken 29 times.
✓ Branch 1159 → 1160 taken 29 times.
✗ Branch 1159 → 1166 not taken.
✓ Branch 1176 → 1177 taken 29 times.
✗ Branch 1176 → 1178 not taken.
✓ Branch 1178 → 1179 taken 29 times.
✗ Branch 1178 → 1180 not taken.
✓ Branch 1180 → 1181 taken 29 times.
✗ Branch 1180 → 1182 not taken.
✗ Branch 2488 → 2489 not taken.
✗ Branch 2488 → 2490 not taken.
✗ Branch 2492 → 2493 not taken.
✗ Branch 2492 → 2494 not taken.
✗ Branch 2496 → 2497 not taken.
✗ Branch 2496 → 2498 not taken.
58 !writeBytes(dir.filePath(QStringLiteral("payloads/large.bin")),
784
2/3
✓ Branch 1152 → 1153 taken 29 times.
✓ Branch 1153 → 1154 taken 29 times.
✗ Branch 1153 → 1166 not taken.
87 largePayload, error) ||
785
7/18
✓ Branch 1163 → 1164 taken 29 times.
✓ Branch 1164 → 1165 taken 29 times.
✗ Branch 1165 → 1166 not taken.
✓ Branch 1165 → 1167 taken 29 times.
✓ Branch 1168 → 1169 taken 29 times.
✗ Branch 1168 → 1170 not taken.
✓ Branch 1170 → 1171 taken 29 times.
✗ Branch 1170 → 1172 not taken.
✓ Branch 1172 → 1173 taken 29 times.
✗ Branch 1172 → 1174 not taken.
✓ Branch 1174 → 1175 taken 29 times.
✗ Branch 1174 → 1176 not taken.
✗ Branch 2476 → 2477 not taken.
✗ Branch 2476 → 2478 not taken.
✗ Branch 2480 → 2481 not taken.
✗ Branch 2480 → 2482 not taken.
✗ Branch 2484 → 2485 not taken.
✗ Branch 2484 → 2486 not taken.
58 !writeBytes(dir.filePath(QStringLiteral("payloads/sample.pdf")),
786 pdfPayload, error))
787 ✗ return false;
788
789
1/1
✓ Branch 1188 → 1189 taken 29 times.
29 const QJsonObject workloads = workloadManifest(spec);
790
4/13
✓ Branch 1192 → 1193 taken 29 times.
✓ Branch 1201 → 1202 taken 29 times.
✗ Branch 1201 → 1203 not taken.
✓ Branch 1203 → 1204 taken 29 times.
✗ Branch 1203 → 1205 not taken.
✗ Branch 1205 → 1206 not taken.
✓ Branch 1205 → 1207 taken 29 times.
✗ Branch 2512 → 2513 not taken.
✗ Branch 2512 → 2514 not taken.
✗ Branch 2516 → 2517 not taken.
✗ Branch 2516 → 2518 not taken.
✗ Branch 2520 → 2521 not taken.
✗ Branch 2520 → 2522 not taken.
58 if (!writeJson(dir.filePath(QStringLiteral("workloads.json")), workloads,
791
3/5
✓ Branch 1193 → 1194 taken 29 times.
✓ Branch 1194 → 1195 taken 29 times.
✗ Branch 1194 → 1197 not taken.
✓ Branch 1199 → 1200 taken 29 times.
✗ Branch 1199 → 1201 not taken.
87 error) ||
792
2/3
✓ Branch 1195 → 1196 taken 29 times.
✗ Branch 1196 → 1197 not taken.
✓ Branch 1196 → 1198 taken 29 times.
29 !createMigrationSnapshots(directory, error))
793 ✗ return false;
794
795 QJsonObject logical = {
796 58 {QStringLiteral("generator_schema_version"), GeneratorSchemaVersion},
797 58 {QStringLiteral("profile"), spec.name},
798 58 {QStringLiteral("seed"), static_cast<qint64>(seed)},
799 58 {QStringLiteral("database_schemas"),
800 58 QJsonObject{{QStringLiteral("mail"), MailCache::SchemaVersion},
801 58 {QStringLiteral("calendar"), CalendarStore::SchemaVersion},
802
2/4
✓ Branch 1455 → 1456 taken 87 times.
✓ Branch 1455 → 1457 taken 29 times.
✗ Branch 2795 → 2796 not taken.
✗ Branch 2795 → 2797 not taken.
203 {QStringLiteral("contacts"), QStringLiteral("current")}}},
803 58 {QStringLiteral("counts"),
804 58 QJsonObject{{QStringLiteral("folders"), spec.folders},
805 58 {QStringLiteral("headers"), spec.headers},
806 58 {QStringLiteral("cached_bodies"), spec.bodies},
807 58 {QStringLiteral("events"), spec.events},
808 58 {QStringLiteral("tasks"), spec.tasks},
809
2/4
✓ Branch 1437 → 1438 taken 174 times.
✓ Branch 1437 → 1439 taken 29 times.
✗ Branch 2740 → 2741 not taken.
✗ Branch 2740 → 2742 not taken.
261 {QStringLiteral("contacts"), spec.contacts}}},
810 58 {QStringLiteral("known_markers"),
811 87 QJsonObject{{QStringLiteral("mail_body"), QStringLiteral("PERF-BODY-1")},
812 87 {QStringLiteral("html_body"), QStringLiteral("PERF-HTML-4")},
813 58 {QStringLiteral("search"),
814
2/4
✓ Branch 1419 → 1420 taken 87 times.
✓ Branch 1419 → 1421 taken 29 times.
✗ Branch 2685 → 2686 not taken.
✗ Branch 2685 → 2687 not taken.
174 QStringLiteral("synthetic-search-token")}}},
815 58 {QStringLiteral("cache_contract"),
816 ✗ QJsonObject{
817 58 {QStringLiteral("body_hit_count"), spec.bodies},
818 58 {QStringLiteral("body_miss_count"), spec.headers - spec.bodies},
819 58 {QStringLiteral("known_body_hits"), bodyHitIdentities},
820
2/4
✓ Branch 1405 → 1406 taken 116 times.
✓ Branch 1405 → 1407 taken 29 times.
✗ Branch 2641 → 2642 not taken.
✗ Branch 2641 → 2643 not taken.
203 {QStringLiteral("known_body_misses"), bodyMissIdentities}}},
821 58 {QStringLiteral("payloads"),
822 58 QJsonObject{{QStringLiteral("small_sha256"),
823
2/2
✓ Branch 1321 → 1322 taken 29 times.
✓ Branch 1322 → 1323 taken 29 times.
58 QString::fromLatin1(sha256(smallPayload))},
824 58 {QStringLiteral("large_sha256"),
825
2/2
✓ Branch 1327 → 1328 taken 29 times.
✓ Branch 1328 → 1329 taken 29 times.
58 QString::fromLatin1(sha256(largePayload))},
826 58 {QStringLiteral("pdf_sha256"),
827
4/6
✓ Branch 1333 → 1334 taken 29 times.
✓ Branch 1334 → 1335 taken 29 times.
✓ Branch 1387 → 1388 taken 87 times.
✓ Branch 1387 → 1389 taken 29 times.
✗ Branch 2586 → 2587 not taken.
✗ Branch 2586 → 2588 not taken.
174 QString::fromLatin1(sha256(pdfPayload))}}},
828 58 {QStringLiteral("workload_sha256"),
829
1/1
✓ Branch 1344 → 1345 taken 29 times.
29 QString::fromLatin1(
830
3/3
✓ Branch 1341 → 1342 taken 29 times.
✓ Branch 1342 → 1343 taken 29 times.
✓ Branch 1343 → 1344 taken 29 times.
58 sha256(QJsonDocument(workloads).toJson(QJsonDocument::Compact)))},
831 58 {QStringLiteral("migration_snapshots"),
832 116 QJsonArray{QStringLiteral("mail-v16"), QStringLiteral("calendar-v4"),
833
2/4
✓ Branch 1369 → 1370 taken 87 times.
✓ Branch 1369 → 1371 taken 29 times.
✗ Branch 2531 → 2532 not taken.
✗ Branch 2531 → 2533 not taken.
174 QStringLiteral("contacts-legacy")}},
834
2/4
✓ Branch 1365 → 1366 taken 290 times.
✓ Branch 1365 → 1367 taken 29 times.
✗ Branch 2525 → 2526 not taken.
✗ Branch 2525 → 2527 not taken.
319 };
835
2/2
✓ Branch 1473 → 1474 taken 1 time.
✓ Branch 1473 → 1534 taken 28 times.
29 if (streamHeaders) {
836
2/2
✓ Branch 1474 → 1475 taken 1 time.
✓ Branch 1478 → 1479 taken 1 time.
2 logical.insert(QStringLiteral("setup_header_batch_limit"), 1000);
837
1/1
✓ Branch 1512 → 1513 taken 1 time.
2 logical.insert(QStringLiteral("last_header_identity"),
838
1/1
✓ Branch 1486 → 1487 taken 1 time.
3 QJsonObject{{QStringLiteral("account"), QString::fromLatin1(AccountId)},
839
1/1
✓ Branch 1491 → 1492 taken 1 time.
2 {QStringLiteral("folder"), folderName(spec.folders - 1)},
840 2 {QStringLiteral("uid"), spec.headers / spec.folders},
841
3/5
✓ Branch 1505 → 1506 taken 1 time.
✓ Branch 1518 → 1519 taken 4 times.
✓ Branch 1518 → 1520 taken 1 time.
✗ Branch 2880 → 2881 not taken.
✗ Branch 2880 → 2882 not taken.
8 {QStringLiteral("subject"), QStringLiteral("Synthetic mail %1").arg(spec.headers, 7, 10, QLatin1Char('0'))}});
842 }
843
2/2
✓ Branch 1536 → 1537 taken 1 time.
✓ Branch 1536 → 1617 taken 28 times.
29 if (profileName == QLatin1String("thread-dense")) {
844
2/2
✓ Branch 1540 → 1541 taken 1 time.
✓ Branch 1541 → 1542 taken 1 time.
1 QSaveFile oracle(dir.filePath(QStringLiteral("thread-oracle.jsonl")));
845
2/3
✓ Branch 1546 → 1547 taken 1 time.
✗ Branch 1547 → 1548 not taken.
✓ Branch 1547 → 1557 taken 1 time.
1 if (!oracle.open(QIODevice::WriteOnly)) {
846 ✗ if (error)
847 ✗ *error = QStringLiteral("Cannot open thread oracle");
848 ✗ return false;
849 }
850
2/2
✓ Branch 1599 → 1558 taken 100000 times.
✓ Branch 1599 → 1600 taken 1 time.
100001 for (qint64 uid = 1; uid <= spec.headers; ++uid) {
851 100000 const auto row = QJsonDocument(QJsonObject{
852 200000 {QStringLiteral("uid"), uid},
853
5/7
✓ Branch 1568 → 1569 taken 100000 times.
✓ Branch 1569 → 1570 taken 100000 times.
✓ Branch 1570 → 1571 taken 100000 times.
✓ Branch 1575 → 1576 taken 200000 times.
✓ Branch 1575 → 1577 taken 100000 times.
✗ Branch 2951 → 2952 not taken.
✗ Branch 2951 → 2953 not taken.
500000 {QStringLiteral("parent_uid"), threadFixtureParent(uid)}}).toJson(QJsonDocument::Compact) + '\n';
854
2/3
✓ Branch 1581 → 1582 taken 100000 times.
✗ Branch 1583 → 1584 not taken.
✓ Branch 1583 → 1593 taken 100000 times.
100000 if (oracle.write(row) != row.size()) {
855 ✗ if (error)
856 ✗ *error = QStringLiteral("Cannot write thread oracle");
857 ✗ return false;
858 }
859 100000 }
860
2/3
✓ Branch 1600 → 1601 taken 1 time.
✗ Branch 1601 → 1602 not taken.
✓ Branch 1601 → 1611 taken 1 time.
1 if (!oracle.commit()) {
861 ✗ if (error)
862 ✗ *error = QStringLiteral("Cannot publish thread oracle");
863 ✗ return false;
864 }
865 1 }
866
1/1
✓ Branch 1617 → 1618 taken 29 times.
29 const QJsonObject artifacts = artifactManifest(directory);
867 const QJsonObject manifest = {
868 58 {QStringLiteral("schema_version"), GeneratorSchemaVersion},
869 58 {QStringLiteral("logical"), logical},
870 58 {QStringLiteral("logical_sha256"),
871
2/2
✓ Branch 1629 → 1630 taken 29 times.
✓ Branch 1630 → 1631 taken 29 times.
58 QString::fromLatin1(logicalChecksum(logical))},
872 58 {QStringLiteral("artifacts"), artifacts},
873 58 {QStringLiteral("generation_duration_ns"),
874 29 generationTimer.nsecsElapsed()},
875
1/1
✓ Branch 1644 → 1645 taken 29 times.
58 {QStringLiteral("disk_bytes"), artifactBytes(artifacts)},
876
2/4
✓ Branch 1648 → 1649 taken 174 times.
✓ Branch 1648 → 1650 taken 29 times.
✗ Branch 2984 → 2985 not taken.
✗ Branch 2984 → 2986 not taken.
232 };
877
3/4
✓ Branch 1667 → 1668 taken 29 times.
✓ Branch 1668 → 1669 taken 29 times.
✗ Branch 1672 → 1673 not taken.
✓ Branch 1672 → 1674 taken 29 times.
29 if (!writeJson(dir.filePath(QStringLiteral("manifest.json")), manifest,
878 error))
879 ✗ return false;
880
1/1
✓ Branch 1674 → 1675 taken 29 times.
29 return verify(directory, error);
881 982588 }
882
883 77 bool FixtureGenerator::verify(const QString &profileDirectory,
884 QString *error) const {
885
1/1
✓ Branch 2 → 3 taken 77 times.
77 QDir dir(profileDirectory);
886
2/2
✓ Branch 6 → 7 taken 77 times.
✓ Branch 7 → 8 taken 77 times.
77 QFile manifestFile(dir.filePath(QStringLiteral("manifest.json")));
887
3/3
✓ Branch 12 → 13 taken 77 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 23 taken 75 times.
77 if (!manifestFile.open(QIODevice::ReadOnly)) {
888
2/2
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 22 taken 1 time.
2 if (error)
889 1 *error = QStringLiteral("Fixture manifest is missing");
890 2 return false;
891 }
892 75 QJsonParseError parseError;
893 const QJsonDocument document =
894
2/2
✓ Branch 23 → 24 taken 75 times.
✓ Branch 24 → 25 taken 75 times.
75 QJsonDocument::fromJson(manifestFile.readAll(), &parseError);
895
6/7
✓ Branch 26 → 27 taken 73 times.
✓ Branch 26 → 29 taken 2 times.
✓ Branch 27 → 28 taken 73 times.
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 73 times.
✓ Branch 31 → 32 taken 2 times.
✓ Branch 31 → 41 taken 73 times.
75 if (parseError.error != QJsonParseError::NoError || !document.isObject()) {
896
2/2
✓ Branch 32 → 33 taken 1 time.
✓ Branch 32 → 40 taken 1 time.
2 if (error)
897 1 *error = QStringLiteral("Fixture manifest is malformed");
898 2 return false;
899 }
900
1/1
✓ Branch 41 → 42 taken 73 times.
73 const QJsonObject manifest = document.object();
901
4/4
✓ Branch 45 → 46 taken 73 times.
✓ Branch 46 → 47 taken 73 times.
✓ Branch 50 → 51 taken 2 times.
✓ Branch 50 → 60 taken 71 times.
73 if (manifest.value(QStringLiteral("schema_version")).toInt() !=
902 GeneratorSchemaVersion) {
903
2/2
✓ Branch 51 → 52 taken 1 time.
✓ Branch 51 → 59 taken 1 time.
2 if (error)
904 1 *error = QStringLiteral("Fixture schema version is stale or unknown");
905 2 return false;
906 }
907 const QJsonObject logical =
908
2/2
✓ Branch 63 → 64 taken 71 times.
✓ Branch 64 → 65 taken 71 times.
71 manifest.value(QStringLiteral("logical")).toObject();
909
2/2
✓ Branch 73 → 74 taken 71 times.
✓ Branch 74 → 75 taken 71 times.
142 if (QString::fromLatin1(logicalChecksum(logical)) !=
910
4/4
✓ Branch 71 → 72 taken 71 times.
✓ Branch 72 → 73 taken 71 times.
✓ Branch 82 → 83 taken 2 times.
✓ Branch 82 → 92 taken 69 times.
213 manifest.value(QStringLiteral("logical_sha256")).toString()) {
911
2/2
✓ Branch 83 → 84 taken 1 time.
✓ Branch 83 → 91 taken 1 time.
2 if (error)
912 1 *error = QStringLiteral("Fixture logical checksum does not match");
913 2 return false;
914 }
915 const FixtureProfile spec =
916
3/3
✓ Branch 95 → 96 taken 69 times.
✓ Branch 96 → 97 taken 69 times.
✓ Branch 97 → 98 taken 69 times.
69 profile(logical.value(QStringLiteral("profile")).toString());
917
3/3
✓ Branch 102 → 103 taken 69 times.
✓ Branch 105 → 106 taken 2 times.
✓ Branch 105 → 115 taken 67 times.
69 if (!profileNames().contains(spec.name)) {
918
2/2
✓ Branch 106 → 107 taken 1 time.
✓ Branch 106 → 114 taken 1 time.
2 if (error)
919 1 *error = QStringLiteral("Fixture profile is unknown");
920 2 return false;
921 }
922 const QJsonObject artifacts =
923
2/2
✓ Branch 118 → 119 taken 67 times.
✓ Branch 119 → 120 taken 67 times.
67 manifest.value(QStringLiteral("artifacts")).toObject();
924 const QStringList requiredArtifacts = {
925 ✗ QStringLiteral("mail.sqlite"),
926 67 QStringLiteral("calendar.sqlite"),
927 67 QStringLiteral("contacts.sqlite"),
928 67 QStringLiteral("workloads.json"),
929 67 QStringLiteral("payloads/small.bin"),
930 67 QStringLiteral("payloads/large.bin"),
931 67 QStringLiteral("payloads/sample.pdf"),
932 67 QStringLiteral("migrations/mail-v16.sqlite"),
933 67 QStringLiteral("migrations/calendar-v4.sqlite"),
934 67 QStringLiteral("migrations/contacts-legacy.sqlite"),
935
2/4
✓ Branch 155 → 156 taken 670 times.
✓ Branch 155 → 157 taken 67 times.
✗ Branch 534 → 535 not taken.
✗ Branch 534 → 536 not taken.
737 };
936
2/2
✓ Branch 186 → 169 taken 667 times.
✓ Branch 186 → 187 taken 66 times.
733 for (const QString &required : requiredArtifacts) {
937
3/3
✓ Branch 170 → 171 taken 667 times.
✓ Branch 171 → 172 taken 1 time.
✓ Branch 171 → 184 taken 666 times.
667 if (!artifacts.contains(required)) {
938
1/2
✓ Branch 172 → 173 taken 1 time.
✗ Branch 172 → 183 not taken.
1 if (error)
939 *error =
940 2 QStringLiteral("Fixture artifact is absent from the manifest: %1")
941
1/1
✓ Branch 177 → 178 taken 1 time.
2 .arg(required);
942 1 return false;
943 }
944 }
945
3/3
✓ Branch 261 → 262 taken 676 times.
✓ Branch 263 → 188 taken 618 times.
✓ Branch 263 → 264 taken 58 times.
676 for (auto it = artifacts.constBegin(); it != artifacts.constEnd(); ++it) {
946
2/2
✓ Branch 188 → 189 taken 618 times.
✓ Branch 189 → 190 taken 618 times.
618 const QString path = dir.filePath(it.key());
947
1/1
✓ Branch 192 → 193 taken 618 times.
618 const QJsonObject expected = it.value().toObject();
948
1/1
✓ Branch 193 → 194 taken 618 times.
618 QFileInfo info(path);
949 3086 if (!info.isFile() ||
950
14/20
✓ Branch 194 → 195 taken 618 times.
✓ Branch 195 → 196 taken 616 times.
✓ Branch 195 → 212 taken 2 times.
✓ Branch 196 → 197 taken 616 times.
✓ Branch 200 → 201 taken 616 times.
✓ Branch 201 → 202 taken 616 times.
✓ Branch 202 → 203 taken 612 times.
✓ Branch 202 → 212 taken 4 times.
✓ Branch 228 → 229 taken 616 times.
✓ Branch 228 → 230 taken 2 times.
✓ Branch 230 → 231 taken 616 times.
✓ Branch 230 → 232 taken 2 times.
✓ Branch 232 → 233 taken 8 times.
✓ Branch 232 → 247 taken 610 times.
✗ Branch 610 → 611 not taken.
✗ Branch 610 → 612 not taken.
✗ Branch 614 → 615 not taken.
✗ Branch 614 → 616 not taken.
✗ Branch 618 → 619 not taken.
✗ Branch 618 → 620 not taken.
2464 info.size() != expected.value(QStringLiteral("bytes")).toInteger() ||
951
8/12
✓ Branch 208 → 209 taken 612 times.
✓ Branch 209 → 210 taken 612 times.
✓ Branch 211 → 212 taken 2 times.
✓ Branch 211 → 213 taken 610 times.
✓ Branch 214 → 215 taken 612 times.
✓ Branch 214 → 216 taken 6 times.
✓ Branch 216 → 217 taken 612 times.
✓ Branch 216 → 218 taken 6 times.
✗ Branch 586 → 587 not taken.
✗ Branch 586 → 588 not taken.
✗ Branch 590 → 591 not taken.
✗ Branch 590 → 592 not taken.
1842 QString::fromLatin1(fileSha256(path)) !=
952
12/20
✓ Branch 206 → 207 taken 612 times.
✓ Branch 207 → 208 taken 612 times.
✓ Branch 218 → 219 taken 612 times.
✓ Branch 218 → 220 taken 6 times.
✓ Branch 220 → 221 taken 612 times.
✓ Branch 220 → 222 taken 6 times.
✓ Branch 222 → 223 taken 612 times.
✓ Branch 222 → 224 taken 6 times.
✓ Branch 224 → 225 taken 612 times.
✓ Branch 224 → 226 taken 6 times.
✓ Branch 226 → 227 taken 616 times.
✓ Branch 226 → 228 taken 2 times.
✗ Branch 594 → 595 not taken.
✗ Branch 594 → 596 not taken.
✗ Branch 598 → 599 not taken.
✗ Branch 598 → 600 not taken.
✗ Branch 602 → 603 not taken.
✗ Branch 602 → 604 not taken.
✗ Branch 606 → 607 not taken.
✗ Branch 606 → 608 not taken.
1842 expected.value(QStringLiteral("sha256")).toString()) {
953
2/2
✓ Branch 233 → 234 taken 4 times.
✓ Branch 233 → 246 taken 4 times.
8 if (error)
954 8 *error = QStringLiteral("Fixture artifact is missing or corrupt: %1")
955
2/2
✓ Branch 238 → 239 taken 4 times.
✓ Branch 239 → 240 taken 4 times.
8 .arg(it.key());
956 8 return false;
957 }
958 634 }
959
960
1/1
✓ Branch 264 → 265 taken 58 times.
58 MailCache mail;
961
4/4
✓ Branch 268 → 269 taken 58 times.
✓ Branch 269 → 270 taken 58 times.
✓ Branch 273 → 274 taken 4 times.
✓ Branch 273 → 280 taken 54 times.
58 if (!mail.open(dir.filePath(QStringLiteral("mail.sqlite")))) {
962
2/2
✓ Branch 274 → 275 taken 2 times.
✓ Branch 274 → 279 taken 2 times.
4 if (error)
963
1/1
✓ Branch 275 → 276 taken 2 times.
2 *error = mail.lastError();
964 4 return false;
965 }
966 const QStringList folders =
967
2/2
✓ Branch 281 → 282 taken 54 times.
✓ Branch 282 → 283 taken 54 times.
54 mail.allFolderPaths(QString::fromLatin1(AccountId));
968 54 int headers = 0;
969 54 int bodies = 0;
970
2/2
✓ Branch 299 → 286 taken 162 times.
✓ Branch 299 → 300 taken 54 times.
216 for (const QString &path : folders) {
971
2/2
✓ Branch 289 → 290 taken 162 times.
✓ Branch 290 → 291 taken 162 times.
162 headers += mail.payloadLimits().cachedHeaderCount(QString::fromLatin1(AccountId), path);
972
2/2
✓ Branch 294 → 295 taken 162 times.
✓ Branch 295 → 296 taken 162 times.
162 bodies += mail.payloadLimits().cachedBodyCount(QString::fromLatin1(AccountId), path);
973 }
974
2/2
✓ Branch 300 → 301 taken 17 times.
✓ Branch 300 → 308 taken 37 times.
71 const bool ftsOk = spec.headers == 0 ||
975
9/15
✓ Branch 305 → 306 taken 17 times.
✓ Branch 307 → 308 taken 16 times.
✓ Branch 307 → 309 taken 1 time.
✓ Branch 310 → 311 taken 17 times.
✓ Branch 310 → 312 taken 37 times.
✓ Branch 312 → 313 taken 17 times.
✓ Branch 312 → 314 taken 37 times.
✓ Branch 314 → 315 taken 17 times.
✓ Branch 314 → 316 taken 37 times.
✗ Branch 665 → 666 not taken.
✗ Branch 665 → 667 not taken.
✗ Branch 669 → 670 not taken.
✗ Branch 669 → 671 not taken.
✗ Branch 673 → 674 not taken.
✗ Branch 673 → 675 not taken.
88 !mail.search().searchFts(QStringLiteral("synthetic"), 1).isEmpty();
976
1/1
✓ Branch 316 → 317 taken 54 times.
54 mail.close();
977
2/2
✓ Branch 319 → 320 taken 51 times.
✓ Branch 319 → 322 taken 1 time.
106 if (folders.size() != spec.folders || headers != spec.headers ||
978
8/8
✓ Branch 318 → 319 taken 52 times.
✓ Branch 318 → 322 taken 2 times.
✓ Branch 320 → 321 taken 50 times.
✓ Branch 320 → 322 taken 1 time.
✓ Branch 321 → 322 taken 1 time.
✓ Branch 321 → 323 taken 49 times.
✓ Branch 324 → 325 taken 5 times.
✓ Branch 324 → 334 taken 49 times.
106 bodies != spec.bodies || !ftsOk) {
979
2/2
✓ Branch 325 → 326 taken 3 times.
✓ Branch 325 → 333 taken 2 times.
5 if (error)
980 3 *error = QStringLiteral(
981 "Generated mail database failed API count verification");
982 5 return false;
983 }
984
4/4
✓ Branch 337 → 338 taken 49 times.
✓ Branch 338 → 339 taken 49 times.
✓ Branch 342 → 343 taken 4 times.
✓ Branch 342 → 344 taken 45 times.
49 if (!validateSqlite(dir.filePath(QStringLiteral("mail.sqlite")),
985 49 MailCache::SchemaVersion, spec.headers, error))
986 4 return false;
987
988
1/1
✓ Branch 344 → 345 taken 45 times.
45 CalendarStore calendar;
989
3/4
✓ Branch 348 → 349 taken 45 times.
✓ Branch 349 → 350 taken 45 times.
✗ Branch 353 → 354 not taken.
✓ Branch 353 → 363 taken 45 times.
45 if (!calendar.open(dir.filePath(QStringLiteral("calendar.sqlite")))) {
990 ✗ if (error)
991 ✗ *error = QStringLiteral("Generated calendar database cannot be reopened");
992 ✗ return false;
993 }
994 const auto events = calendar.eventsForDateRange(
995
4/4
✓ Branch 367 → 368 taken 45 times.
✓ Branch 368 → 369 taken 45 times.
✓ Branch 369 → 370 taken 45 times.
✓ Branch 370 → 371 taken 45 times.
90 QDateTime(QDate(2000, 1, 1), QTime(0, 0), QTimeZone::utc()),
996
5/5
✓ Branch 363 → 364 taken 45 times.
✓ Branch 364 → 365 taken 45 times.
✓ Branch 365 → 366 taken 45 times.
✓ Branch 366 → 367 taken 45 times.
✓ Branch 371 → 372 taken 45 times.
135 QDateTime(QDate(2100, 1, 1), QTime(0, 0), QTimeZone::utc()));
997
1/1
✓ Branch 376 → 377 taken 45 times.
45 const int taskCount = calendar.allTasks().size();
998
1/1
✓ Branch 379 → 380 taken 45 times.
45 calendar.close();
999
6/6
✓ Branch 381 → 382 taken 44 times.
✓ Branch 381 → 383 taken 1 time.
✓ Branch 382 → 383 taken 1 time.
✓ Branch 382 → 384 taken 43 times.
✓ Branch 385 → 386 taken 2 times.
✓ Branch 385 → 395 taken 43 times.
45 if (events.size() != spec.events || taskCount != spec.tasks) {
1000
2/2
✓ Branch 386 → 387 taken 1 time.
✓ Branch 386 → 394 taken 1 time.
2 if (error)
1001 1 *error = QStringLiteral(
1002 "Generated calendar database failed API count verification");
1003 2 return false;
1004 }
1005
4/4
✓ Branch 398 → 399 taken 43 times.
✓ Branch 399 → 400 taken 43 times.
✓ Branch 403 → 404 taken 2 times.
✓ Branch 403 → 405 taken 41 times.
43 if (!validateSqlite(dir.filePath(QStringLiteral("calendar.sqlite")),
1006 CalendarStore::SchemaVersion, -1, error))
1007 2 return false;
1008
1009
1/1
✓ Branch 405 → 406 taken 41 times.
41 ContactStore contacts;
1010
3/4
✓ Branch 409 → 410 taken 41 times.
✓ Branch 410 → 411 taken 41 times.
✗ Branch 414 → 415 not taken.
✓ Branch 414 → 424 taken 41 times.
41 if (!contacts.open(dir.filePath(QStringLiteral("contacts.sqlite")))) {
1011 ✗ if (error)
1012 ✗ *error = QStringLiteral("Generated contact database cannot be reopened");
1013 ✗ return false;
1014 }
1015
2/2
✓ Branch 424 → 425 taken 41 times.
✓ Branch 425 → 426 taken 41 times.
41 const int contactCount = ContactQueries::allCount(contacts.database());
1016
1/1
✓ Branch 427 → 428 taken 41 times.
41 contacts.close();
1017
2/2
✓ Branch 428 → 429 taken 2 times.
✓ Branch 428 → 438 taken 39 times.
41 if (contactCount != spec.contacts) {
1018
2/2
✓ Branch 429 → 430 taken 1 time.
✓ Branch 429 → 437 taken 1 time.
2 if (error)
1019 1 *error = QStringLiteral(
1020 "Generated contact database failed API count verification");
1021 2 return false;
1022 }
1023
3/4
✓ Branch 441 → 442 taken 39 times.
✓ Branch 442 → 443 taken 39 times.
✗ Branch 446 → 447 not taken.
✓ Branch 446 → 448 taken 39 times.
39 if (!validateSqlite(dir.filePath(QStringLiteral("contacts.sqlite")), -1, -1,
1024 error))
1025 ✗ return false;
1026 39 return true;
1027 814 }
1028
1029 } // namespace MailJD::Performance
1030