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.6% 258 / 0 / 267
Functions: 100.0% 21 / 0 / 21
Branches: 83.3% 335 / 0 / 402

data/RelationGraph.cpp
Line Branch Exec Source
1 #include "RelationGraph.h"
2
3 #include <QSet>
4
5 #include "RelationGraphFamilies.h"
6 #include "RelationVocabulary.h"
7
8 #include <QMap>
9 #include <algorithm>
10 #include <functional>
11
12 namespace RelationGraph {
13
14 namespace {
15
16 const QStringList kSectorOrder{QStringLiteral("family"), QStringLiteral("work"), QStringLiteral("friends"),
17 QStringLiteral("other"), QStringLiteral("introductions")};
18
19 36392 QString categoryOf(const QString &type) {
20 36392 return RelationVocabulary::categoryId(RelationVocabulary::categoryOf(type));
21 }
22
23 struct Neighbour {
24 qint64 cardId = 0;
25 QString sector;
26 };
27
28 // Index of everything ego()/overview() need, built once per call.
29 struct Index {
30 QHash<qint64, const Person *> people;
31 QHash<qint64, QList<int>> edgesOf; // card → indices into input.edges
32 QHash<qint64, int> degree;
33
34 156 explicit Index(const Input &input) {
35
2/2
✓ Branch 11 → 7 taken 42979 times.
✓ Branch 11 → 12 taken 156 times.
43135 for (const Person &p : input.people)
36
1/1
✓ Branch 8 → 9 taken 42979 times.
42979 people.insert(p.cardId, &p);
37 156 QSet<QString> relations; // S106-174: a mirrored pair is one relationship
38
2/2
✓ Branch 58 → 14 taken 69858 times.
✓ Branch 58 → 59 taken 156 times.
70014 for (int i = 0; i < input.edges.size(); ++i) {
39 69858 const StoredEdge &e = input.edges.at(i);
40
7/8
✓ Branch 15 → 16 taken 69794 times.
✓ Branch 15 → 21 taken 64 times.
✓ Branch 16 → 17 taken 69790 times.
✓ Branch 16 → 21 taken 4 times.
✓ Branch 18 → 19 taken 69790 times.
✗ Branch 18 → 21 not taken.
✓ Branch 23 → 24 taken 68 times.
✓ Branch 23 → 25 taken 69790 times.
139648 if (e.toCardId <= 0 || e.fromCardId == e.toCardId || !people.contains(e.fromCardId) ||
41
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 69790 times.
69790 !people.contains(e.toCardId))
42 68 continue;
43
2/2
✓ Branch 25 → 26 taken 69790 times.
✓ Branch 26 → 27 taken 69790 times.
69790 edgesOf[e.fromCardId].append(i);
44
2/2
✓ Branch 27 → 28 taken 69790 times.
✓ Branch 28 → 29 taken 69790 times.
69790 edgesOf[e.toCardId].append(i);
45
2/2
✓ Branch 31 → 32 taken 67610 times.
✓ Branch 31 → 53 taken 2180 times.
69790 if (e.role == QLatin1String("relation")) {
46
1/1
✓ Branch 32 → 33 taken 67610 times.
67610 const QString mirrored = RelationVocabulary::inverseOf(e.type);
47
9/11
✓ Branch 34 → 35 taken 67546 times.
✓ Branch 34 → 39 taken 64 times.
✓ Branch 35 → 36 taken 67546 times.
✓ Branch 37 → 38 taken 29999 times.
✓ Branch 37 → 39 taken 37547 times.
✓ Branch 40 → 41 taken 67546 times.
✓ Branch 40 → 42 taken 64 times.
✓ Branch 42 → 43 taken 29999 times.
✓ Branch 42 → 44 taken 37611 times.
✗ Branch 63 → 64 not taken.
✗ Branch 63 → 65 not taken.
67610 if (!mirrored.isEmpty() && relations.contains(edgeKey(e.toCardId, e.fromCardId, mirrored)))
48 29999 continue;
49
2/2
✓ Branch 44 → 45 taken 37611 times.
✓ Branch 45 → 46 taken 37611 times.
37611 relations.insert(edgeKey(e.fromCardId, e.toCardId, e.type));
50 67610 }
51
1/1
✓ Branch 53 → 54 taken 39791 times.
39791 degree[e.fromCardId] += 1;
52
1/1
✓ Branch 54 → 55 taken 39791 times.
39791 degree[e.toCardId] += 1;
53 }
54 156 }
55
56 105157 static QString edgeKey(qint64 from, qint64 to, const QString &type) {
57
6/6
✓ Branch 4 → 5 taken 105157 times.
✓ Branch 7 → 8 taken 105157 times.
✓ Branch 8 → 9 taken 105157 times.
✓ Branch 9 → 10 taken 105157 times.
✓ Branch 10 → 11 taken 105157 times.
✓ Branch 11 → 12 taken 105157 times.
105157 return QString::number(from) + QLatin1Char('|') + QString::number(to) + QLatin1Char('|') + type;
58 }
59
60 // The neighbours of a card with the sector their strongest edge gives.
61 202 QList<Neighbour> neighbours(const Input &input, qint64 card) const {
62 202 QHash<qint64, QString> best;
63
4/4
✓ Branch 3 → 4 taken 202 times.
✓ Branch 4 → 5 taken 202 times.
✓ Branch 34 → 6 taken 1523 times.
✓ Branch 34 → 35 taken 202 times.
1725 for (int index : edgesOf.value(card)) {
64 1523 const StoredEdge &e = input.edges.at(index);
65
2/2
✓ Branch 8 → 9 taken 947 times.
✓ Branch 8 → 10 taken 576 times.
1523 const qint64 other = e.fromCardId == card ? e.toCardId : e.fromCardId;
66
5/7
✓ Branch 13 → 14 taken 89 times.
✓ Branch 13 → 17 taken 1434 times.
✓ Branch 17 → 18 taken 1434 times.
✓ Branch 18 → 19 taken 89 times.
✓ Branch 18 → 20 taken 1434 times.
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 54 not taken.
1612 const QString sector = e.role == QLatin1String("via") ? QStringLiteral("introductions") : categoryOf(e.type);
67 1523 const QString current = best.value(other);
68
6/6
✓ Branch 22 → 23 taken 53 times.
✓ Branch 22 → 26 taken 1470 times.
✓ Branch 25 → 26 taken 7 times.
✓ Branch 25 → 27 taken 46 times.
✓ Branch 28 → 29 taken 1477 times.
✓ Branch 28 → 30 taken 46 times.
1523 if (current.isEmpty() || kSectorOrder.indexOf(sector) < kSectorOrder.indexOf(current))
69
1/1
✓ Branch 29 → 30 taken 1477 times.
1477 best.insert(other, sector);
70 1725 }
71 202 QList<Neighbour> out;
72
2/2
✓ Branch 45 → 37 taken 1470 times.
✓ Branch 45 → 46 taken 202 times.
1672 for (auto it = best.cbegin(); it != best.cend(); ++it)
73
1/1
✓ Branch 40 → 41 taken 1470 times.
2940 out.append({it.key(), it.value()});
74
3/3
✓ Branch 46 → 47 taken 202 times.
✓ Branch 47 → 48 taken 202 times.
✓ Branch 48 → 49 taken 202 times.
6114 std::sort(out.begin(), out.end(), [](const Neighbour &a, const Neighbour &b) { return a.cardId < b.cardId; });
75 202 return out;
76 1672 }
77 };
78
79 33163 Node nodeOf(const Person &p, int depth, const QString &sector, int degree) {
80 33163 Node n;
81 33163 n.cardId = p.cardId;
82 33163 n.key = p.key;
83 33163 n.name = p.name;
84 33163 n.gender = p.gender;
85 33163 n.inScope = p.inScope;
86 33163 n.me = p.me;
87 33163 n.birthday = p.birthday;
88 33163 n.depth = depth;
89 33163 n.sector = sector;
90 33163 n.degree = degree;
91 33163 return n;
92 }
93
94 // The node index of every card in the graph. S106-157: the per-edge linear
95 // Graph::indexOf() and the scan over all edges for the mirror check made
96 // the overview O(E·n + E²) — ~10^8 steps for a 5 000-person book.
97 312 QHash<qint64, int> nodeIndex(const Graph &graph) {
98 312 QHash<qint64, int> index;
99
1/1
✓ Branch 3 → 4 taken 312 times.
312 index.reserve(graph.nodes.size());
100
2/2
✓ Branch 11 → 5 taken 66332 times.
✓ Branch 11 → 12 taken 312 times.
66644 for (int i = 0; i < graph.nodes.size(); ++i)
101
2/2
✓ Branch 6 → 7 taken 66324 times.
✓ Branch 6 → 9 taken 8 times.
66332 if (graph.nodes.at(i).cardId > 0)
102
1/1
✓ Branch 8 → 9 taken 66324 times.
66324 index.insert(graph.nodes.at(i).cardId, i);
103 312 return index;
104 ✗ }
105
106 156 void addStoredEdges(const Input &input, Graph *graph) {
107
1/1
✓ Branch 2 → 3 taken 156 times.
156 const QHash<qint64, int> index = nodeIndex(*graph);
108 156 QSet<QString> relations; // "from|to|type" of the relation edges added
109 131589 const auto key = [](int from, int to, const QString &type) {
110
6/6
✓ Branch 4 → 5 taken 131589 times.
✓ Branch 7 → 8 taken 131589 times.
✓ Branch 8 → 9 taken 131589 times.
✓ Branch 9 → 10 taken 131589 times.
✓ Branch 10 → 11 taken 131589 times.
✓ Branch 11 → 12 taken 131589 times.
131589 return QString::number(from) + QLatin1Char('|') + QString::number(to) + QLatin1Char('|') + type;
111 };
112
1/2
✗ Branch 17 → 7 not taken.
✓ Branch 17 → 18 taken 156 times.
156 for (const Edge &o : std::as_const(graph->edges))
113 ✗ if (o.kind == QLatin1String("relation"))
114 ✗ relations.insert(key(o.from, o.to, o.type));
115
2/2
✓ Branch 111 → 20 taken 69858 times.
✓ Branch 111 → 112 taken 156 times.
70014 for (const StoredEdge &e : input.edges) {
116
1/2
✓ Branch 21 → 22 taken 69858 times.
✗ Branch 21 → 23 not taken.
69858 const int from = e.fromCardId > 0 ? index.value(e.fromCardId, -1) : -1;
117
2/2
✓ Branch 24 → 25 taken 69794 times.
✓ Branch 24 → 26 taken 64 times.
69858 const int to = e.toCardId > 0 ? index.value(e.toCardId, -1) : -1;
118
6/6
✓ Branch 27 → 28 taken 65883 times.
✓ Branch 27 → 30 taken 3975 times.
✓ Branch 28 → 29 taken 65110 times.
✓ Branch 28 → 30 taken 773 times.
✓ Branch 29 → 30 taken 2 times.
✓ Branch 29 → 31 taken 65108 times.
69858 if (from < 0 || to < 0 || from == to)
119 34749 continue;
120 // F103-22: a relationship entered on both cards is one edge.
121
1/1
✓ Branch 31 → 32 taken 65108 times.
65108 const QString mirrored = RelationVocabulary::inverseOf(e.type);
122
2/2
✓ Branch 34 → 35 taken 63884 times.
✓ Branch 34 → 44 taken 1224 times.
128992 if (e.role == QLatin1String("relation") &&
123
8/11
✓ Branch 36 → 37 taken 63873 times.
✓ Branch 36 → 40 taken 11 times.
✓ Branch 37 → 38 taken 63873 times.
✓ Branch 39 → 40 taken 33874 times.
✓ Branch 39 → 43 taken 29999 times.
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 44 taken 33885 times.
✓ Branch 49 → 50 taken 29999 times.
✓ Branch 49 → 51 taken 35109 times.
✗ Branch 124 → 125 not taken.
✗ Branch 124 → 126 not taken.
162877 ((!mirrored.isEmpty() && relations.contains(key(to, from, mirrored))) ||
124
5/7
✓ Branch 40 → 41 taken 33885 times.
✓ Branch 45 → 46 taken 33885 times.
✓ Branch 45 → 47 taken 31223 times.
✓ Branch 47 → 48 taken 63873 times.
✓ Branch 47 → 49 taken 1235 times.
✗ Branch 120 → 121 not taken.
✗ Branch 120 → 122 not taken.
98993 relations.contains(key(from, to, e.type))))
125 29999 continue;
126 35109 Edge edge;
127 35109 edge.from = from;
128 35109 edge.to = to;
129 // Membership keeps a family together in the layouts but is drawn as the area (F103-10).
130
2/4
✓ Branch 53 → 54 taken 151 times.
✓ Branch 53 → 57 taken 34958 times.
✗ Branch 142 → 143 not taken.
✗ Branch 142 → 144 not taken.
105478 edge.kind = e.role == QLatin1String("via") ? QStringLiteral("via")
131
4/6
✓ Branch 59 → 60 taken 1073 times.
✓ Branch 59 → 63 taken 33885 times.
✓ Branch 79 → 80 taken 151 times.
✓ Branch 79 → 81 taken 34958 times.
✗ Branch 137 → 138 not taken.
✗ Branch 137 → 139 not taken.
70067 : e.role == QLatin1String("inferred") ? QStringLiteral("inferred") // F103-31: follows from others
132
5/7
✓ Branch 63 → 64 taken 33885 times.
✓ Branch 64 → 65 taken 54 times.
✓ Branch 64 → 68 taken 33831 times.
✓ Branch 77 → 78 taken 1073 times.
✓ Branch 77 → 79 taken 34036 times.
✗ Branch 133 → 134 not taken.
✗ Branch 133 → 135 not taken.
68994 : Families::isMembership(e) ? QStringLiteral("member")
133
4/6
✓ Branch 73 → 74 taken 33831 times.
✓ Branch 73 → 75 taken 1278 times.
✓ Branch 75 → 76 taken 54 times.
✓ Branch 75 → 77 taken 35055 times.
✗ Branch 129 → 130 not taken.
✗ Branch 129 → 131 not taken.
70218 : QStringLiteral("relation");
134 35109 edge.type = e.type;
135 35109 edge.label = e.label;
136
5/7
✓ Branch 85 → 86 taken 151 times.
✓ Branch 85 → 89 taken 34958 times.
✓ Branch 89 → 90 taken 34958 times.
✓ Branch 92 → 93 taken 151 times.
✓ Branch 92 → 94 taken 34958 times.
✗ Branch 148 → 149 not taken.
✗ Branch 148 → 150 not taken.
35260 edge.category = edge.kind == QLatin1String("via") ? QStringLiteral("other") : categoryOf(e.type);
137 35109 edge.storedOnCardId = e.fromCardId;
138 35109 edge.sourceIndex = e.sourceIndex;
139
2/2
✓ Branch 96 → 97 taken 33831 times.
✓ Branch 96 → 101 taken 1278 times.
35109 if (edge.kind == QLatin1String("relation"))
140
2/2
✓ Branch 97 → 98 taken 33831 times.
✓ Branch 98 → 99 taken 33831 times.
33831 relations.insert(key(from, to, e.type));
141
1/1
✓ Branch 101 → 102 taken 35109 times.
35109 graph->edges.append(edge);
142 65108 }
143 156 }
144
145 // Derived layers (D-19): drawn between shown nodes; in an ego graph only
146 // between the centre and the others, in the overview as a chain per value.
147 156 void addLayers(const Input &input, const Layers &layers, const QHash<qint64, const Person *> &people, int center,
148 Graph *graph) {
149 12 const auto link = [graph](int a, int b, const QString &kind, const QString &label, int evidence) {
150
5/6
✓ Branch 2 → 3 taken 12 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 11 times.
✓ Branch 3 → 5 taken 1 time.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 9 times.
12 if (a < 0 || b < 0 || a == b)
151 3 return;
152 9 Edge edge;
153 9 edge.from = std::min(a, b);
154 9 edge.to = std::max(a, b);
155 9 edge.kind = kind;
156 9 edge.label = label;
157 9 edge.category = QStringLiteral("other");
158 9 edge.evidence = evidence;
159
1/1
✓ Branch 16 → 17 taken 9 times.
9 graph->edges.append(edge);
160 9 };
161 2 const auto byValue = [&](const QString &kind, const std::function<QStringList(const Person &)> &values) {
162 2 QMap<QString, QList<int>> members;
163
2/2
✓ Branch 27 → 3 taken 16 times.
✓ Branch 27 → 28 taken 2 times.
18 for (int i = 0; i < graph->nodes.size(); ++i)
164
1/2
✓ Branch 5 → 6 taken 16 times.
✗ Branch 5 → 25 not taken.
16 if (const Person *p = people.value(graph->nodes.at(i).cardId))
165
5/5
✓ Branch 6 → 7 taken 16 times.
✓ Branch 7 → 8 taken 16 times.
✓ Branch 8 → 9 taken 16 times.
✓ Branch 22 → 10 taken 10 times.
✓ Branch 22 → 23 taken 16 times.
26 for (const QString &value : values(*p))
166
3/3
✓ Branch 11 → 12 taken 10 times.
✓ Branch 14 → 15 taken 4 times.
✓ Branch 14 → 20 taken 6 times.
10 if (!value.trimmed().isEmpty())
167
3/3
✓ Branch 15 → 16 taken 4 times.
✓ Branch 16 → 17 taken 4 times.
✓ Branch 17 → 18 taken 4 times.
20 members[value.trimmed()].append(i);
168
4/4
✓ Branch 28 → 29 taken 2 times.
✓ Branch 52 → 53 taken 4 times.
✓ Branch 54 → 30 taken 2 times.
✓ Branch 54 → 55 taken 2 times.
4 for (auto it = members.cbegin(); it != members.cend(); ++it) {
169 2 const QList<int> &list = it.value();
170
1/2
✓ Branch 31 → 32 taken 2 times.
✗ Branch 31 → 43 not taken.
2 if (center >= 0) {
171
1/2
✓ Branch 33 → 34 taken 2 times.
✗ Branch 33 → 51 not taken.
2 if (list.contains(center))
172
2/2
✓ Branch 41 → 36 taken 4 times.
✓ Branch 41 → 42 taken 2 times.
6 for (int i : list)
173
1/1
✓ Branch 38 → 39 taken 4 times.
4 link(center, i, kind, it.key(), 0);
174 } else {
175 ✗ for (int i = 1; i < list.size(); ++i)
176 ✗ link(list.at(i - 1), list.at(i), kind, it.key(), 0);
177 }
178 }
179 2 };
180
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 12 taken 155 times.
156 if (layers.organization)
181
4/10
✓ Branch 3 → 4 taken 8 times.
✓ Branch 5 → 6 taken 8 times.
✓ Branch 5 → 7 taken 8 times.
✓ Branch 7 → 8 taken 1 time.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 17 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
18 byValue(QStringLiteral("organization"), [](const Person &p) { return QStringList{p.organization}; });
182
2/2
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 22 taken 155 times.
156 if (layers.group)
183
1/1
✓ Branch 17 → 18 taken 1 time.
10 byValue(QStringLiteral("group"), [](const Person &p) { return p.groups; });
184
1/1
✓ Branch 22 → 23 taken 156 times.
156 const QHash<qint64, int> index = nodeIndex(*graph);
185 11 const auto together = [&](const QList<Together> &pairs, const QString &kind) {
186
2/2
✓ Branch 21 → 4 taken 8 times.
✓ Branch 21 → 22 taken 11 times.
19 for (const Together &t : pairs) {
187
1/2
✓ Branch 5 → 6 taken 8 times.
✗ Branch 5 → 7 not taken.
8 const int a = t.a > 0 ? index.value(t.a, -1) : -1;
188
1/2
✓ Branch 8 → 9 taken 8 times.
✗ Branch 8 → 10 not taken.
8 const int b = t.b > 0 ? index.value(t.b, -1) : -1;
189
2/6
✓ Branch 11 → 12 taken 8 times.
✗ Branch 11 → 15 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 15 taken 8 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
8 if (center >= 0 && a != center && b != center)
190 ✗ continue;
191
1/1
✓ Branch 16 → 17 taken 8 times.
8 link(a, b, kind, QString(), t.count);
192 }
193 11 };
194
2/2
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 31 taken 155 times.
156 if (layers.events)
195
1/1
✓ Branch 27 → 28 taken 1 time.
1 together(input.sharedEvents, QStringLiteral("events"));
196
2/2
✓ Branch 31 → 32 taken 10 times.
✓ Branch 31 → 39 taken 146 times.
156 if (layers.suggestions)
197
1/1
✓ Branch 35 → 36 taken 10 times.
10 together(input.suggestions, QStringLiteral("suggestion"));
198 156 }
199
200 } // namespace
201
202 1302 int Graph::indexOf(qint64 cardId) const {
203
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1302 times.
1302 if (cardId <= 0)
204 ✗ return -1;
205
2/2
✓ Branch 10 → 5 taken 11261 times.
✓ Branch 10 → 11 taken 549 times.
11810 for (int i = 0; i < nodes.size(); ++i)
206
2/2
✓ Branch 6 → 7 taken 753 times.
✓ Branch 6 → 8 taken 10508 times.
11261 if (nodes.at(i).cardId == cardId)
207 753 return i;
208 549 return -1;
209 }
210
211 126 Graph ego(const Input &input, qint64 centerCardId, int depth, const Layers &layers,
212 const QSet<QString> &expandedSectors) {
213 126 Graph graph;
214
1/1
✓ Branch 2 → 3 taken 126 times.
126 const Index index(input);
215 126 const Person *center = index.people.value(centerCardId);
216
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 126 times.
126 if (!center)
217 ✗ return graph;
218 126 Node centre = nodeOf(*center, 0, QString(), index.degree.value(centerCardId));
219 126 centre.center = true;
220
1/1
✓ Branch 10 → 11 taken 126 times.
126 graph.nodes.append(centre);
221 // First ring, sector by sector, capped (D-18, IX large sectors).
222 // F103-10: a family the centre belongs to is shown by its other members
223 // (and its area), not as a person of its own.
224
1/1
✓ Branch 11 → 12 taken 126 times.
126 const QHash<qint64, QList<qint64>> families = Families::of(input);
225 126 QHash<qint64, QString> firstRing;
226
5/5
✓ Branch 12 → 13 taken 126 times.
✓ Branch 13 → 14 taken 126 times.
✓ Branch 14 → 15 taken 126 times.
✓ Branch 20 → 16 taken 1149 times.
✓ Branch 20 → 21 taken 126 times.
1275 for (const Neighbour &n : index.neighbours(input, centerCardId))
227
1/1
✓ Branch 17 → 18 taken 1149 times.
1275 firstRing.insert(n.cardId, n.sector);
228 126 QHash<qint64, qint64> familyOrder; // member → its family, to keep a family side by side
229 // F103-16: a family next to the centre (the centre is a member, or related
230 // to the family) is shown by its members in that sector — never as a
231 // node of its own that would leave an empty place in the area.
232
2/2
✓ Branch 66 → 23 taken 137 times.
✓ Branch 66 → 67 taken 126 times.
263 for (auto it = families.cbegin(); it != families.cend(); ++it) {
233 137 const bool member = it.value().contains(centerCardId);
234
6/6
✓ Branch 25 → 26 taken 89 times.
✓ Branch 25 → 30 taken 48 times.
✓ Branch 28 → 29 taken 88 times.
✓ Branch 28 → 30 taken 1 time.
✓ Branch 31 → 32 taken 88 times.
✓ Branch 31 → 33 taken 49 times.
137 if (!member && !firstRing.contains(it.key()))
235 88 continue;
236
4/4
✓ Branch 33 → 34 taken 48 times.
✓ Branch 33 → 37 taken 1 time.
✓ Branch 39 → 40 taken 48 times.
✓ Branch 39 → 41 taken 1 time.
97 const QString sector = member ? QStringLiteral("family") : firstRing.value(it.key());
237
1/1
✓ Branch 42 → 43 taken 49 times.
49 firstRing.remove(it.key());
238
2/2
✓ Branch 60 → 46 taken 144 times.
✓ Branch 60 → 61 taken 49 times.
193 for (qint64 person : it.value())
239
7/8
✓ Branch 47 → 48 taken 96 times.
✓ Branch 47 → 53 taken 48 times.
✓ Branch 49 → 50 taken 96 times.
✗ Branch 49 → 53 not taken.
✓ Branch 51 → 52 taken 8 times.
✓ Branch 51 → 53 taken 88 times.
✓ Branch 54 → 55 taken 8 times.
✓ Branch 54 → 58 taken 136 times.
144 if (person != centerCardId && index.people.contains(person) && !firstRing.contains(person)) {
240
1/1
✓ Branch 55 → 56 taken 8 times.
8 firstRing.insert(person, sector);
241
1/1
✓ Branch 57 → 58 taken 8 times.
8 familyOrder.insert(person, it.key());
242 }
243 49 }
244
2/2
✓ Branch 68 → 69 taken 4 times.
✓ Branch 68 → 80 taken 122 times.
126 if (families.contains(centerCardId))
245
4/4
✓ Branch 70 → 71 taken 4 times.
✓ Branch 71 → 72 taken 4 times.
✓ Branch 77 → 73 taken 12 times.
✓ Branch 77 → 78 taken 4 times.
16 for (qint64 member : families.value(centerCardId))
246
1/1
✓ Branch 74 → 75 taken 12 times.
16 familyOrder.insert(member, centerCardId);
247 126 QHash<QString, QList<qint64>> sectors;
248
1/1
✓ Branch 80 → 81 taken 126 times.
126 QList<qint64> ringCards = firstRing.keys();
249
3/3
✓ Branch 81 → 82 taken 126 times.
✓ Branch 82 → 83 taken 126 times.
✓ Branch 83 → 84 taken 126 times.
126 std::sort(ringCards.begin(), ringCards.end());
250
2/2
✓ Branch 94 → 87 taken 1108 times.
✓ Branch 94 → 95 taken 126 times.
1234 for (qint64 card : std::as_const(ringCards))
251
2/2
✓ Branch 89 → 90 taken 1108 times.
✓ Branch 90 → 91 taken 1108 times.
1108 sectors[firstRing.value(card)].append(card);
252 126 QList<int> ring;
253
2/2
✓ Branch 163 → 97 taken 630 times.
✓ Branch 163 → 164 taken 126 times.
756 for (const QString &sector : kSectorOrder) {
254 630 QList<qint64> members = sectors.value(sector);
255
3/3
✓ Branch 99 → 100 taken 630 times.
✓ Branch 100 → 101 taken 630 times.
✓ Branch 101 → 102 taken 630 times.
630 std::sort(members.begin(), members.end(), [&](qint64 a, qint64 b) {
256 3455 const qint64 fa = familyOrder.value(a, 0), fb = familyOrder.value(b, 0);
257
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 3455 times.
3455 if (fa != fb)
258 ✗ return fa > fb; // members of one family next to each other, families first
259 3455 const int da = index.degree.value(a);
260 3455 const int db = index.degree.value(b);
261
2/2
✓ Branch 8 → 9 taken 1633 times.
✓ Branch 8 → 10 taken 1822 times.
3455 if (da != db)
262 1633 return da > db;
263 1822 const QString na = index.people.value(a)->name;
264 1822 const QString nb = index.people.value(b)->name;
265
4/7
✓ Branch 14 → 15 taken 1822 times.
✓ Branch 15 → 16 taken 787 times.
✓ Branch 15 → 19 taken 1035 times.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 20 taken 787 times.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
1822 return na.localeAwareCompare(nb) < 0 || (na == nb && a < b);
266 1822 });
267
2/2
✓ Branch 104 → 105 taken 126 times.
✓ Branch 104 → 106 taken 504 times.
630 const int cap = sector == QLatin1String("family") ? MaxFamilyNodes : MaxSectorNodes;
268
4/4
✓ Branch 108 → 109 taken 6 times.
✓ Branch 108 → 112 taken 624 times.
✓ Branch 110 → 111 taken 3 times.
✓ Branch 110 → 112 taken 3 times.
630 const bool capped = members.size() > cap && !expandedSectors.contains(sector);
269
2/2
✓ Branch 113 → 114 taken 3 times.
✓ Branch 113 → 115 taken 627 times.
630 const int shown = capped ? cap - 1 : int(members.size());
270 630 int added = 0;
271
6/6
✓ Branch 128 → 129 taken 897 times.
✓ Branch 128 → 132 taken 629 times.
✓ Branch 130 → 131 taken 896 times.
✓ Branch 130 → 132 taken 1 time.
✓ Branch 133 → 118 taken 896 times.
✓ Branch 133 → 134 taken 630 times.
1526 for (int i = 0; i < shown && graph.nodes.size() < MaxEgoNodes; ++i) {
272
1/1
✓ Branch 123 → 124 taken 896 times.
896 graph.nodes.append(nodeOf(*index.people.value(members.at(i)), 1, sector, index.degree.value(members.at(i))));
273
1/1
✓ Branch 126 → 127 taken 896 times.
896 ring.append(int(graph.nodes.size()) - 1);
274 896 ++added;
275 }
276 // S106-174: "+N more" counts the people really left out (also by the
277 // node cap) and stays within MaxEgoNodes — a shown person gives way.
278
2/2
✓ Branch 135 → 136 taken 4 times.
✓ Branch 135 → 160 taken 626 times.
630 if (added < members.size()) {
279
5/6
✓ Branch 137 → 138 taken 1 time.
✓ Branch 137 → 140 taken 3 times.
✓ Branch 138 → 139 taken 1 time.
✗ Branch 138 → 140 not taken.
✓ Branch 141 → 142 taken 1 time.
✓ Branch 141 → 145 taken 3 times.
4 if (graph.nodes.size() >= MaxEgoNodes && added > 0) {
280 1 graph.nodes.removeLast();
281 1 ring.removeLast();
282 1 --added;
283 }
284
1/2
✓ Branch 146 → 147 taken 4 times.
✗ Branch 146 → 160 not taken.
4 if (graph.nodes.size() < MaxEgoNodes) {
285 4 Node more;
286 4 more.depth = 1;
287 4 more.sector = sector;
288 4 more.moreCount = int(members.size()) - added;
289
1/1
✓ Branch 152 → 153 taken 4 times.
4 more.key = QStringLiteral("more:") + sector;
290
1/1
✓ Branch 157 → 158 taken 4 times.
4 graph.nodes.append(more);
291 4 }
292 }
293 630 }
294 // S103.5.8: the strongest suggestions with the centre join as their own row.
295
2/2
✓ Branch 164 → 165 taken 10 times.
✓ Branch 164 → 216 taken 116 times.
126 if (layers.suggestions) {
296 10 QList<Together> mine;
297
2/2
✓ Branch 181 → 167 taken 7 times.
✓ Branch 181 → 182 taken 10 times.
17 for (const Together &s : input.suggestions)
298
4/10
✗ Branch 168 → 169 not taken.
✓ Branch 168 → 170 taken 7 times.
✗ Branch 169 → 170 not taken.
✗ Branch 169 → 176 not taken.
✓ Branch 170 → 171 taken 7 times.
✗ Branch 170 → 172 not taken.
✓ Branch 174 → 175 taken 7 times.
✗ Branch 174 → 176 not taken.
✓ Branch 177 → 178 taken 7 times.
✗ Branch 177 → 179 not taken.
7 if ((s.a == centerCardId || s.b == centerCardId) && index.people.contains(s.a == centerCardId ? s.b : s.a))
299
1/1
✓ Branch 178 → 179 taken 7 times.
7 mine.append(s);
300
3/3
✓ Branch 182 → 183 taken 10 times.
✓ Branch 183 → 184 taken 10 times.
✓ Branch 184 → 185 taken 10 times.
12 std::stable_sort(mine.begin(), mine.end(), [](const Together &x, const Together &y) { return x.count > y.count; });
301
6/8
✓ Branch 207 → 208 taken 7 times.
✓ Branch 207 → 212 taken 10 times.
✓ Branch 208 → 209 taken 7 times.
✗ Branch 208 → 212 not taken.
✓ Branch 210 → 211 taken 7 times.
✗ Branch 210 → 212 not taken.
✓ Branch 213 → 186 taken 7 times.
✓ Branch 213 → 214 taken 10 times.
17 for (int i = 0; i < mine.size() && i < MaxSuggestedNodes && graph.nodes.size() < MaxEgoNodes; ++i) {
302
1/2
✓ Branch 187 → 188 taken 7 times.
✗ Branch 187 → 190 not taken.
7 const qint64 other = mine.at(i).a == centerCardId ? mine.at(i).b : mine.at(i).a;
303
1/2
✓ Branch 193 → 194 taken 7 times.
✗ Branch 193 → 205 not taken.
7 if (graph.indexOf(other) < 0)
304
1/1
✓ Branch 200 → 201 taken 7 times.
14 graph.nodes.append(nodeOf(*index.people.value(other), 1, QStringLiteral("suggestions"), index.degree.value(other)));
305 }
306 10 }
307 // Second ring: people connected to a first-ring person, not shown yet.
308
2/2
✓ Branch 216 → 217 taken 10 times.
✓ Branch 216 → 252 taken 116 times.
126 if (depth >= 2) {
309 10 int visited = 0;
310
4/4
✓ Branch 217 → 218 taken 10 times.
✓ Branch 218 → 219 taken 10 times.
✓ Branch 250 → 220 taken 76 times.
✓ Branch 250 → 251 taken 10 times.
86 for (int r : ring) {
311 76 const qint64 card = graph.nodes.at(r).cardId;
312
5/5
✓ Branch 222 → 223 taken 76 times.
✓ Branch 223 → 224 taken 76 times.
✓ Branch 224 → 225 taken 76 times.
✓ Branch 246 → 226 taken 238 times.
✓ Branch 246 → 247 taken 58 times.
296 for (const Neighbour &n : index.neighbours(input, card)) {
313
5/6
✓ Branch 227 → 228 taken 238 times.
✗ Branch 227 → 230 not taken.
✓ Branch 229 → 230 taken 18 times.
✓ Branch 229 → 231 taken 220 times.
✓ Branch 232 → 233 taken 18 times.
✓ Branch 232 → 234 taken 220 times.
238 if (++visited > MaxVisited || graph.nodes.size() >= MaxEgoNodes)
314 18 break;
315
2/2
✓ Branch 235 → 236 taken 93 times.
✓ Branch 235 → 237 taken 127 times.
220 if (graph.indexOf(n.cardId) >= 0)
316 93 continue;
317
1/1
✓ Branch 241 → 242 taken 127 times.
127 graph.nodes.append(nodeOf(*index.people.value(n.cardId), 2, graph.nodes.at(r).sector,
318 127 index.degree.value(n.cardId)));
319 76 }
320 }
321 }
322
1/1
✓ Branch 252 → 253 taken 126 times.
126 addStoredEdges(input, &graph);
323
1/1
✓ Branch 253 → 254 taken 126 times.
126 addLayers(input, layers, index.people, 0, &graph);
324
1/1
✓ Branch 254 → 255 taken 126 times.
126 Families::addAreas(input, families, centerCardId, &graph);
325 126 return graph;
326 126 }
327
328 30 Graph overview(const Input &input, const Layers &layers) {
329 30 Graph graph;
330
1/1
✓ Branch 2 → 3 taken 30 times.
30 const Index index(input);
331 30 QList<const Person *> people;
332
2/2
✓ Branch 10 → 5 taken 32529 times.
✓ Branch 10 → 11 taken 30 times.
32559 for (const Person &p : input.people)
333
1/2
✓ Branch 6 → 7 taken 32529 times.
✗ Branch 6 → 8 not taken.
32529 if (p.inScope)
334
1/1
✓ Branch 7 → 8 taken 32529 times.
32529 people.append(&p);
335
3/3
✓ Branch 11 → 12 taken 30 times.
✓ Branch 12 → 13 taken 30 times.
✓ Branch 13 → 14 taken 30 times.
456821 std::sort(people.begin(), people.end(), [](const Person *a, const Person *b) { return a->cardId < b->cardId; });
336
4/4
✓ Branch 14 → 15 taken 30 times.
✓ Branch 15 → 16 taken 30 times.
✓ Branch 29 → 17 taken 32529 times.
✓ Branch 29 → 30 taken 30 times.
32559 for (const Person *p : people) {
337 32529 const int degree = index.degree.value(p->cardId);
338
2/2
✓ Branch 19 → 20 taken 522 times.
✓ Branch 19 → 21 taken 32007 times.
32529 if (degree == 0) {
339 522 ++graph.hiddenWithoutRelationships;
340 522 continue;
341 }
342
1/1
✓ Branch 23 → 24 taken 32007 times.
32007 graph.nodes.append(nodeOf(*p, 0, QString(), degree));
343 }
344
1/1
✓ Branch 30 → 31 taken 30 times.
30 addStoredEdges(input, &graph);
345
1/1
✓ Branch 31 → 32 taken 30 times.
30 addLayers(input, layers, index.people, -1, &graph);
346
2/2
✓ Branch 32 → 33 taken 30 times.
✓ Branch 33 → 34 taken 30 times.
30 Families::addAreas(input, Families::of(input), 0, &graph);
347 30 return graph;
348 30 }
349
350 } // namespace RelationGraph
351