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: 94.5% 172 / 0 / 182
Functions: 100.0% 15 / 0 / 15
Branches: 77.6% 302 / 0 / 389

presentation/QuickContactVcf.cpp
Line Branch Exec Source
1 #include "QuickContactVcf.h"
2
3 #include "app/core/ContactOutbox.h"
4 #include "app/core/ContactSyncCoordinator.h"
5 #include "app/core/MailSessionCore.h"
6 #include "data/ContactIndex.h"
7 #include "data/ContactQueries.h"
8 #include "data/ContactStore.h"
9 #include "data/MailCache.h"
10 #include "data/VCardCodec.h"
11 #include "service/MailPayloadExportService.h"
12
13 #include <algorithm>
14
15 #include <QDateTime>
16 #include <QDir>
17 #include <QFile>
18 #include <QFileInfo>
19 #include <QLoggingCategory>
20 #include <QSaveFile>
21 #include <QStandardPaths>
22
23
4/7
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 8 taken 38 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
40 Q_LOGGING_CATEGORY(lcContactVcf, "mailjd.contacts.vcf")
24
25 802 QuickContactVcf::QuickContactVcf(MailSessionCore &session, Deps deps, QObject *parent)
26 802 : QObject(parent), m_session(session), m_deps(std::move(deps)) {}
27
28 50 ContactSyncCoordinator *QuickContactVcf::coordinator() const { return m_session.contactSync(); }
29
30 13 QString QuickContactVcf::fileName(const QString &title) {
31 13 QString name;
32
6/6
✓ Branch 2 → 3 taken 13 times.
✓ Branch 3 → 4 taken 13 times.
✓ Branch 5 → 6 taken 13 times.
✓ Branch 6 → 7 taken 13 times.
✓ Branch 24 → 8 taken 101 times.
✓ Branch 24 → 25 taken 13 times.
114 for (const QChar c : title.trimmed().left(80))
33
8/15
✓ Branch 9 → 10 taken 101 times.
✗ Branch 9 → 16 not taken.
✓ Branch 13 → 14 taken 101 times.
✓ Branch 14 → 15 taken 96 times.
✓ Branch 14 → 16 taken 5 times.
✓ Branch 17 → 18 taken 101 times.
✗ Branch 17 → 19 not taken.
✓ Branch 19 → 20 taken 101 times.
✗ Branch 19 → 21 not taken.
✓ Branch 21 → 22 taken 96 times.
✓ Branch 21 → 23 taken 5 times.
✗ Branch 64 → 65 not taken.
✗ Branch 64 → 66 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 68 → 70 not taken.
202 if (c.category() != QChar::Other_Control && !QStringLiteral("/\\:*?\"<>|").contains(c))
34
1/1
✓ Branch 22 → 23 taken 96 times.
109 name += c;
35
7/8
✓ Branch 30 → 31 taken 15 times.
✓ Branch 31 → 32 taken 13 times.
✓ Branch 31 → 36 taken 2 times.
✓ Branch 34 → 35 taken 13 times.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 13 times.
✓ Branch 38 → 27 taken 2 times.
✓ Branch 38 → 39 taken 13 times.
15 while (name.startsWith(QLatin1Char('.')) || name.startsWith(QLatin1Char(' ')))
36
1/1
✓ Branch 27 → 28 taken 2 times.
2 name.remove(0, 1);
37
1/1
✓ Branch 39 → 40 taken 13 times.
13 name = name.trimmed();
38
5/7
✓ Branch 46 → 47 taken 1 time.
✓ Branch 46 → 50 taken 12 times.
✓ Branch 51 → 52 taken 13 times.
✓ Branch 54 → 55 taken 1 time.
✓ Branch 54 → 56 taken 12 times.
✗ Branch 82 → 83 not taken.
✗ Branch 82 → 84 not taken.
27 return (name.isEmpty() ? QStringLiteral("contacts") : name) + QStringLiteral(".vcf");
39 13 }
40
41 2327 int QuickContactVcf::duplicateCount() const {
42 2327 int count = 0;
43
2/2
✓ Branch 24 → 4 taken 53 times.
✓ Branch 24 → 25 taken 2327 times.
2380 for (const QVariant &item : m_items)
44
5/5
✓ Branch 5 → 6 taken 53 times.
✓ Branch 10 → 11 taken 53 times.
✓ Branch 11 → 12 taken 53 times.
✓ Branch 13 → 14 taken 19 times.
✓ Branch 13 → 15 taken 34 times.
106 count += item.toMap().value(QStringLiteral("duplicate")).toString().isEmpty() ? 0 : 1;
45 2327 return count;
46 }
47
48 4 bool QuickContactVcf::openFile(const QUrl &url) {
49 4 ++m_readGeneration; // a pending attachment read no longer applies (as S106-407 for calendars)
50
2/2
✓ Branch 2 → 3 taken 4 times.
✓ Branch 3 → 4 taken 4 times.
4 QFile file(url.toLocalFile());
51
8/8
✓ Branch 5 → 6 taken 4 times.
✓ Branch 6 → 7 taken 3 times.
✓ Branch 6 → 10 taken 1 time.
✓ Branch 8 → 9 taken 3 times.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 2 times.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 17 taken 2 times.
4 if (file.size() > MaxBytes || !file.open(QIODevice::ReadOnly)) {
52
2/2
✓ Branch 13 → 14 taken 2 times.
✓ Branch 14 → 15 taken 2 times.
2 m_deps.setStatus(tr("The contact file could not be read."));
53 2 return false;
54 }
55
5/5
✓ Branch 17 → 18 taken 2 times.
✓ Branch 18 → 19 taken 2 times.
✓ Branch 19 → 20 taken 2 times.
✓ Branch 20 → 21 taken 2 times.
✓ Branch 21 → 22 taken 2 times.
2 return openText(VCardCodec::decodeBytes(file.readAll()), QFileInfo(file).fileName());
56 4 }
57
58 3 bool QuickContactVcf::openAttachment(qint64 attachmentId, const QString &name) {
59
2/3
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 5 not taken.
✓ Branch 4 → 6 taken 3 times.
3 const MailId mail = m_deps.currentMail ? m_deps.currentMail() : MailId{};
60 3 MailCache *cache = m_session.cache();
61
7/8
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 11 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 1 time.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 1 time.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 1 time.
3 if (!mail.isValid() || attachmentId <= 0 || !cache)
62 2 return false;
63 3 MailPayloadRequest request{cache->databasePath(), mail, cache->uidValidity(mail.folderId),
64
1/5
✓ Branch 17 → 18 taken 1 time.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
✗ Branch 44 → 45 not taken.
✗ Branch 44 → 46 not taken.
1 MailPayloadRequest::Kind::Attachment, attachmentId, 0, AttachmentMaxBytes};
65 1 const quint64 generation = ++m_readGeneration;
66
6/6
✓ Branch 18 → 19 taken 1 time.
✓ Branch 22 → 23 taken 1 time.
✓ Branch 23 → 24 taken 1 time.
✓ Branch 24 → 25 taken 1 time.
✓ Branch 28 → 20 taken 1 time.
✓ Branch 28 → 29 taken 1 time.
2 qCInfo(lcContactVcf) << "Reading contact attachment" << attachmentId;
67
3/3
✓ Branch 29 → 30 taken 1 time.
✓ Branch 31 → 32 taken 1 time.
✓ Branch 33 → 34 taken 1 time.
2 MailPayloadExportService::shared()->readWindow(
68 2 this, request, [this, generation, name](QByteArray bytes, MailPayloadReadResult result) {
69
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1 time.
1 if (generation != m_readGeneration)
70 ✗ return;
71
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 18 taken 1 time.
1 if (!result.completed) {
72 ✗ qCWarning(lcContactVcf) << "Contact attachment unreadable:" << result.error;
73 ✗ m_deps.setStatus(result.error);
74 ✗ return;
75 }
76
2/2
✓ Branch 18 → 19 taken 1 time.
✓ Branch 19 → 20 taken 1 time.
1 openText(VCardCodec::decodeBytes(bytes), name);
77 });
78 1 return true;
79 3 }
80
81 9 bool QuickContactVcf::openText(const QString &text, const QString &name) {
82
1/1
✓ Branch 2 → 3 taken 9 times.
9 m_cards = VCardCodec::splitCards(text);
83 9 m_sourceName = name;
84
2/2
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 14 taken 8 times.
9 if (m_cards.isEmpty()) {
85 1 m_items.clear();
86 1 emit changed();
87
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 11 → 12 taken 1 time.
1 m_deps.setStatus(tr("The file contains no contacts."));
88 1 return false;
89 }
90
7/7
✓ Branch 14 → 15 taken 8 times.
✓ Branch 18 → 19 taken 8 times.
✓ Branch 19 → 20 taken 8 times.
✓ Branch 21 → 22 taken 8 times.
✓ Branch 22 → 23 taken 8 times.
✓ Branch 26 → 16 taken 8 times.
✓ Branch 26 → 27 taken 8 times.
16 qCInfo(lcContactVcf) << "Import preview of" << m_cards.size() << "cards";
91 8 const bool cut = m_cards.size() > MaxCards;
92
2/2
✓ Branch 28 → 29 taken 1 time.
✓ Branch 28 → 42 taken 7 times.
8 if (cut) {
93
7/7
✓ Branch 29 → 30 taken 1 time.
✓ Branch 33 → 34 taken 1 time.
✓ Branch 34 → 35 taken 1 time.
✓ Branch 35 → 36 taken 1 time.
✓ Branch 36 → 37 taken 1 time.
✓ Branch 40 → 31 taken 1 time.
✓ Branch 40 → 41 taken 1 time.
2 qCWarning(lcContactVcf) << "Contact file has more than" << MaxCards << "cards; the rest is left out";
94 1 m_cards.resize(MaxCards);
95 }
96 8 ContactSyncCoordinator *sync = coordinator();
97
3/4
✓ Branch 43 → 44 taken 3 times.
✓ Branch 43 → 45 taken 5 times.
✓ Branch 45 → 46 taken 5 times.
✗ Branch 45 → 49 not taken.
8 setTargetBook(m_targetBook > 0 ? m_targetBook : (sync ? sync->store()->localBookId() : 0));
98
2/2
✓ Branch 52 → 53 taken 1 time.
✓ Branch 52 → 57 taken 7 times.
8 if (cut)
99
2/2
✓ Branch 53 → 54 taken 1 time.
✓ Branch 54 → 55 taken 1 time.
1 m_deps.setStatus(tr("The file has more than %n contacts; only the first %n are shown.", "", MaxCards));
100 8 return true;
101 }
102
103 18 void QuickContactVcf::setTargetBook(qint64 bookId) {
104 18 m_targetBook = bookId;
105 18 ContactSyncCoordinator *sync = coordinator();
106 18 m_items.clear();
107
4/4
✓ Branch 4 → 5 taken 18 times.
✓ Branch 5 → 6 taken 18 times.
✓ Branch 96 → 7 taken 5038 times.
✓ Branch 96 → 97 taken 18 times.
5056 for (const QString &raw : m_cards) {
108
1/1
✓ Branch 8 → 9 taken 5038 times.
5038 ContactCard card;
109
1/1
✓ Branch 9 → 10 taken 5038 times.
5038 VCardCodec::parse(raw, &card);
110 5038 QString duplicate;
111
2/4
✓ Branch 10 → 11 taken 5038 times.
✗ Branch 10 → 62 not taken.
✓ Branch 11 → 12 taken 5038 times.
✗ Branch 11 → 62 not taken.
5038 if (sync && bookId > 0) {
112
9/11
✓ Branch 13 → 14 taken 31 times.
✓ Branch 13 → 19 taken 5007 times.
✓ Branch 15 → 16 taken 31 times.
✓ Branch 17 → 18 taken 15 times.
✓ Branch 17 → 19 taken 16 times.
✓ Branch 20 → 21 taken 31 times.
✓ Branch 20 → 22 taken 5007 times.
✓ Branch 22 → 23 taken 15 times.
✓ Branch 22 → 30 taken 5023 times.
✗ Branch 99 → 100 not taken.
✗ Branch 99 → 101 not taken.
5038 if (!card.uid.isEmpty() && sync->store()->card(bookId, card.uid))
113 15 duplicate = QStringLiteral("uid");
114
4/4
✓ Branch 30 → 31 taken 5038 times.
✓ Branch 31 → 32 taken 5038 times.
✓ Branch 60 → 33 taken 38 times.
✓ Branch 60 → 61 taken 5038 times.
5076 for (const ContactValue &email : card.emails)
115
14/20
✓ Branch 35 → 36 taken 23 times.
✓ Branch 35 → 43 taken 15 times.
✓ Branch 38 → 39 taken 23 times.
✓ Branch 39 → 40 taken 23 times.
✓ Branch 41 → 42 taken 10 times.
✓ Branch 41 → 43 taken 13 times.
✓ Branch 44 → 45 taken 23 times.
✓ Branch 44 → 46 taken 15 times.
✓ Branch 46 → 47 taken 23 times.
✓ Branch 46 → 48 taken 15 times.
✓ Branch 48 → 49 taken 23 times.
✓ Branch 48 → 50 taken 15 times.
✓ Branch 50 → 51 taken 10 times.
✓ Branch 50 → 58 taken 28 times.
✗ Branch 103 → 104 not taken.
✗ Branch 103 → 105 not taken.
✗ Branch 107 → 108 not taken.
✗ Branch 107 → 109 not taken.
✗ Branch 111 → 112 not taken.
✗ Branch 111 → 113 not taken.
38 if (duplicate.isEmpty() && !ContactQueries::sameBookOwners(sync->store()->database(), bookId, email.value).isEmpty())
116 10 duplicate = QStringLiteral("address");
117 }
118
2/2
✓ Branch 65 → 66 taken 5038 times.
✓ Branch 78 → 79 taken 5038 times.
15114 m_items.append(QVariantMap{{QStringLiteral("name"), ContactIndex::displayName(card)},
119
1/1
✓ Branch 70 → 71 taken 5038 times.
10076 {QStringLiteral("address"), ContactIndex::primaryAddress(card)},
120
2/4
✓ Branch 82 → 83 taken 15114 times.
✓ Branch 82 → 84 taken 5038 times.
✗ Branch 121 → 122 not taken.
✗ Branch 121 → 123 not taken.
25190 {QStringLiteral("duplicate"), duplicate}});
121 5038 }
122 18 emit changed();
123 20170 }
124
125 7 QVariantMap QuickContactVcf::importAll(qint64 bookId, const QString &duplicates) {
126 7 ContactSyncCoordinator *sync = coordinator();
127
5/6
✓ Branch 3 → 4 taken 7 times.
✗ Branch 3 → 6 not taken.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 6 times.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 20 taken 6 times.
7 if (!sync || m_cards.isEmpty())
128
2/4
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 17 taken 1 time.
✗ Branch 223 → 224 not taken.
✗ Branch 223 → 225 not taken.
4 return {{QStringLiteral("ok"), false}};
129
1/1
✓ Branch 20 → 21 taken 6 times.
6 setTargetBook(bookId);
130 6 int added = 0, updated = 0, skipped = 0, failed = 0;
131 6 QStringList keys;
132
2/2
✓ Branch 146 → 22 taken 14 times.
✓ Branch 146 → 147 taken 6 times.
20 for (int i = 0; i < m_cards.size(); ++i) {
133 14 QString raw = m_cards.at(i);
134
3/3
✓ Branch 25 → 26 taken 14 times.
✓ Branch 30 → 31 taken 14 times.
✓ Branch 31 → 32 taken 14 times.
28 const QString duplicate = m_items.at(i).toMap().value(QStringLiteral("duplicate")).toString();
135
6/6
✓ Branch 38 → 39 taken 11 times.
✓ Branch 38 → 43 taken 3 times.
✓ Branch 41 → 42 taken 2 times.
✓ Branch 41 → 43 taken 9 times.
✓ Branch 44 → 45 taken 2 times.
✓ Branch 44 → 46 taken 12 times.
14 if (!duplicate.isEmpty() && duplicates == QLatin1String("skip")) {
136 2 ++skipped;
137 2 continue;
138 }
139
1/1
✓ Branch 46 → 47 taken 12 times.
12 ContactCard card;
140
1/1
✓ Branch 47 → 48 taken 12 times.
12 VCardCodec::parse(raw, &card);
141 // "Update" replaces a card with the same UID; a card that only shares
142 // an address is another card and stays untouched.
143
6/6
✓ Branch 50 → 51 taken 3 times.
✓ Branch 50 → 55 taken 9 times.
✓ Branch 53 → 54 taken 2 times.
✓ Branch 53 → 55 taken 1 time.
✓ Branch 56 → 57 taken 2 times.
✓ Branch 56 → 58 taken 10 times.
12 if (duplicate == QLatin1String("address") && duplicates == QLatin1String("update")) {
144 2 ++skipped;
145 2 continue;
146 }
147
6/6
✓ Branch 60 → 61 taken 6 times.
✓ Branch 60 → 65 taken 4 times.
✓ Branch 63 → 64 taken 4 times.
✓ Branch 63 → 65 taken 2 times.
✓ Branch 66 → 67 taken 4 times.
✓ Branch 66 → 88 taken 6 times.
10 if (duplicate == QLatin1String("uid") && duplicates == QLatin1String("update")) {
148
3/3
✓ Branch 68 → 69 taken 4 times.
✓ Branch 69 → 70 taken 3 times.
✓ Branch 69 → 74 taken 1 time.
4 if (sync->outbox()->replaceRaw(bookId, card.uid, raw)) {
149 3 ++updated;
150
2/2
✓ Branch 70 → 71 taken 3 times.
✓ Branch 71 → 72 taken 3 times.
3 keys.append(ContactQueries::cardKey(bookId, card.uid));
151 } else {
152 // S106-287: a card that could not be updated is reported.
153 1 ++failed;
154
7/7
✓ Branch 74 → 75 taken 1 time.
✓ Branch 78 → 79 taken 1 time.
✓ Branch 79 → 80 taken 1 time.
✓ Branch 80 → 81 taken 1 time.
✓ Branch 81 → 82 taken 1 time.
✓ Branch 85 → 76 taken 1 time.
✓ Branch 85 → 86 taken 1 time.
2 qCWarning(lcContactVcf) << "Card" << i << "could not be updated";
155 }
156 4 continue;
157 4 }
158
2/2
✓ Branch 90 → 91 taken 2 times.
✓ Branch 90 → 103 taken 4 times.
6 if (duplicate == QLatin1String("uid")) {
159 // "Keep both": the copy gets its own identity.
160 2 card.uid.clear();
161
1/1
✓ Branch 96 → 97 taken 2 times.
4 raw = VCardCodec::create(card);
162 }
163 6 QString error;
164
1/1
✓ Branch 104 → 105 taken 6 times.
6 const QString key = sync->outbox()->importRaw(bookId, raw, &error);
165
1/2
✗ Branch 106 → 107 not taken.
✓ Branch 106 → 121 taken 6 times.
6 if (key.isEmpty()) {
166 ✗ ++skipped;
167 ✗ qCInfo(lcContactVcf) << "Card" << i << "not imported:" << error;
168 ✗ continue;
169 ✗ }
170 6 ++added;
171
1/1
✓ Branch 121 → 122 taken 6 times.
6 keys.append(key);
172 28 }
173
12/12
✓ Branch 147 → 148 taken 6 times.
✓ Branch 151 → 152 taken 6 times.
✓ Branch 152 → 153 taken 6 times.
✓ Branch 153 → 154 taken 6 times.
✓ Branch 154 → 155 taken 6 times.
✓ Branch 155 → 156 taken 6 times.
✓ Branch 156 → 157 taken 6 times.
✓ Branch 157 → 158 taken 6 times.
✓ Branch 158 → 159 taken 6 times.
✓ Branch 159 → 160 taken 6 times.
✓ Branch 163 → 149 taken 6 times.
✓ Branch 163 → 164 taken 6 times.
12 qCInfo(lcContactVcf) << "Imported" << added << "updated" << updated << "skipped" << skipped << "failed" << failed;
174
4/4
✓ Branch 164 → 165 taken 2 times.
✓ Branch 164 → 166 taken 4 times.
✓ Branch 165 → 167 taken 2 times.
✓ Branch 166 → 167 taken 4 times.
6 const QString done = added + updated == 1 ? tr("1 contact imported.") : tr("%n contacts imported.", "", added + updated);
175
4/4
✓ Branch 167 → 168 taken 5 times.
✓ Branch 167 → 169 taken 1 time.
✓ Branch 175 → 176 taken 1 time.
✓ Branch 176 → 177 taken 6 times.
7 m_deps.setStatus(failed == 0 ? done
176
7/13
✓ Branch 169 → 170 taken 1 time.
✗ Branch 169 → 171 not taken.
✓ Branch 170 → 172 taken 1 time.
✗ Branch 171 → 172 not taken.
✓ Branch 174 → 175 taken 1 time.
✓ Branch 178 → 179 taken 1 time.
✓ Branch 178 → 180 taken 5 times.
✓ Branch 180 → 181 taken 1 time.
✓ Branch 180 → 182 taken 5 times.
✗ Branch 295 → 296 not taken.
✗ Branch 295 → 297 not taken.
✗ Branch 301 → 302 not taken.
✗ Branch 301 → 303 not taken.
7 : done + QLatin1Char(' ') + (failed == 1 ? tr("One contact could not be updated.")
177 : tr("%n contacts could not be updated.", "", failed)));
178
1/1
✓ Branch 182 → 183 taken 6 times.
6 close();
179
1/1
✓ Branch 183 → 184 taken 6 times.
6 emit imported(keys);
180 30 return {{QStringLiteral("ok"), true}, {QStringLiteral("imported"), added}, {QStringLiteral("updated"), updated},
181
2/4
✓ Branch 206 → 207 taken 30 times.
✓ Branch 206 → 208 taken 6 times.
✗ Branch 307 → 308 not taken.
✗ Branch 307 → 309 not taken.
48 {QStringLiteral("skipped"), skipped}, {QStringLiteral("failed"), failed}};
182 51 }
183
184 8 void QuickContactVcf::close() {
185 8 ++m_readGeneration;
186 8 m_cards.clear();
187 8 m_items.clear();
188 8 m_sourceName.clear();
189 8 emit changed();
190 8 }
191
192 13 QStringList QuickContactVcf::rawCards(const QStringList &keys) const {
193 13 ContactSyncCoordinator *sync = coordinator();
194 13 QStringList raws;
195
2/2
✓ Branch 23 → 5 taken 12 times.
✓ Branch 23 → 24 taken 13 times.
25 for (const QString &key : keys) {
196 12 qint64 bookId = 0;
197 12 QString uid;
198
6/7
✓ Branch 6 → 7 taken 12 times.
✗ Branch 6 → 10 not taken.
✓ Branch 7 → 8 taken 12 times.
✓ Branch 8 → 9 taken 11 times.
✓ Branch 8 → 10 taken 1 time.
✓ Branch 11 → 12 taken 11 times.
✓ Branch 11 → 20 taken 1 time.
12 if (sync && ContactQueries::splitKey(key, &bookId, &uid))
199
2/3
✓ Branch 13 → 14 taken 11 times.
✓ Branch 15 → 16 taken 11 times.
✗ Branch 15 → 18 not taken.
11 if (const auto stored = sync->store()->card(bookId, uid))
200
1/1
✓ Branch 17 → 18 taken 11 times.
11 raws.append(stored->raw);
201 12 }
202 13 return raws;
203 ✗ }
204
205 4 QString QuickContactVcf::attachmentFile(const QStringList &keys, const QString &title) {
206
1/1
✓ Branch 2 → 3 taken 4 times.
4 const QStringList raws = rawCards(keys);
207
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 3 times.
4 if (raws.isEmpty())
208 1 return {};
209 //: The personal-details sheet before "Send as vCard" (D-22).
210
1/3
✓ Branch 8 → 9 taken 3 times.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
9 const QString sheet = raws.size() == 1 ? tr("Send %1 as vCard").arg(title)
211
3/6
✓ Branch 7 → 8 taken 3 times.
✗ Branch 7 → 11 not taken.
✓ Branch 10 → 13 taken 3 times.
✗ Branch 12 → 13 not taken.
✓ Branch 13 → 14 taken 3 times.
✗ Branch 13 → 15 not taken.
6 : tr("Send %n contacts as vCard", "", int(raws.size()));
212
3/3
✓ Branch 15 → 16 taken 3 times.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 18 taken 2 times.
3 if (startExport(raws, title, sheet, true))
213 1 return {}; // waits for the answer; attachmentReady() follows
214
1/1
✓ Branch 18 → 19 taken 2 times.
2 return writeAttachment(raws, title);
215 4 }
216
217 3 QString QuickContactVcf::writeAttachment(const QStringList &raws, const QString &title) {
218
3/3
✓ Branch 5 → 6 taken 3 times.
✓ Branch 6 → 7 taken 3 times.
✓ Branch 7 → 8 taken 3 times.
3 const QDir root(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStringLiteral("/vcard-attachments"));
219 // Earlier attachments stay while a draft may still send them.
220
2/2
✓ Branch 12 → 13 taken 3 times.
✓ Branch 13 → 14 taken 3 times.
3 const QDateTime expiry = QDateTime::currentDateTimeUtc().addDays(-7);
221
5/5
✓ Branch 17 → 18 taken 3 times.
✓ Branch 18 → 19 taken 3 times.
✓ Branch 19 → 20 taken 3 times.
✓ Branch 37 → 21 taken 1 time.
✓ Branch 37 → 38 taken 3 times.
4 for (const QFileInfo &old : root.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))
222
4/5
✓ Branch 22 → 23 taken 1 time.
✓ Branch 23 → 24 taken 1 time.
✓ Branch 24 → 25 taken 1 time.
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 35 taken 1 time.
1 if (old.lastModified().toUTC() < expiry)
223
0/3
✗ Branch 29 → 30 not taken.
✗ Branch 30 → 31 not taken.
✗ Branch 31 → 32 not taken.
3 QDir(old.absoluteFilePath()).removeRecursively();
224
2/2
✓ Branch 40 → 41 taken 3 times.
✓ Branch 41 → 42 taken 3 times.
3 const QString folder = root.filePath(QString::number(QDateTime::currentMSecsSinceEpoch()));
225
3/3
✓ Branch 43 → 44 taken 3 times.
✓ Branch 44 → 45 taken 3 times.
✓ Branch 45 → 46 taken 3 times.
3 const QString target = QDir(folder).filePath(fileName(title));
226 3 QByteArray bytes;
227
2/2
✓ Branch 60 → 50 taken 3 times.
✓ Branch 60 → 61 taken 3 times.
6 for (const QString &raw : raws)
228
4/4
✓ Branch 51 → 52 taken 3 times.
✓ Branch 52 → 53 taken 3 times.
✓ Branch 53 → 54 taken 3 times.
✓ Branch 54 → 55 taken 3 times.
3 bytes += raw.trimmed().toUtf8() + "\r\n";
229
1/1
✓ Branch 61 → 62 taken 3 times.
3 QSaveFile file(target);
230
4/10
✓ Branch 63 → 64 taken 3 times.
✓ Branch 68 → 69 taken 3 times.
✓ Branch 69 → 70 taken 3 times.
✗ Branch 69 → 79 not taken.
✓ Branch 82 → 83 taken 3 times.
✗ Branch 82 → 84 not taken.
✗ Branch 170 → 171 not taken.
✗ Branch 170 → 172 not taken.
✗ Branch 174 → 175 not taken.
✗ Branch 174 → 176 not taken.
9 const bool ok = QDir().mkpath(folder) && QFile::setPermissions(folder, QFile::ReadOwner | QFile::WriteOwner |
231 3 QFile::ExeOwner) &&
232
9/14
✓ Branch 64 → 65 taken 3 times.
✓ Branch 65 → 66 taken 3 times.
✗ Branch 65 → 79 not taken.
✓ Branch 71 → 72 taken 3 times.
✓ Branch 72 → 73 taken 3 times.
✗ Branch 72 → 79 not taken.
✓ Branch 73 → 74 taken 3 times.
✓ Branch 75 → 76 taken 3 times.
✗ Branch 75 → 79 not taken.
✓ Branch 76 → 77 taken 3 times.
✓ Branch 77 → 78 taken 3 times.
✗ Branch 77 → 79 not taken.
✓ Branch 80 → 81 taken 3 times.
✗ Branch 80 → 82 not taken.
9 file.open(QIODevice::WriteOnly) && file.write(bytes) == bytes.size() && file.commit();
233
9/10
✓ Branch 84 → 85 taken 3 times.
✓ Branch 88 → 89 taken 3 times.
✓ Branch 89 → 90 taken 3 times.
✓ Branch 91 → 92 taken 3 times.
✓ Branch 92 → 93 taken 3 times.
✓ Branch 93 → 94 taken 3 times.
✗ Branch 93 → 95 not taken.
✓ Branch 96 → 97 taken 3 times.
✓ Branch 100 → 86 taken 3 times.
✓ Branch 100 → 101 taken 3 times.
6 qCInfo(lcContactVcf) << "vCard attachment of" << raws.size() << "cards:" << (ok ? "written" : "failed");
234
1/2
✗ Branch 101 → 102 not taken.
✓ Branch 101 → 106 taken 3 times.
3 if (!ok)
235 ✗ m_deps.setStatus(tr("The contact file could not be saved."));
236
1/2
✓ Branch 106 → 107 taken 3 times.
✗ Branch 106 → 108 not taken.
6 return ok ? target : QString();
237 3 }
238
239