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: 96.8% 181 / 0 / 187
Functions: 100.0% 21 / 0 / 21
Branches: 81.7% 367 / 0 / 449

presentation/ContactMerge.cpp
Line Branch Exec Source
1 #include "ContactMerge.h"
2
3 #include <functional>
4
5 namespace {
6
7 const char *kScalarFields[] = {"organization", "department", "title", "nickname", "birthday"};
8 const char *kNameFields[] = {"given", "family", "prefix", "additional", "suffix", "formattedName"};
9
10 644 QString text(const QVariantMap &card, const char *field) {
11
4/4
✓ Branch 4 → 5 taken 644 times.
✓ Branch 5 → 6 taken 644 times.
✓ Branch 6 → 7 taken 644 times.
✓ Branch 7 → 8 taken 644 times.
644 return card.value(QLatin1String(field)).toString().trimmed();
12 }
13
14 19 QString nameOf(const QVariantMap &card) {
15
3/3
✓ Branch 6 → 7 taken 19 times.
✓ Branch 7 → 8 taken 19 times.
✓ Branch 8 → 9 taken 19 times.
38 const QString display = card.value(QStringLiteral("displayName")).toString().trimmed();
16
1/2
✓ Branch 15 → 16 taken 19 times.
✗ Branch 15 → 18 not taken.
19 if (!display.isEmpty()) return display;
17 ✗ const QString formatted = text(card, "formattedName");
18 ✗ if (!formatted.isEmpty()) return formatted;
19 ✗ return (text(card, "given") + QLatin1Char(' ') + text(card, "family")).trimmed();
20 19 }
21
22 56 QString digits(const QString &number) {
23 56 QString result;
24
2/2
✓ Branch 8 → 4 taken 683 times.
✓ Branch 8 → 9 taken 56 times.
739 for (const QChar ch : number)
25
3/3
✓ Branch 5 → 6 taken 585 times.
✓ Branch 5 → 7 taken 98 times.
✓ Branch 6 → 7 taken 585 times.
683 if (ch.isDigit()) result.append(ch);
26
4/4
✓ Branch 10 → 11 taken 23 times.
✓ Branch 13 → 14 taken 79 times.
✓ Branch 14 → 10 taken 23 times.
✓ Branch 14 → 15 taken 56 times.
79 while (result.startsWith(QLatin1Char('0'))) result.remove(0, 1);
27 56 return result;
28 ✗ }
29
30 using SameValue = bool (*)(const QString &, const QString &);
31
32
2/2
✓ Branch 2 → 3 taken 56 times.
✓ Branch 3 → 4 taken 56 times.
56 bool sameText(const QString &a, const QString &b) { return a.trimmed().compare(b.trimmed(), Qt::CaseInsensitive) == 0; }
33
34 // Values of `extra` not yet in `result` (compared on `field`) are appended
35 // as new values of the kept card.
36 88 void unite(QVariantList &result, const QVariantList &extra, const QString &field, SameValue same) {
37
2/2
✓ Branch 65 → 4 taken 32 times.
✓ Branch 65 → 66 taken 88 times.
120 for (const QVariant &value : extra) {
38
1/1
✓ Branch 5 → 6 taken 32 times.
32 QVariantMap entry = value.toMap();
39
3/3
✓ Branch 7 → 8 taken 32 times.
✓ Branch 8 → 9 taken 32 times.
✓ Branch 9 → 10 taken 32 times.
32 const QString candidate = entry.value(field).toString().trimmed();
40
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 32 times.
32 if (candidate.isEmpty()) continue;
41 32 bool known = false;
42
4/4
✓ Branch 16 → 17 taken 32 times.
✓ Branch 17 → 18 taken 32 times.
✓ Branch 39 → 19 taken 51 times.
✓ Branch 39 → 40 taken 32 times.
83 for (const QVariant &existing : result)
43
16/24
✓ Branch 20 → 21 taken 45 times.
✓ Branch 20 → 27 taken 6 times.
✓ Branch 21 → 22 taken 45 times.
✓ Branch 23 → 24 taken 45 times.
✓ Branch 24 → 25 taken 45 times.
✓ Branch 25 → 26 taken 45 times.
✓ Branch 26 → 27 taken 15 times.
✓ Branch 26 → 28 taken 30 times.
✓ Branch 29 → 30 taken 45 times.
✓ Branch 29 → 31 taken 6 times.
✓ Branch 31 → 32 taken 45 times.
✓ Branch 31 → 33 taken 6 times.
✓ Branch 33 → 34 taken 45 times.
✓ Branch 33 → 35 taken 6 times.
✓ Branch 35 → 36 taken 45 times.
✓ Branch 35 → 37 taken 6 times.
✗ Branch 76 → 77 not taken.
✗ Branch 76 → 78 not taken.
✗ Branch 80 → 81 not taken.
✗ Branch 80 → 82 not taken.
✗ Branch 84 → 85 not taken.
✗ Branch 84 → 86 not taken.
✗ Branch 88 → 89 not taken.
✗ Branch 88 → 90 not taken.
51 known = known || same(existing.toMap().value(field).toString(), candidate);
44
2/2
✓ Branch 40 → 41 taken 15 times.
✓ Branch 40 → 42 taken 17 times.
32 if (known) continue;
45
1/1
✓ Branch 46 → 47 taken 17 times.
34 entry.insert(QStringLiteral("sourceIndex"), -1);
46
1/1
✓ Branch 51 → 52 taken 17 times.
17 result.append(entry);
47 47 }
48 88 }
49
50 // The id of a list value: equal ids are one value (phone numbers by
51 // digits, the rest by text without case).
52 87 QString valueId(const QString &field, const QString &value) {
53
11/16
✓ Branch 4 → 5 taken 14 times.
✓ Branch 4 → 10 taken 73 times.
✓ Branch 5 → 6 taken 14 times.
✓ Branch 7 → 8 taken 14 times.
✗ Branch 7 → 10 not taken.
✓ Branch 8 → 9 taken 14 times.
✓ Branch 10 → 11 taken 73 times.
✓ Branch 11 → 12 taken 73 times.
✓ Branch 12 → 13 taken 73 times.
✓ Branch 12 → 14 taken 14 times.
✓ Branch 14 → 15 taken 14 times.
✓ Branch 14 → 16 taken 73 times.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
101 const QString key = field == QLatin1String("phones") && !digits(value).isEmpty() ? digits(value) : value.trimmed().toLower();
54
2/2
✓ Branch 18 → 19 taken 87 times.
✓ Branch 19 → 20 taken 87 times.
174 return field + QLatin1Char(':') + key;
55 87 }
56
57 73 QString listKey(const QString &field) {
58
3/6
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 73 times.
✓ Branch 12 → 13 taken 73 times.
✗ Branch 12 → 14 not taken.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 73 times.
146 return field == QLatin1String("addresses") ? QStringLiteral("oneLine") : QStringLiteral("value");
59 }
60
61 // Removes the values whose id is excluded (Sprint 107 D-16).
62 52 QVariantList without(const QVariantList &values, const QString &field, const QStringList &excluded) {
63 52 QVariantList kept;
64
2/2
✓ Branch 22 → 4 taken 41 times.
✓ Branch 22 → 23 taken 52 times.
93 for (const QVariant &value : values)
65
8/8
✓ Branch 5 → 6 taken 41 times.
✓ Branch 7 → 8 taken 41 times.
✓ Branch 8 → 9 taken 41 times.
✓ Branch 9 → 10 taken 41 times.
✓ Branch 10 → 11 taken 41 times.
✓ Branch 18 → 19 taken 39 times.
✓ Branch 18 → 20 taken 2 times.
✓ Branch 19 → 20 taken 39 times.
41 if (!excluded.contains(valueId(field, value.toMap().value(listKey(field)).toString()))) kept.append(value);
66 52 return kept;
67 ✗ }
68
69 24 int completeness(const QVariantMap &card) {
70 24 int score = 0;
71
5/5
✓ Branch 3 → 4 taken 120 times.
✓ Branch 5 → 6 taken 104 times.
✓ Branch 5 → 7 taken 16 times.
✓ Branch 10 → 3 taken 120 times.
✓ Branch 10 → 11 taken 24 times.
144 for (const char *field : kScalarFields) score += text(card, field).isEmpty() ? 0 : 2;
72
5/5
✓ Branch 12 → 13 taken 144 times.
✓ Branch 14 → 15 taken 120 times.
✓ Branch 14 → 16 taken 24 times.
✓ Branch 19 → 12 taken 144 times.
✓ Branch 19 → 20 taken 24 times.
168 for (const char *field : kNameFields) score += text(card, field).isEmpty() ? 0 : 1;
73
5/5
✓ Branch 24 → 25 taken 96 times.
✓ Branch 25 → 26 taken 96 times.
✓ Branch 26 → 27 taken 96 times.
✓ Branch 33 → 22 taken 96 times.
✓ Branch 33 → 34 taken 24 times.
120 for (const char *list : {"emails", "phones", "addresses", "urls"}) score += 2 * int(card.value(QLatin1String(list)).toList().size());
74
2/2
✓ Branch 38 → 39 taken 24 times.
✓ Branch 39 → 40 taken 24 times.
48 score += int(card.value(QStringLiteral("categories")).toStringList().size());
75
3/3
✓ Branch 46 → 47 taken 24 times.
✓ Branch 48 → 49 taken 21 times.
✓ Branch 48 → 50 taken 3 times.
24 score += text(card, "note").isEmpty() ? 0 : 1;
76
3/4
✓ Branch 56 → 57 taken 24 times.
✓ Branch 57 → 58 taken 24 times.
✗ Branch 58 → 59 not taken.
✓ Branch 58 → 60 taken 24 times.
48 score += card.value(QStringLiteral("hasPhoto")).toBool() ? 3 : 0;
77 24 return score;
78 }
79
80 } // namespace
81
82 12 bool ContactMerge::samePhone(const QString &a, const QString &b) {
83
2/2
✓ Branch 2 → 3 taken 12 times.
✓ Branch 3 → 4 taken 12 times.
12 const QString left = digits(a), right = digits(b);
84
3/7
✓ Branch 5 → 6 taken 12 times.
✗ Branch 5 → 8 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 12 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 13 taken 12 times.
✗ Branch 11 → 12 not taken.
12 if (left.isEmpty() || right.isEmpty()) return sameText(a, b);
85
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 12 times.
12 if (left == right) return true;
86 // "+49 30 1234567" (49301234567) and "030 1234567" (301234567): one ends
87 // with the other and the difference is a country code (1–3 digits).
88
2/2
✓ Branch 18 → 19 taken 6 times.
✓ Branch 18 → 20 taken 6 times.
12 const QString &longer = left.size() > right.size() ? left : right;
89
2/2
✓ Branch 23 → 24 taken 6 times.
✓ Branch 23 → 25 taken 6 times.
12 const QString &shorter = left.size() > right.size() ? right : left;
90
5/7
✓ Branch 27 → 28 taken 12 times.
✗ Branch 27 → 34 not taken.
✓ Branch 28 → 29 taken 12 times.
✓ Branch 29 → 30 taken 11 times.
✓ Branch 29 → 34 taken 1 time.
✓ Branch 32 → 33 taken 11 times.
✗ Branch 32 → 34 not taken.
12 return shorter.size() >= 6 && longer.endsWith(shorter) && longer.size() - shorter.size() <= 3;
91 12 }
92
93 7 int ContactMerge::keeperIndex(const QList<QVariantMap> &cards) {
94 7 int best = 0;
95
2/2
✓ Branch 11 → 3 taken 12 times.
✓ Branch 11 → 12 taken 7 times.
19 for (int i = 1; i < cards.size(); ++i)
96
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 12 times.
12 if (completeness(cards.at(i)) > completeness(cards.at(best))) best = i;
97 7 return best;
98 }
99
100 7 QVariantList ContactMerge::conflicts(const QList<QVariantMap> &cards, int keeper) {
101 7 QVariantList result;
102 42 const auto add = [&](const QString &field, const QString &label, const std::function<QString(const QVariantMap &)> &value) {
103 42 QVariantList options;
104 42 QStringList seen;
105 42 int chosen = -1;
106
2/2
✓ Branch 76 → 3 taken 114 times.
✓ Branch 76 → 77 taken 42 times.
156 for (int i = 0; i < cards.size(); ++i) {
107
1/1
✓ Branch 4 → 5 taken 114 times.
114 const QString current = value(cards.at(i));
108
2/2
✓ Branch 6 → 7 taken 82 times.
✓ Branch 6 → 8 taken 32 times.
114 if (current.isEmpty()) continue;
109
2/2
✓ Branch 8 → 9 taken 12 times.
✓ Branch 8 → 11 taken 20 times.
32 if (i == keeper) chosen = int(options.size());
110 32 bool known = false;
111
8/9
✓ Branch 11 → 12 taken 32 times.
✓ Branch 12 → 13 taken 32 times.
✓ Branch 15 → 16 taken 16 times.
✗ Branch 15 → 18 not taken.
✓ Branch 16 → 17 taken 16 times.
✓ Branch 17 → 18 taken 5 times.
✓ Branch 17 → 19 taken 11 times.
✓ Branch 22 → 14 taken 16 times.
✓ Branch 22 → 23 taken 32 times.
48 for (const QString &other : seen) known = known || sameText(other, current);
112
2/2
✓ Branch 23 → 24 taken 5 times.
✓ Branch 23 → 47 taken 27 times.
32 if (known) {
113
2/2
✓ Branch 24 → 25 taken 1 time.
✓ Branch 24 → 46 taken 4 times.
5 if (i == keeper) // the keeper's value is already listed under another card
114
2/2
✓ Branch 45 → 26 taken 1 time.
✓ Branch 45 → 46 taken 1 time.
2 for (int o = 0; o < options.size(); ++o)
115
5/6
✓ Branch 27 → 28 taken 1 time.
✓ Branch 32 → 33 taken 1 time.
✓ Branch 33 → 34 taken 1 time.
✓ Branch 34 → 35 taken 1 time.
✓ Branch 41 → 42 taken 1 time.
✗ Branch 41 → 43 not taken.
2 if (sameText(options.at(o).toMap().value(QStringLiteral("text")).toString(), current)) chosen = o;
116 5 continue;
117 5 }
118
1/1
✓ Branch 47 → 48 taken 27 times.
27 seen.append(current);
119
3/5
✓ Branch 58 → 59 taken 27 times.
✓ Branch 62 → 63 taken 54 times.
✓ Branch 62 → 64 taken 27 times.
✗ Branch 161 → 162 not taken.
✗ Branch 161 → 163 not taken.
135 options.append(QVariantMap{{QStringLiteral("text"), current}, {QStringLiteral("from"), i}});
120 114 }
121
2/2
✓ Branch 78 → 79 taken 33 times.
✓ Branch 78 → 80 taken 9 times.
42 if (options.size() < 2) return;
122
1/2
✗ Branch 80 → 81 not taken.
✓ Branch 80 → 82 taken 9 times.
9 if (chosen < 0) chosen = 0;
123
1/1
✓ Branch 107 → 108 taken 9 times.
45 result.append(QVariantMap{{QStringLiteral("field"), field}, {QStringLiteral("label"), label}, {QStringLiteral("options"), options},
124
4/6
✓ Branch 98 → 99 taken 9 times.
✓ Branch 103 → 104 taken 9 times.
✓ Branch 111 → 112 taken 36 times.
✓ Branch 111 → 113 taken 9 times.
✗ Branch 193 → 194 not taken.
✗ Branch 193 → 195 not taken.
63 {QStringLiteral("chosen"), options.at(chosen).toMap().value(QStringLiteral("from"))}});
125
6/14
✓ Branch 56 → 57 taken 27 times.
✓ Branch 105 → 106 taken 9 times.
✓ Branch 128 → 129 taken 9 times.
✓ Branch 128 → 130 taken 33 times.
✓ Branch 132 → 133 taken 9 times.
✓ Branch 132 → 135 taken 33 times.
✗ Branch 176 → 177 not taken.
✗ Branch 176 → 180 not taken.
✗ Branch 178 → 179 not taken.
✗ Branch 178 → 180 not taken.
✗ Branch 235 → 236 not taken.
✗ Branch 235 → 239 not taken.
✗ Branch 237 → 238 not taken.
✗ Branch 237 → 239 not taken.
201 };
126
2/2
✓ Branch 3 → 4 taken 7 times.
✓ Branch 7 → 8 taken 7 times.
33 add(QStringLiteral("name"), tr("Name"), [](const QVariantMap &card) { return nameOf(card); });
127
2/2
✓ Branch 13 → 14 taken 7 times.
✓ Branch 17 → 18 taken 7 times.
33 add(QStringLiteral("organization"), tr("Organization"), [](const QVariantMap &card) { return text(card, "organization"); });
128
2/2
✓ Branch 23 → 24 taken 7 times.
✓ Branch 27 → 28 taken 7 times.
33 add(QStringLiteral("department"), tr("Department"), [](const QVariantMap &card) { return text(card, "department"); });
129
2/2
✓ Branch 33 → 34 taken 7 times.
✓ Branch 37 → 38 taken 7 times.
33 add(QStringLiteral("title"), tr("Job title"), [](const QVariantMap &card) { return text(card, "title"); });
130
2/2
✓ Branch 43 → 44 taken 7 times.
✓ Branch 47 → 48 taken 7 times.
33 add(QStringLiteral("nickname"), tr("Nickname"), [](const QVariantMap &card) { return text(card, "nickname"); });
131
2/2
✓ Branch 53 → 54 taken 7 times.
✓ Branch 57 → 58 taken 7 times.
33 add(QStringLiteral("birthday"), tr("Birthday"), [](const QVariantMap &card) { return text(card, "birthday"); });
132 7 return result;
133 ✗ }
134
135 7 QVariantList ContactMerge::setRows(const QList<QVariantMap> &cards, int keeper) {
136 7 QVariantList rows;
137
1/1
✓ Branch 5 → 6 taken 7 times.
14 const QList<QPair<QString, QString>> lists{{QStringLiteral("emails"), tr("E-mail addresses")},
138
1/1
✓ Branch 10 → 11 taken 7 times.
14 {QStringLiteral("phones"), tr("Phone numbers")},
139
1/1
✓ Branch 15 → 16 taken 7 times.
14 {QStringLiteral("addresses"), tr("Postal addresses")},
140
1/1
✓ Branch 20 → 21 taken 7 times.
14 {QStringLiteral("urls"), tr("Links")},
141
3/5
✓ Branch 25 → 26 taken 7 times.
✓ Branch 29 → 30 taken 35 times.
✓ Branch 29 → 31 taken 7 times.
✗ Branch 252 → 253 not taken.
✗ Branch 252 → 254 not taken.
49 {QStringLiteral("categories"), tr("Groups")}};
142
2/2
✓ Branch 247 → 48 taken 35 times.
✓ Branch 247 → 248 taken 7 times.
42 for (const auto &[field, label] : lists) {
143 35 QVariantList values;
144 35 QStringList ids;
145 // The kept card first, so its values lead the row.
146
1/1
✓ Branch 51 → 52 taken 35 times.
35 QList<int> order{keeper};
147
2/2
✓ Branch 57 → 53 taken 95 times.
✓ Branch 57 → 58 taken 35 times.
130 for (int i = 0; i < cards.size(); ++i)
148
3/3
✓ Branch 53 → 54 taken 60 times.
✓ Branch 53 → 55 taken 35 times.
✓ Branch 54 → 55 taken 60 times.
95 if (i != keeper) order.append(i);
149
4/4
✓ Branch 58 → 59 taken 35 times.
✓ Branch 59 → 60 taken 35 times.
✓ Branch 212 → 61 taken 95 times.
✓ Branch 212 → 213 taken 35 times.
130 for (const int index : order) {
150 95 QStringList texts;
151
2/2
✓ Branch 64 → 65 taken 19 times.
✓ Branch 64 → 74 taken 76 times.
95 if (field == QLatin1String("categories"))
152
2/2
✓ Branch 67 → 68 taken 19 times.
✓ Branch 68 → 69 taken 19 times.
19 texts = cards.at(index).value(field).toStringList();
153 else
154
6/6
✓ Branch 76 → 77 taken 76 times.
✓ Branch 77 → 78 taken 76 times.
✓ Branch 80 → 81 taken 76 times.
✓ Branch 81 → 82 taken 76 times.
✓ Branch 99 → 83 taken 32 times.
✓ Branch 99 → 100 taken 76 times.
108 for (const QVariant &value : cards.at(index).value(field).toList())
155
6/6
✓ Branch 84 → 85 taken 32 times.
✓ Branch 86 → 87 taken 32 times.
✓ Branch 87 → 88 taken 32 times.
✓ Branch 88 → 89 taken 32 times.
✓ Branch 89 → 90 taken 32 times.
✓ Branch 90 → 91 taken 32 times.
108 texts.append(value.toMap().value(listKey(field)).toString().trimmed());
156
2/2
✓ Branch 208 → 105 taken 37 times.
✓ Branch 208 → 209 taken 95 times.
132 for (const QString &text : std::as_const(texts)) {
157
1/2
✗ Branch 107 → 108 not taken.
✓ Branch 107 → 109 taken 37 times.
46 if (text.isEmpty()) continue;
158
1/1
✓ Branch 109 → 110 taken 37 times.
37 QString id = valueId(field, text);
159
2/2
✓ Branch 112 → 113 taken 8 times.
✓ Branch 112 → 131 taken 29 times.
37 if (field == QLatin1String("phones"))
160
2/2
✓ Branch 129 → 116 taken 4 times.
✓ Branch 129 → 130 taken 8 times.
12 for (const QString &known : std::as_const(ids)) // "+49 30 …" and "030 …" are one number
161
4/5
✓ Branch 117 → 118 taken 4 times.
✓ Branch 121 → 122 taken 4 times.
✓ Branch 122 → 123 taken 4 times.
✓ Branch 125 → 126 taken 4 times.
✗ Branch 125 → 127 not taken.
4 if (samePhone(known.mid(known.indexOf(QLatin1Char(':')) + 1), digits(text))) id = known;
162 37 const int existing = int(ids.indexOf(id));
163
2/2
✓ Branch 132 → 133 taken 9 times.
✓ Branch 132 → 167 taken 28 times.
37 if (existing >= 0) {
164
1/1
✓ Branch 134 → 135 taken 9 times.
9 QVariantMap entry = values.at(existing).toMap();
165
2/2
✓ Branch 139 → 140 taken 9 times.
✓ Branch 140 → 141 taken 9 times.
18 QVariantList from = entry.value(QStringLiteral("from")).toList();
166
2/3
✓ Branch 146 → 147 taken 9 times.
✗ Branch 146 → 151 not taken.
✓ Branch 148 → 149 taken 9 times.
9 if (!from.contains(index)) from.append(index);
167
1/1
✓ Branch 155 → 156 taken 9 times.
18 entry.insert(QStringLiteral("from"), from);
168
1/1
✓ Branch 160 → 161 taken 9 times.
9 values[existing] = entry;
169 9 continue;
170 9 }
171
1/1
✓ Branch 167 → 168 taken 28 times.
28 ids.append(id);
172
1/1
✓ Branch 184 → 185 taken 28 times.
112 values.append(QVariantMap{{QStringLiteral("id"), id}, {QStringLiteral("text"), text},
173
4/8
✓ Branch 188 → 189 taken 84 times.
✓ Branch 188 → 190 taken 28 times.
✓ Branch 192 → 193 taken 28 times.
✓ Branch 192 → 194 taken 28 times.
✗ Branch 387 → 388 not taken.
✗ Branch 387 → 389 not taken.
✗ Branch 393 → 394 not taken.
✗ Branch 393 → 395 not taken.
168 {QStringLiteral("from"), QVariantList{index}}});
174 37 }
175 95 }
176
2/2
✓ Branch 214 → 215 taken 15 times.
✓ Branch 214 → 242 taken 20 times.
35 if (!values.isEmpty())
177
3/5
✓ Branch 229 → 230 taken 15 times.
✓ Branch 233 → 234 taken 45 times.
✓ Branch 233 → 235 taken 15 times.
✗ Branch 442 → 443 not taken.
✗ Branch 442 → 444 not taken.
105 rows.append(QVariantMap{{QStringLiteral("field"), field}, {QStringLiteral("label"), label}, {QStringLiteral("values"), values}});
178 35 }
179 7 return rows;
180 221 }
181
182 13 QVariantMap ContactMerge::merged(const QList<QVariantMap> &cards, int keeper, const QMap<QString, int> &choices,
183 const QStringList &excluded) {
184
3/6
✓ Branch 2 → 3 taken 13 times.
✗ Branch 2 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 13 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 13 times.
13 if (keeper < 0 || keeper >= cards.size()) return {};
185 13 const QVariantMap &keep = cards.at(keeper);
186
2/2
✓ Branch 14 → 15 taken 13 times.
✓ Branch 15 → 16 taken 13 times.
26 QVariantList emails = keep.value(QStringLiteral("emails")).toList();
187
2/2
✓ Branch 24 → 25 taken 13 times.
✓ Branch 25 → 26 taken 13 times.
26 QVariantList phones = keep.value(QStringLiteral("phones")).toList();
188
2/2
✓ Branch 34 → 35 taken 13 times.
✓ Branch 35 → 36 taken 13 times.
26 QVariantList urls = keep.value(QStringLiteral("urls")).toList();
189
2/2
✓ Branch 44 → 45 taken 13 times.
✓ Branch 45 → 46 taken 13 times.
26 QVariantList addresses = keep.value(QStringLiteral("addresses")).toList();
190
2/2
✓ Branch 54 → 55 taken 13 times.
✓ Branch 55 → 56 taken 13 times.
26 QStringList categories = keep.value(QStringLiteral("categories")).toStringList();
191 13 QStringList notes;
192
5/5
✓ Branch 60 → 61 taken 13 times.
✓ Branch 63 → 64 taken 2 times.
✓ Branch 63 → 68 taken 11 times.
✓ Branch 64 → 65 taken 2 times.
✓ Branch 65 → 66 taken 2 times.
13 if (!text(keep, "note").isEmpty()) notes << text(keep, "note");
193 13 QVariantMap fields;
194
2/2
✓ Branch 206 → 69 taken 35 times.
✓ Branch 206 → 207 taken 13 times.
48 for (int i = 0; i < cards.size(); ++i) {
195
2/2
✓ Branch 69 → 70 taken 13 times.
✓ Branch 69 → 71 taken 22 times.
35 if (i == keeper) continue;
196 22 const QVariantMap &other = cards.at(i);
197
3/3
✓ Branch 79 → 80 taken 22 times.
✓ Branch 80 → 81 taken 22 times.
✓ Branch 81 → 82 taken 22 times.
44 unite(emails, other.value(QStringLiteral("emails")).toList(), QStringLiteral("value"), sameText);
198
3/3
✓ Branch 96 → 97 taken 22 times.
✓ Branch 97 → 98 taken 22 times.
✓ Branch 98 → 99 taken 22 times.
44 unite(phones, other.value(QStringLiteral("phones")).toList(), QStringLiteral("value"), samePhone);
199
3/3
✓ Branch 113 → 114 taken 22 times.
✓ Branch 114 → 115 taken 22 times.
✓ Branch 115 → 116 taken 22 times.
44 unite(urls, other.value(QStringLiteral("urls")).toList(), QStringLiteral("value"), sameText);
200
3/3
✓ Branch 130 → 131 taken 22 times.
✓ Branch 131 → 132 taken 22 times.
✓ Branch 132 → 133 taken 22 times.
44 unite(addresses, other.value(QStringLiteral("addresses")).toList(), QStringLiteral("oneLine"), sameText);
201
6/6
✓ Branch 144 → 145 taken 22 times.
✓ Branch 145 → 146 taken 22 times.
✓ Branch 150 → 151 taken 22 times.
✓ Branch 151 → 152 taken 22 times.
✓ Branch 159 → 153 taken 4 times.
✓ Branch 159 → 160 taken 22 times.
48 for (const QString &category : other.value(QStringLiteral("categories")).toStringList())
202
2/3
✓ Branch 155 → 156 taken 4 times.
✗ Branch 155 → 157 not taken.
✓ Branch 156 → 157 taken 4 times.
26 if (!categories.contains(category, Qt::CaseInsensitive)) categories.append(category);
203
1/1
✓ Branch 161 → 162 taken 22 times.
22 const QString note = text(other, "note");
204
6/7
✓ Branch 163 → 164 taken 4 times.
✓ Branch 163 → 167 taken 18 times.
✓ Branch 165 → 166 taken 4 times.
✗ Branch 165 → 167 not taken.
✓ Branch 168 → 169 taken 4 times.
✓ Branch 168 → 170 taken 18 times.
✓ Branch 169 → 170 taken 4 times.
22 if (!note.isEmpty() && !notes.contains(note)) notes << note;
205 // Empty fields of the kept card are filled from the others.
206
2/2
✓ Branch 200 → 171 taken 110 times.
✓ Branch 200 → 201 taken 22 times.
132 for (const char *field : kScalarFields)
207
17/24
✓ Branch 171 → 172 taken 110 times.
✓ Branch 173 → 174 taken 97 times.
✓ Branch 173 → 182 taken 13 times.
✓ Branch 175 → 176 taken 97 times.
✓ Branch 176 → 177 taken 97 times.
✓ Branch 177 → 178 taken 94 times.
✓ Branch 177 → 182 taken 3 times.
✓ Branch 178 → 179 taken 94 times.
✓ Branch 180 → 181 taken 8 times.
✓ Branch 180 → 182 taken 86 times.
✓ Branch 183 → 184 taken 94 times.
✓ Branch 183 → 185 taken 16 times.
✓ Branch 185 → 186 taken 97 times.
✓ Branch 185 → 187 taken 13 times.
✓ Branch 187 → 188 taken 110 times.
✗ Branch 187 → 189 not taken.
✓ Branch 189 → 190 taken 8 times.
✓ Branch 189 → 199 taken 102 times.
✗ Branch 551 → 552 not taken.
✗ Branch 551 → 553 not taken.
✗ Branch 555 → 556 not taken.
✗ Branch 555 → 557 not taken.
✗ Branch 560 → 561 not taken.
✗ Branch 560 → 562 not taken.
110 if (text(keep, field).isEmpty() && !fields.contains(QLatin1String(field)) && !text(other, field).isEmpty())
208
3/3
✓ Branch 190 → 191 taken 8 times.
✓ Branch 193 → 194 taken 8 times.
✓ Branch 194 → 195 taken 8 times.
8 fields.insert(QLatin1String(field), text(other, field));
209 22 }
210
2/2
✓ Branch 210 → 211 taken 13 times.
✓ Branch 215 → 216 taken 13 times.
26 fields.insert(QStringLiteral("emails"), without(emails, QStringLiteral("emails"), excluded));
211
2/2
✓ Branch 225 → 226 taken 13 times.
✓ Branch 230 → 231 taken 13 times.
26 fields.insert(QStringLiteral("phones"), without(phones, QStringLiteral("phones"), excluded));
212
2/2
✓ Branch 240 → 241 taken 13 times.
✓ Branch 245 → 246 taken 13 times.
26 fields.insert(QStringLiteral("urls"), without(urls, QStringLiteral("urls"), excluded));
213
2/2
✓ Branch 255 → 256 taken 13 times.
✓ Branch 260 → 261 taken 13 times.
26 fields.insert(QStringLiteral("addresses"), without(addresses, QStringLiteral("addresses"), excluded));
214 13 QStringList keptCategories;
215
2/2
✓ Branch 283 → 270 taken 9 times.
✓ Branch 283 → 284 taken 13 times.
22 for (const QString &category : std::as_const(categories))
216
4/4
✓ Branch 274 → 275 taken 9 times.
✓ Branch 279 → 280 taken 8 times.
✓ Branch 279 → 281 taken 1 time.
✓ Branch 280 → 281 taken 8 times.
9 if (!excluded.contains(valueId(QStringLiteral("categories"), category))) keptCategories.append(category);
217
1/1
✓ Branch 288 → 289 taken 13 times.
26 fields.insert(QStringLiteral("categories"), keptCategories);
218
12/14
✓ Branch 293 → 294 taken 12 times.
✓ Branch 293 → 300 taken 1 time.
✓ Branch 295 → 296 taken 4 times.
✓ Branch 295 → 301 taken 8 times.
✓ Branch 296 → 297 taken 4 times.
✓ Branch 297 → 298 taken 4 times.
✓ Branch 299 → 300 taken 3 times.
✓ Branch 299 → 301 taken 1 time.
✓ Branch 302 → 303 taken 4 times.
✓ Branch 302 → 304 taken 9 times.
✓ Branch 304 → 305 taken 4 times.
✓ Branch 304 → 321 taken 9 times.
✗ Branch 666 → 667 not taken.
✗ Branch 666 → 668 not taken.
13 if (notes.size() > 1 || (notes.size() == 1 && notes.first() != text(keep, "note")))
219
2/2
✓ Branch 308 → 309 taken 4 times.
✓ Branch 313 → 314 taken 4 times.
8 fields.insert(QStringLiteral("note"), notes.join(QStringLiteral("\n\n")));
220 // Conflicts: the chosen card's value.
221
4/4
✓ Branch 321 → 322 taken 13 times.
✓ Branch 374 → 375 taken 15 times.
✓ Branch 376 → 323 taken 2 times.
✓ Branch 376 → 377 taken 13 times.
15 for (auto it = choices.begin(); it != choices.end(); ++it) {
222
4/8
✓ Branch 324 → 325 taken 2 times.
✗ Branch 324 → 330 not taken.
✓ Branch 327 → 328 taken 2 times.
✗ Branch 327 → 330 not taken.
✗ Branch 329 → 330 not taken.
✓ Branch 329 → 331 taken 2 times.
✗ Branch 332 → 333 not taken.
✓ Branch 332 → 334 taken 2 times.
2 if (it.value() < 0 || it.value() >= cards.size() || it.value() == keeper) continue;
223 2 const QVariantMap &source = cards.at(it.value());
224
2/2
✓ Branch 339 → 340 taken 1 time.
✓ Branch 339 → 351 taken 1 time.
2 if (it.key() == QLatin1String("name")) {
225
5/5
✓ Branch 341 → 342 taken 6 times.
✓ Branch 344 → 345 taken 6 times.
✓ Branch 345 → 346 taken 6 times.
✓ Branch 350 → 341 taken 6 times.
✓ Branch 350 → 373 taken 1 time.
7 for (const char *field : kNameFields) fields.insert(QLatin1String(field), text(source, field));
226 } else {
227
2/2
✓ Branch 372 → 352 taken 5 times.
✓ Branch 372 → 373 taken 1 time.
6 for (const char *field : kScalarFields)
228
10/13
✓ Branch 355 → 356 taken 1 time.
✓ Branch 355 → 360 taken 4 times.
✓ Branch 356 → 357 taken 1 time.
✓ Branch 358 → 359 taken 1 time.
✗ Branch 358 → 360 not taken.
✓ Branch 361 → 362 taken 1 time.
✓ Branch 361 → 363 taken 4 times.
✓ Branch 363 → 364 taken 1 time.
✓ Branch 363 → 371 taken 4 times.
✓ Branch 364 → 365 taken 1 time.
✓ Branch 367 → 368 taken 1 time.
✗ Branch 698 → 699 not taken.
✗ Branch 698 → 700 not taken.
5 if (it.key() == QLatin1String(field) && !text(source, field).isEmpty()) fields.insert(it.key(), text(source, field));
229 }
230 }
231 13 return fields;
232 13 }
233