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: 93.1% 633 / 0 / 680
Functions: 98.0% 50 / 0 / 51
Branches: 76.7% 1039 / 0 / 1354

presentation/QuickConflictResolver.cpp
Line Branch Exec Source
1 #include "QuickConflictResolver.h"
2
3 #include "QuickRulesBackend.h"
4 #include "app/core/CalendarOutbox.h"
5 #include "app/core/ContactOutbox.h"
6 #include "app/core/SettingsSyncSession.h"
7 #include "data/CalendarStore.h"
8 #include "data/ContactQueries.h"
9 #include "data/ContactStore.h"
10 #include "data/ICalMergePlan.h"
11 #include "data/ICalendar.h"
12 #include "data/MergeValidation.h"
13 #include "data/SettingsMergePlan.h"
14 #include "data/SieveMergePlan.h"
15 #include "data/VCardCodec.h"
16 #include "data/VCardMergePlan.h"
17
18 #include <QCryptographicHash>
19 #include <QElapsedTimer>
20 #include <QJsonDocument>
21 #include <QJsonObject>
22 #include <QLoggingCategory>
23
24 Q_DECLARE_LOGGING_CATEGORY(lcConflict)
25
26 namespace {
27
28 45 Merge::Choice sideChoice(const QString &side) {
29
2/2
✓ Branch 4 → 5 taken 13 times.
✓ Branch 4 → 6 taken 32 times.
45 if (side == QLatin1String("server"))
30 13 return Merge::Choice::Server;
31
2/2
✓ Branch 8 → 9 taken 3 times.
✓ Branch 8 → 10 taken 29 times.
32 if (side == QLatin1String("base"))
32 3 return Merge::Choice::Base;
33
2/2
✓ Branch 12 → 13 taken 27 times.
✓ Branch 12 → 14 taken 2 times.
29 if (side == QLatin1String("local"))
34 27 return Merge::Choice::Local;
35 2 return Merge::Choice::None;
36 }
37
38 9 QString identityHash(const QByteArray &bytes) {
39
3/3
✓ Branch 3 → 4 taken 9 times.
✓ Branch 4 → 5 taken 9 times.
✓ Branch 5 → 6 taken 9 times.
9 return QString::fromLatin1(QCryptographicHash::hash(bytes, QCryptographicHash::Sha256).toHex());
40 }
41
42 2 QString calendarName(CalendarStore *store, const QString &accountId, const QString &path) {
43
1/2
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 28 not taken.
2 if (store)
44
4/5
✓ Branch 3 → 4 taken 2 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 5 → 6 taken 2 times.
✓ Branch 21 → 7 taken 3 times.
✗ Branch 21 → 22 not taken.
3 for (const CalendarInfo &calendar : store->allCalendars())
45
6/8
✓ Branch 9 → 10 taken 3 times.
✗ Branch 9 → 15 not taken.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 15 taken 1 time.
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 15 not taken.
✓ Branch 16 → 17 taken 2 times.
✓ Branch 16 → 19 taken 1 time.
3 if (calendar.accountId == accountId && calendar.path == path && !calendar.displayName.isEmpty())
46
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 27 taken 2 times.
2 return calendar.displayName;
47 ✗ return path.section(QLatin1Char('/'), -2, -2);
48 }
49
50 67 QString titleOfResource(const QString &text, const QString &component) {
51
4/5
✓ Branch 5 → 6 taken 67 times.
✓ Branch 8 → 9 taken 67 times.
✓ Branch 9 → 10 taken 67 times.
✓ Branch 38 → 11 taken 118 times.
✗ Branch 38 → 39 not taken.
118 for (const ICal::Component &child : ICal::parseFirst(text).components)
52
10/15
✓ Branch 13 → 14 taken 67 times.
✓ Branch 13 → 20 taken 51 times.
✓ Branch 17 → 18 taken 67 times.
✓ Branch 18 → 19 taken 67 times.
✗ Branch 18 → 20 not taken.
✓ Branch 21 → 22 taken 67 times.
✓ Branch 21 → 23 taken 51 times.
✓ Branch 23 → 24 taken 67 times.
✓ Branch 23 → 25 taken 51 times.
✓ Branch 25 → 26 taken 67 times.
✓ Branch 25 → 36 taken 51 times.
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 54 not taken.
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 58 not taken.
185 if (child.name == component && !child.hasProperty(QStringLiteral("RECURRENCE-ID")))
53
3/4
✓ Branch 29 → 30 taken 67 times.
✓ Branch 30 → 31 taken 67 times.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 44 taken 67 times.
67 return ICal::unescapeText(child.value(QStringLiteral("SUMMARY")));
54 ✗ return {};
55 }
56
57 27 SyncPayload payloadOf(const QString &json) {
58
4/4
✓ Branch 2 → 3 taken 27 times.
✓ Branch 3 → 4 taken 27 times.
✓ Branch 4 → 5 taken 27 times.
✓ Branch 5 → 6 taken 27 times.
27 return SyncPayload::fromJson(QJsonDocument::fromJson(json.toUtf8()).object());
59 }
60
61 // The kind of a conflict and its plan (empty for a deletion), shared by
62 // open() and preview() so the banner and the sheet always agree.
63 struct Analysis {
64 QString kind;
65 Merge::Plan plan;
66 };
67
68 template <typename Inputs, typename Analyze>
69 95 Analysis analysisOf(const Inputs &inputs, Analyze analyze) {
70 95 Analysis analysis;
71
11/12
(anonymous namespace)::Analysis (anonymous namespace)::analysisOf<ContactOutbox::ConflictInputs, (anonymous namespace)::contactAnalysis(ContactOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}>(ContactOutbox::ConflictInputs const&, (anonymous namespace)::contactAnalysis(ContactOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}):
✓ Branch 4 → 5 taken 13 times.
✓ Branch 4 → 7 taken 1 time.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 12 times.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 25 taken 12 times.
(anonymous namespace)::Analysis (anonymous namespace)::analysisOf<CalendarOutbox::ConflictInputs, (anonymous namespace)::calendarAnalysis(CalendarOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}>(CalendarOutbox::ConflictInputs const&, (anonymous namespace)::calendarAnalysis(CalendarOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}):
✓ Branch 4 → 5 taken 81 times.
✗ Branch 4 → 7 not taken.
✓ Branch 6 → 7 taken 10 times.
✓ Branch 6 → 8 taken 71 times.
✓ Branch 9 → 10 taken 10 times.
✓ Branch 9 → 25 taken 71 times.
95 if (inputs.operation == QLatin1String("delete") || inputs.server.isEmpty()) {
72
9/12
(anonymous namespace)::Analysis (anonymous namespace)::analysisOf<ContactOutbox::ConflictInputs, (anonymous namespace)::contactAnalysis(ContactOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}>(ContactOutbox::ConflictInputs const&, (anonymous namespace)::contactAnalysis(ContactOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}):
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 15 taken 1 time.
✓ Branch 20 → 21 taken 1 time.
✓ Branch 20 → 22 taken 1 time.
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 24 taken 1 time.
(anonymous namespace)::Analysis (anonymous namespace)::analysisOf<CalendarOutbox::ConflictInputs, (anonymous namespace)::calendarAnalysis(CalendarOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}>(CalendarOutbox::ConflictInputs const&, (anonymous namespace)::calendarAnalysis(CalendarOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}):
✓ Branch 11 → 12 taken 10 times.
✗ Branch 11 → 15 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 10 times.
✓ Branch 22 → 23 taken 10 times.
✗ Branch 22 → 24 not taken.
24 analysis.kind = inputs.server.isEmpty() ? QStringLiteral("deletedOnServer") : QStringLiteral("deletedHere");
73 12 return analysis;
74 }
75
12/12
(anonymous namespace)::Analysis (anonymous namespace)::analysisOf<ContactOutbox::ConflictInputs, (anonymous namespace)::contactAnalysis(ContactOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}>(ContactOutbox::ConflictInputs const&, (anonymous namespace)::contactAnalysis(ContactOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}):
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 33 taken 11 times.
✓ Branch 38 → 39 taken 11 times.
✓ Branch 38 → 40 taken 1 time.
✓ Branch 40 → 41 taken 1 time.
✓ Branch 40 → 42 taken 11 times.
(anonymous namespace)::Analysis (anonymous namespace)::analysisOf<CalendarOutbox::ConflictInputs, (anonymous namespace)::calendarAnalysis(CalendarOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}>(CalendarOutbox::ConflictInputs const&, (anonymous namespace)::calendarAnalysis(CalendarOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}):
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 33 taken 70 times.
✓ Branch 38 → 39 taken 70 times.
✓ Branch 38 → 40 taken 1 time.
✓ Branch 40 → 41 taken 1 time.
✓ Branch 40 → 42 taken 70 times.
249 analysis.kind = inputs.fields.contains(QStringLiteral("moved")) ? QStringLiteral("moved") : QStringLiteral("changed");
76
2/2
(anonymous namespace)::Analysis (anonymous namespace)::analysisOf<ContactOutbox::ConflictInputs, (anonymous namespace)::contactAnalysis(ContactOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}>(ContactOutbox::ConflictInputs const&, (anonymous namespace)::contactAnalysis(ContactOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}):
✓ Branch 44 → 45 taken 12 times.
(anonymous namespace)::Analysis (anonymous namespace)::analysisOf<CalendarOutbox::ConflictInputs, (anonymous namespace)::calendarAnalysis(CalendarOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}>(CalendarOutbox::ConflictInputs const&, (anonymous namespace)::calendarAnalysis(CalendarOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}):
✓ Branch 44 → 45 taken 71 times.
83 analysis.plan = analyze(inputs.base, inputs.local, inputs.server);
77
3/4
(anonymous namespace)::Analysis (anonymous namespace)::analysisOf<ContactOutbox::ConflictInputs, (anonymous namespace)::contactAnalysis(ContactOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}>(ContactOutbox::ConflictInputs const&, (anonymous namespace)::contactAnalysis(ContactOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}):
✓ Branch 47 → 48 taken 1 time.
✓ Branch 47 → 55 taken 11 times.
(anonymous namespace)::Analysis (anonymous namespace)::analysisOf<CalendarOutbox::ConflictInputs, (anonymous namespace)::calendarAnalysis(CalendarOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}>(CalendarOutbox::ConflictInputs const&, (anonymous namespace)::calendarAnalysis(CalendarOutbox::ConflictInputs const&)::{lambda(QString const&, QString const&, QString const&)#1}):
✗ Branch 47 → 48 not taken.
✓ Branch 47 → 55 taken 71 times.
83 if (!analysis.plan.comparable)
78 1 analysis.kind = QStringLiteral("raw");
79 83 return analysis;
80 ✗ }
81
82 81 Analysis calendarAnalysis(const CalendarOutbox::ConflictInputs &inputs) {
83 71 return analysisOf(inputs, [](const QString &base, const QString &local, const QString &server) {
84 71 return ICalMergePlan::analyze(base, local, server);
85 81 });
86 }
87
88 14 Analysis contactAnalysis(const ContactOutbox::ConflictInputs &inputs) {
89 12 return analysisOf(inputs, [](const QString &base, const QString &local, const QString &server) {
90 12 return VCardMergePlan::analyze(base, local, server);
91 14 });
92 }
93
94 // A contact is named by {bookId, uid} or by the list's key "<bookId>/<uid>".
95 18 bool contactIdentity(const QVariantMap &identity, qint64 *bookId, QString *uid) {
96
3/3
✓ Branch 5 → 6 taken 18 times.
✓ Branch 8 → 9 taken 7 times.
✓ Branch 8 → 23 taken 11 times.
18 if (identity.contains(QStringLiteral("key")))
97
3/3
✓ Branch 13 → 14 taken 7 times.
✓ Branch 14 → 15 taken 7 times.
✓ Branch 15 → 16 taken 7 times.
14 return ContactQueries::splitKey(identity.value(QStringLiteral("key")).toString(), bookId, uid);
98
2/2
✓ Branch 27 → 28 taken 11 times.
✓ Branch 28 → 29 taken 11 times.
22 *bookId = identity.value(QStringLiteral("bookId")).toLongLong();
99
2/2
✓ Branch 37 → 38 taken 11 times.
✓ Branch 38 → 39 taken 11 times.
22 *uid = identity.value(QStringLiteral("uid")).toString();
100
2/4
✓ Branch 45 → 46 taken 11 times.
✗ Branch 45 → 49 not taken.
✓ Branch 47 → 48 taken 11 times.
✗ Branch 47 → 49 not taken.
11 return *bookId > 0 && !uid->isEmpty();
101 }
102
103 } // namespace
104
105 832 QuickConflictResolver::QuickConflictResolver(Deps deps, QObject *parent)
106
2/2
✓ Branch 5 → 6 taken 832 times.
✓ Branch 6 → 7 taken 832 times.
832 : QObject(parent), m_deps(std::move(deps)), m_rows(this) {
107 1664 m_context.taskTitle = [this](const QString &uid) {
108 ✗ CalendarStore *store = m_deps.calendarStore ? m_deps.calendarStore() : nullptr;
109 ✗ if (!store)
110 ✗ return QString();
111 ✗ const auto task = store->taskFor(uid, m_identity.value(QStringLiteral("accountId")).toString(),
112 ✗ m_identity.value(QStringLiteral("calendarPath")).toString());
113 ✗ return task ? task->summary : QString();
114 832 };
115 832 }
116
117 370 int QuickConflictResolver::questionCount() const {
118
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 369 times.
370 if (!m_active)
119 1 return 0; // the rows of the last conflict stay until the next open()
120 369 int count = 0;
121
2/2
✓ Branch 13 → 7 taken 633 times.
✓ Branch 13 → 14 taken 369 times.
1002 for (const ConflictRows::Row &row : m_rows.rows())
122
2/2
✓ Branch 8 → 9 taken 400 times.
✓ Branch 8 → 10 taken 233 times.
633 count += row.conflict ? 1 : 0;
123 369 return count;
124 }
125
126
2/2
✓ Branch 2 → 3 taken 282 times.
✓ Branch 2 → 7 taken 151 times.
433 int QuickConflictResolver::combinedCount() const { return m_active ? int(m_rows.rows().size()) - questionCount() : 0; }
127
128 1004 bool QuickConflictResolver::canApply() const {
129
4/4
✓ Branch 3 → 4 taken 138 times.
✓ Branch 3 → 15 taken 9 times.
✓ Branch 6 → 7 taken 131 times.
✓ Branch 6 → 15 taken 7 times.
147 return m_active && !m_applying && m_kind != QLatin1String("deletedOnServer") &&
130
8/9
✓ Branch 2 → 3 taken 147 times.
✓ Branch 2 → 15 taken 857 times.
✓ Branch 9 → 10 taken 131 times.
✗ Branch 9 → 15 not taken.
✓ Branch 10 → 11 taken 131 times.
✓ Branch 11 → 12 taken 77 times.
✓ Branch 11 → 15 taken 54 times.
✓ Branch 13 → 14 taken 72 times.
✓ Branch 13 → 15 taken 5 times.
1151 m_kind != QLatin1String("deletedHere") && m_plan.openConflicts() == 0 && m_problems.isEmpty();
131 }
132
133 951 bool QuickConflictResolver::canKeepBoth() const {
134
4/4
✓ Branch 7 → 8 taken 808 times.
✓ Branch 7 → 11 taken 32 times.
✓ Branch 10 → 11 taken 6 times.
✓ Branch 10 → 15 taken 802 times.
2599 return (m_domain == QLatin1String("event") || m_domain == QLatin1String("task") ||
135
4/4
✓ Branch 4 → 5 taken 840 times.
✓ Branch 4 → 11 taken 111 times.
✓ Branch 13 → 14 taken 135 times.
✓ Branch 13 → 15 taken 14 times.
2710 m_domain == QLatin1String("contact")) &&
136 1100 m_kind == QLatin1String("changed");
137 }
138
139 39 bool QuickConflictResolver::touched() const {
140
2/2
✓ Branch 9 → 4 taken 147 times.
✓ Branch 9 → 10 taken 32 times.
179 for (const Merge::Item &item : m_plan.items)
141
2/2
✓ Branch 5 → 6 taken 7 times.
✓ Branch 5 → 7 taken 140 times.
147 if (item.touched)
142 7 return true;
143 32 return false;
144 }
145
146 7 void QuickConflictResolver::setShowOriginal(bool show) {
147
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 6 times.
7 if (m_showOriginal == show)
148 1 return;
149 6 m_showOriginal = show;
150 6 emit changed();
151 }
152
153 // ─── opening ─────────────────────────────────────────────────
154
155 91 bool QuickConflictResolver::open(const QString &domain, const QVariantMap &identity) {
156 91 QElapsedTimer timer;
157 91 timer.start();
158
1/1
✓ Branch 3 → 4 taken 91 times.
91 close();
159 91 m_identity = identity;
160 91 m_domain = domain;
161 91 m_notice.clear();
162 91 m_showOriginal = false;
163 91 bool ok = false;
164
6/6
✓ Branch 9 → 10 taken 46 times.
✓ Branch 9 → 13 taken 45 times.
✓ Branch 12 → 13 taken 15 times.
✓ Branch 12 → 14 taken 31 times.
✓ Branch 15 → 16 taken 60 times.
✓ Branch 15 → 20 taken 31 times.
91 if (domain == QLatin1String("event") || domain == QLatin1String("task"))
165
1/1
✓ Branch 18 → 19 taken 60 times.
60 ok = openEvent(identity, domain == QLatin1String("task"));
166
2/2
✓ Branch 22 → 23 taken 12 times.
✓ Branch 22 → 25 taken 19 times.
31 else if (domain == QLatin1String("contact"))
167
1/1
✓ Branch 23 → 24 taken 12 times.
12 ok = openContact(identity);
168
2/2
✓ Branch 27 → 28 taken 9 times.
✓ Branch 27 → 30 taken 10 times.
19 else if (domain == QLatin1String("rules"))
169
1/1
✓ Branch 28 → 29 taken 9 times.
9 ok = openRules();
170
2/2
✓ Branch 32 → 33 taken 9 times.
✓ Branch 32 → 35 taken 1 time.
10 else if (domain == QLatin1String("settings"))
171
1/1
✓ Branch 33 → 34 taken 9 times.
9 ok = openSettings();
172
2/2
✓ Branch 35 → 36 taken 6 times.
✓ Branch 35 → 51 taken 85 times.
91 if (!ok) {
173
6/6
✓ Branch 36 → 37 taken 6 times.
✓ Branch 40 → 41 taken 6 times.
✓ Branch 41 → 42 taken 6 times.
✓ Branch 42 → 43 taken 6 times.
✓ Branch 46 → 38 taken 6 times.
✓ Branch 46 → 47 taken 6 times.
12 qCInfo(lcConflict) << "resolver: no open conflict for" << domain;
174 6 m_domain.clear();
175
1/1
✓ Branch 48 → 49 taken 6 times.
6 m_identity.clear();
176
1/1
✓ Branch 49 → 50 taken 6 times.
6 emit changed();
177 6 return false;
178 }
179 85 m_active = true;
180
1/1
✓ Branch 51 → 52 taken 85 times.
85 connectSources();
181
1/1
✓ Branch 52 → 53 taken 85 times.
85 rebuild();
182
10/10
✓ Branch 53 → 54 taken 85 times.
✓ Branch 57 → 58 taken 85 times.
✓ Branch 58 → 59 taken 85 times.
✓ Branch 59 → 60 taken 85 times.
✓ Branch 60 → 61 taken 85 times.
✓ Branch 61 → 62 taken 85 times.
✓ Branch 62 → 63 taken 85 times.
✓ Branch 64 → 65 taken 85 times.
✓ Branch 78 → 55 taken 85 times.
✓ Branch 78 → 79 taken 85 times.
170 qCInfo(lcConflict) << "resolver opened" << domain << "kind" << m_kind << "items" << m_plan.items.size()
183
8/8
✓ Branch 65 → 66 taken 85 times.
✓ Branch 66 → 67 taken 85 times.
✓ Branch 67 → 68 taken 85 times.
✓ Branch 68 → 69 taken 85 times.
✓ Branch 69 → 70 taken 85 times.
✓ Branch 70 → 71 taken 85 times.
✓ Branch 71 → 72 taken 85 times.
✓ Branch 73 → 74 taken 85 times.
85 << "questions" << questionCount() << "combined" << combinedCount() << "in" << timer.elapsed()
184
1/1
✓ Branch 74 → 75 taken 85 times.
85 << "ms";
185
1/1
✓ Branch 79 → 80 taken 85 times.
85 checkServer();
186 85 return true;
187 }
188
189 40 QVariantMap QuickConflictResolver::preview(const QString &domain, const QVariantMap &identity) const {
190 40 Analysis analysis;
191
6/6
✓ Branch 4 → 5 taken 37 times.
✓ Branch 4 → 8 taken 3 times.
✓ Branch 7 → 8 taken 25 times.
✓ Branch 7 → 9 taken 12 times.
✓ Branch 10 → 11 taken 28 times.
✓ Branch 10 → 79 taken 12 times.
40 if (domain == QLatin1String("event") || domain == QLatin1String("task")) {
192
3/3
✓ Branch 12 → 13 taken 27 times.
✓ Branch 12 → 15 taken 1 time.
✓ Branch 13 → 14 taken 27 times.
28 const CalendarOutbox *outbox = m_deps.calendarOutbox ? m_deps.calendarOutbox() : nullptr;
193 const CalendarOutbox::ConflictInputs inputs =
194
12/22
✓ Branch 33 → 34 taken 27 times.
✓ Branch 34 → 35 taken 27 times.
✓ Branch 37 → 38 taken 27 times.
✓ Branch 37 → 39 taken 1 time.
✓ Branch 39 → 40 taken 27 times.
✓ Branch 39 → 41 taken 1 time.
✓ Branch 41 → 42 taken 27 times.
✓ Branch 41 → 43 taken 1 time.
✓ Branch 43 → 44 taken 27 times.
✓ Branch 43 → 45 taken 1 time.
✓ Branch 45 → 46 taken 27 times.
✓ Branch 45 → 47 taken 1 time.
✗ Branch 263 → 264 not taken.
✗ Branch 263 → 265 not taken.
✗ Branch 267 → 268 not taken.
✗ Branch 267 → 269 not taken.
✗ Branch 271 → 272 not taken.
✗ Branch 271 → 273 not taken.
✗ Branch 275 → 276 not taken.
✗ Branch 275 → 277 not taken.
✗ Branch 279 → 280 not taken.
✗ Branch 279 → 281 not taken.
82 outbox ? outbox->conflictInputs(identity.value(QStringLiteral("uid")).toString(),
195
12/22
✓ Branch 27 → 28 taken 27 times.
✓ Branch 28 → 29 taken 27 times.
✓ Branch 47 → 48 taken 27 times.
✓ Branch 47 → 49 taken 1 time.
✓ Branch 49 → 50 taken 27 times.
✓ Branch 49 → 51 taken 1 time.
✓ Branch 51 → 52 taken 27 times.
✓ Branch 51 → 53 taken 1 time.
✓ Branch 53 → 54 taken 27 times.
✓ Branch 53 → 55 taken 1 time.
✓ Branch 55 → 56 taken 27 times.
✓ Branch 55 → 57 taken 1 time.
✗ Branch 283 → 284 not taken.
✗ Branch 283 → 285 not taken.
✗ Branch 287 → 288 not taken.
✗ Branch 287 → 289 not taken.
✗ Branch 291 → 292 not taken.
✗ Branch 291 → 293 not taken.
✗ Branch 295 → 296 not taken.
✗ Branch 295 → 297 not taken.
✗ Branch 299 → 300 not taken.
✗ Branch 299 → 301 not taken.
82 identity.value(QStringLiteral("accountId")).toString(),
196
12/22
✓ Branch 21 → 22 taken 27 times.
✓ Branch 22 → 23 taken 27 times.
✓ Branch 57 → 58 taken 27 times.
✓ Branch 57 → 59 taken 1 time.
✓ Branch 59 → 60 taken 27 times.
✓ Branch 59 → 61 taken 1 time.
✓ Branch 61 → 62 taken 27 times.
✓ Branch 61 → 63 taken 1 time.
✓ Branch 63 → 64 taken 27 times.
✓ Branch 63 → 65 taken 1 time.
✓ Branch 65 → 66 taken 27 times.
✓ Branch 65 → 67 taken 1 time.
✗ Branch 303 → 304 not taken.
✗ Branch 303 → 305 not taken.
✗ Branch 307 → 308 not taken.
✗ Branch 307 → 309 not taken.
✗ Branch 311 → 312 not taken.
✗ Branch 311 → 313 not taken.
✗ Branch 315 → 316 not taken.
✗ Branch 315 → 317 not taken.
✗ Branch 319 → 320 not taken.
✗ Branch 319 → 321 not taken.
82 identity.value(QStringLiteral("calendarPath")).toString())
197
3/3
✓ Branch 16 → 17 taken 27 times.
✓ Branch 16 → 36 taken 1 time.
✓ Branch 35 → 37 taken 27 times.
109 : CalendarOutbox::ConflictInputs{};
198
2/2
✓ Branch 67 → 68 taken 14 times.
✓ Branch 67 → 70 taken 14 times.
28 if (!inputs.valid)
199 14 return {};
200
1/1
✓ Branch 70 → 71 taken 14 times.
14 analysis = calendarAnalysis(inputs);
201
4/4
✓ Branch 75 → 76 taken 14 times.
✓ Branch 75 → 78 taken 14 times.
✓ Branch 81 → 82 taken 4 times.
✓ Branch 81 → 112 taken 8 times.
40 } else if (domain == QLatin1String("contact")) {
202
3/3
✓ Branch 83 → 84 taken 3 times.
✓ Branch 83 → 86 taken 1 time.
✓ Branch 84 → 85 taken 3 times.
4 const ContactOutbox *outbox = m_deps.contactOutbox ? m_deps.contactOutbox() : nullptr;
203 4 qint64 bookId = 0;
204 4 QString uid;
205
6/7
✓ Branch 87 → 88 taken 3 times.
✓ Branch 87 → 90 taken 1 time.
✓ Branch 88 → 89 taken 3 times.
✗ Branch 89 → 90 not taken.
✓ Branch 89 → 91 taken 3 times.
✓ Branch 92 → 93 taken 1 time.
✓ Branch 92 → 95 taken 3 times.
4 if (!outbox || !contactIdentity(identity, &bookId, &uid))
206 1 return {};
207
1/1
✓ Branch 95 → 96 taken 3 times.
3 const ContactOutbox::ConflictInputs inputs = outbox->conflictInputs(bookId, uid);
208
1/2
✗ Branch 96 → 97 not taken.
✓ Branch 96 → 99 taken 3 times.
3 if (!inputs.valid)
209 ✗ return {};
210
1/1
✓ Branch 99 → 100 taken 3 times.
3 analysis = contactAnalysis(inputs);
211
5/6
✓ Branch 104 → 105 taken 3 times.
✗ Branch 104 → 106 not taken.
✓ Branch 108 → 109 taken 3 times.
✓ Branch 108 → 111 taken 1 time.
✓ Branch 114 → 115 taken 3 times.
✓ Branch 114 → 147 taken 5 times.
12 } else if (domain == QLatin1String("rules")) {
212
3/3
✓ Branch 116 → 117 taken 2 times.
✓ Branch 116 → 119 taken 1 time.
✓ Branch 117 → 118 taken 2 times.
3 QuickRulesBackend *rules = m_deps.rules ? m_deps.rules() : nullptr;
213
5/6
✓ Branch 120 → 121 taken 2 times.
✓ Branch 120 → 123 taken 1 time.
✗ Branch 122 → 123 not taken.
✓ Branch 122 → 124 taken 2 times.
✓ Branch 125 → 126 taken 1 time.
✓ Branch 125 → 128 taken 2 times.
3 if (!rules || !rules->conflictActive())
214 1 return {};
215 2 const QuickRulesBackend::ConflictInputs inputs = rules->conflictInputs();
216
1/1
✓ Branch 129 → 130 taken 2 times.
2 analysis.plan = SieveMergePlan::analyze(inputs.base, inputs.mine, inputs.theirs);
217
3/6
✓ Branch 132 → 133 taken 2 times.
✗ Branch 132 → 136 not taken.
✗ Branch 141 → 142 not taken.
✓ Branch 141 → 143 taken 2 times.
✓ Branch 143 → 144 taken 2 times.
✗ Branch 143 → 145 not taken.
4 analysis.kind = analysis.plan.comparable ? QStringLiteral("changed") : QStringLiteral("raw");
218
2/2
✓ Branch 149 → 150 taken 4 times.
✓ Branch 149 → 190 taken 1 time.
7 } else if (domain == QLatin1String("settings")) {
219
3/3
✓ Branch 151 → 152 taken 3 times.
✓ Branch 151 → 154 taken 1 time.
✓ Branch 152 → 153 taken 3 times.
4 SettingsSyncSession *session = m_deps.settings ? m_deps.settings() : nullptr;
220
8/8
✓ Branch 155 → 156 taken 3 times.
✓ Branch 155 → 160 taken 1 time.
✓ Branch 159 → 160 taken 1 time.
✓ Branch 159 → 161 taken 2 times.
✓ Branch 162 → 163 taken 3 times.
✓ Branch 162 → 164 taken 1 time.
✓ Branch 164 → 165 taken 2 times.
✓ Branch 164 → 167 taken 2 times.
4 if (!session || session->state() != QLatin1String("conflict"))
221 2 return {};
222 2 const SettingsSyncSession::ConflictInputs inputs = session->conflictInputs();
223 2 std::optional<SyncPayload> base;
224
1/2
✓ Branch 169 → 170 taken 2 times.
✗ Branch 169 → 174 not taken.
2 if (!inputs.base.isEmpty())
225
1/1
✓ Branch 170 → 171 taken 2 times.
2 base = payloadOf(inputs.base);
226
3/3
✓ Branch 174 → 175 taken 2 times.
✓ Branch 175 → 176 taken 2 times.
✓ Branch 176 → 177 taken 2 times.
2 analysis.plan = SettingsMergePlan::analyze(base, payloadOf(inputs.local), payloadOf(inputs.remote));
227 2 analysis.kind = QStringLiteral("changed");
228 2 } else {
229
6/6
✓ Branch 190 → 191 taken 1 time.
✓ Branch 194 → 195 taken 1 time.
✓ Branch 195 → 196 taken 1 time.
✓ Branch 196 → 197 taken 1 time.
✓ Branch 200 → 192 taken 1 time.
✓ Branch 200 → 201 taken 1 time.
2 qCWarning(lcConflict) << "resolver preview: unknown domain" << domain;
230 1 return {};
231 }
232 21 int questions = 0, combined = 0;
233
5/5
✓ Branch 202 → 203 taken 21 times.
✓ Branch 203 → 204 taken 21 times.
✓ Branch 204 → 205 taken 21 times.
✓ Branch 212 → 206 taken 29 times.
✓ Branch 212 → 213 taken 21 times.
50 for (const ConflictRows::Row &row : ConflictRows::build(analysis.plan, m_context))
234
2/2
✓ Branch 207 → 208 taken 17 times.
✓ Branch 207 → 209 taken 12 times.
50 (row.conflict ? questions : combined) += 1;
235
4/4
✓ Branch 219 → 220 taken 7 times.
✓ Branch 219 → 223 taken 12 times.
✓ Branch 222 → 223 taken 3 times.
✓ Branch 222 → 230 taken 4 times.
47 const bool keepBoth = (domain == QLatin1String("event") || domain == QLatin1String("task") ||
236 7 domain == QLatin1String("contact")) &&
237
5/6
✓ Branch 216 → 217 taken 19 times.
✓ Branch 216 → 223 taken 2 times.
✓ Branch 225 → 226 taken 15 times.
✓ Branch 225 → 230 taken 2 times.
✓ Branch 228 → 229 taken 15 times.
✗ Branch 228 → 230 not taken.
40 analysis.kind != QLatin1String("deletedOnServer") && analysis.kind != QLatin1String("deletedHere");
238 42 return {{QStringLiteral("kind"), analysis.kind},
239 42 {QStringLiteral("questions"), questions},
240 42 {QStringLiteral("combined"), combined},
241
2/4
✓ Branch 249 → 250 taken 84 times.
✓ Branch 249 → 251 taken 21 times.
✗ Branch 359 → 360 not taken.
✗ Branch 359 → 361 not taken.
147 {QStringLiteral("keepBoth"), keepBoth}};
242 145 }
243
244 74 bool QuickConflictResolver::openEvent(const QVariantMap &identity, bool task) {
245
3/3
✓ Branch 3 → 4 taken 73 times.
✓ Branch 3 → 6 taken 1 time.
✓ Branch 4 → 5 taken 73 times.
74 CalendarOutbox *outbox = m_deps.calendarOutbox ? m_deps.calendarOutbox() : nullptr;
246
2/2
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 73 times.
74 if (!outbox)
247 1 return false;
248
2/2
✓ Branch 13 → 14 taken 73 times.
✓ Branch 14 → 15 taken 73 times.
146 const QString uid = identity.value(QStringLiteral("uid")).toString();
249
2/2
✓ Branch 23 → 24 taken 73 times.
✓ Branch 24 → 25 taken 73 times.
146 const QString accountId = identity.value(QStringLiteral("accountId")).toString();
250
2/2
✓ Branch 33 → 34 taken 73 times.
✓ Branch 34 → 35 taken 73 times.
146 const QString calendarPath = identity.value(QStringLiteral("calendarPath")).toString();
251
1/1
✓ Branch 39 → 40 taken 73 times.
73 const CalendarOutbox::ConflictInputs inputs = outbox->conflictInputs(uid, accountId, calendarPath);
252
2/2
✓ Branch 40 → 41 taken 6 times.
✓ Branch 40 → 42 taken 67 times.
73 if (!inputs.valid)
253 6 return false;
254 67 const QString component = inputs.componentType;
255
7/8
✓ Branch 45 → 46 taken 50 times.
✓ Branch 45 → 47 taken 17 times.
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 50 taken 50 times.
✓ Branch 55 → 56 taken 50 times.
✓ Branch 55 → 57 taken 17 times.
✓ Branch 57 → 58 taken 17 times.
✓ Branch 57 → 59 taken 50 times.
134 m_domain = component == QLatin1String("VTODO") || task ? QStringLiteral("task") : QStringLiteral("event");
256 67 m_serverEtag = inputs.serverEtag;
257
1/1
✓ Branch 60 → 61 taken 67 times.
67 m_title = titleOfResource(inputs.local, component);
258
1/2
✗ Branch 64 → 65 not taken.
✓ Branch 64 → 69 taken 67 times.
67 if (m_title.isEmpty())
259 ✗ m_title = titleOfResource(inputs.server, component);
260
1/1
✓ Branch 69 → 70 taken 67 times.
67 m_deletionChanges.clear();
261
2/3
✓ Branch 71 → 72 taken 67 times.
✗ Branch 71 → 74 not taken.
✓ Branch 72 → 73 taken 67 times.
67 CalendarStore *store = m_deps.calendarStore ? m_deps.calendarStore() : nullptr;
262
1/1
✓ Branch 75 → 76 taken 67 times.
67 Analysis analysis = calendarAnalysis(inputs);
263 67 m_kind = analysis.kind;
264
5/6
✓ Branch 79 → 80 taken 59 times.
✓ Branch 79 → 83 taken 8 times.
✗ Branch 82 → 83 not taken.
✓ Branch 82 → 84 taken 59 times.
✓ Branch 85 → 86 taken 8 times.
✓ Branch 85 → 144 taken 59 times.
67 if (m_kind == QLatin1String("deletedOnServer") || m_kind == QLatin1String("deletedHere")) {
265 // Deletion card (§4.5): what changed on the side that kept it.
266 8 const bool deletedOnServer = m_kind == QLatin1String("deletedOnServer");
267
2/4
✓ Branch 88 → 89 taken 8 times.
✗ Branch 88 → 90 not taken.
✓ Branch 89 → 91 taken 8 times.
✗ Branch 90 → 91 not taken.
16 m_subtitle = deletedOnServer ? tr("Deleted on the server, changed on this computer")
268 8 : tr("Deleted on this computer, changed on the server");
269
1/2
✓ Branch 93 → 94 taken 8 times.
✗ Branch 93 → 95 not taken.
8 const Merge::Plan changes = deletedOnServer ? ICalMergePlan::analyze(inputs.base, inputs.local, inputs.base)
270
1/2
✓ Branch 94 → 96 taken 8 times.
✗ Branch 95 → 96 not taken.
8 : ICalMergePlan::analyze(inputs.base, inputs.base, inputs.server);
271
5/5
✓ Branch 96 → 97 taken 8 times.
✓ Branch 97 → 98 taken 8 times.
✓ Branch 98 → 99 taken 8 times.
✓ Branch 132 → 100 taken 8 times.
✓ Branch 132 → 133 taken 8 times.
16 for (const ConflictRows::Row &row : ConflictRows::build(changes, m_context))
272
1/1
✓ Branch 118 → 119 taken 8 times.
24 m_deletionChanges.append(QVariantMap{{QStringLiteral("label"), row.label},
273 16 {QStringLiteral("before"), row.baseText},
274
2/4
✓ Branch 122 → 123 taken 24 times.
✓ Branch 122 → 124 taken 8 times.
✗ Branch 225 → 226 not taken.
✗ Branch 225 → 227 not taken.
56 {QStringLiteral("after"), deletedOnServer ? row.localText : row.serverText}});
275
1/2
✗ Branch 135 → 136 not taken.
✓ Branch 135 → 140 taken 8 times.
8 if (m_title.isEmpty())
276 ✗ m_title = titleOfResource(inputs.base, component);
277
1/1
✓ Branch 140 → 141 taken 8 times.
8 setPlan({}, false);
278 8 return true;
279 8 }
280
2/2
✓ Branch 146 → 147 taken 1 time.
✓ Branch 146 → 157 taken 58 times.
59 if (m_kind == QLatin1String("moved"))
281
1/1
✓ Branch 147 → 148 taken 1 time.
1 m_subtitle = tr("Moved to %1 on this computer; changed in %2 on the server")
282
3/3
✓ Branch 148 → 149 taken 1 time.
✓ Branch 149 → 150 taken 1 time.
✓ Branch 150 → 151 taken 1 time.
1 .arg(calendarName(store, accountId, calendarPath), calendarName(store, accountId, inputs.movedFrom));
283
1/2
✗ Branch 159 → 160 not taken.
✓ Branch 159 → 164 taken 58 times.
58 else if (m_kind == QLatin1String("raw"))
284 ✗ m_subtitle = tr("Changed on this computer and on the server — can't be compared field by field");
285 else
286
1/1
✓ Branch 164 → 165 taken 58 times.
58 m_subtitle = tr("Changed on this computer and on the server");
287
1/1
✓ Branch 170 → 171 taken 59 times.
59 setPlan(std::move(analysis.plan), false);
288 59 return true;
289 105 }
290
291 16 bool QuickConflictResolver::openContact(const QVariantMap &identity) {
292
3/3
✓ Branch 3 → 4 taken 15 times.
✓ Branch 3 → 6 taken 1 time.
✓ Branch 4 → 5 taken 15 times.
16 ContactOutbox *outbox = m_deps.contactOutbox ? m_deps.contactOutbox() : nullptr;
293 16 qint64 bookId = 0;
294 16 QString uid;
295
6/7
✓ Branch 7 → 8 taken 15 times.
✓ Branch 7 → 10 taken 1 time.
✓ Branch 8 → 9 taken 15 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 15 times.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 15 times.
16 if (!outbox || !contactIdentity(identity, &bookId, &uid))
296 1 return false;
297
2/4
✓ Branch 26 → 27 taken 30 times.
✓ Branch 26 → 28 taken 15 times.
✗ Branch 165 → 166 not taken.
✗ Branch 165 → 167 not taken.
75 m_identity = QVariantMap{{QStringLiteral("bookId"), bookId}, {QStringLiteral("uid"), uid}};
298
1/1
✓ Branch 32 → 33 taken 15 times.
15 const ContactOutbox::ConflictInputs inputs = outbox->conflictInputs(bookId, uid);
299
2/2
✓ Branch 33 → 34 taken 4 times.
✓ Branch 33 → 35 taken 11 times.
15 if (!inputs.valid)
300 4 return false;
301 11 m_serverEtag = inputs.serverEtag;
302
1/1
✓ Branch 36 → 37 taken 11 times.
11 ContactCard card;
303
4/5
✓ Branch 38 → 39 taken 1 time.
✓ Branch 38 → 40 taken 10 times.
✓ Branch 41 → 42 taken 11 times.
✓ Branch 42 → 43 taken 11 times.
✗ Branch 42 → 44 not taken.
11 if (VCardCodec::parse(inputs.local.isEmpty() ? inputs.server : inputs.local, &card))
304 11 m_title = card.formattedName;
305
1/1
✓ Branch 44 → 45 taken 11 times.
11 m_deletionChanges.clear();
306
1/1
✓ Branch 45 → 46 taken 11 times.
11 Analysis analysis = contactAnalysis(inputs);
307 11 m_kind = analysis.kind;
308
6/6
✓ Branch 49 → 50 taken 10 times.
✓ Branch 49 → 53 taken 1 time.
✓ Branch 52 → 53 taken 1 time.
✓ Branch 52 → 54 taken 9 times.
✓ Branch 55 → 56 taken 2 times.
✓ Branch 55 → 108 taken 9 times.
11 if (m_kind == QLatin1String("deletedOnServer") || m_kind == QLatin1String("deletedHere")) {
309 2 const bool deletedOnServer = m_kind == QLatin1String("deletedOnServer");
310
4/4
✓ Branch 58 → 59 taken 1 time.
✓ Branch 58 → 60 taken 1 time.
✓ Branch 59 → 61 taken 1 time.
✓ Branch 60 → 61 taken 1 time.
4 m_subtitle = deletedOnServer ? tr("Deleted on the server, changed on this computer")
311 2 : tr("Deleted on this computer, changed on the server");
312
2/2
✓ Branch 63 → 64 taken 1 time.
✓ Branch 63 → 65 taken 1 time.
2 const Merge::Plan changes = deletedOnServer ? VCardMergePlan::analyze(inputs.base, inputs.local, inputs.base)
313
2/2
✓ Branch 64 → 66 taken 1 time.
✓ Branch 65 → 66 taken 1 time.
2 : VCardMergePlan::analyze(inputs.base, inputs.base, inputs.server);
314
5/5
✓ Branch 66 → 67 taken 2 times.
✓ Branch 67 → 68 taken 2 times.
✓ Branch 68 → 69 taken 2 times.
✓ Branch 102 → 70 taken 2 times.
✓ Branch 102 → 103 taken 2 times.
4 for (const ConflictRows::Row &row : ConflictRows::build(changes, m_context))
315
1/1
✓ Branch 88 → 89 taken 2 times.
6 m_deletionChanges.append(QVariantMap{{QStringLiteral("label"), row.label},
316 4 {QStringLiteral("before"), row.baseText},
317
2/4
✓ Branch 92 → 93 taken 6 times.
✓ Branch 92 → 94 taken 2 times.
✗ Branch 193 → 194 not taken.
✗ Branch 193 → 195 not taken.
14 {QStringLiteral("after"), deletedOnServer ? row.localText : row.serverText}});
318
1/1
✓ Branch 104 → 105 taken 2 times.
2 setPlan({}, false);
319 2 return true;
320 2 }
321
2/2
✓ Branch 110 → 111 taken 1 time.
✓ Branch 110 → 142 taken 8 times.
9 if (m_kind == QLatin1String("moved")) {
322
2/3
✓ Branch 112 → 113 taken 1 time.
✗ Branch 112 → 115 not taken.
✓ Branch 113 → 114 taken 1 time.
1 ContactStore *store = m_deps.contactStore ? m_deps.contactStore() : nullptr;
323
2/3
✓ Branch 116 → 117 taken 1 time.
✗ Branch 116 → 118 not taken.
✓ Branch 117 → 119 taken 1 time.
1 const auto target = store ? store->book(bookId) : std::nullopt;
324
2/3
✓ Branch 119 → 120 taken 1 time.
✗ Branch 119 → 121 not taken.
✓ Branch 120 → 122 taken 1 time.
1 const auto source = store ? store->book(inputs.movedFrom) : std::nullopt;
325
1/1
✓ Branch 122 → 123 taken 1 time.
1 m_subtitle = tr("Moved to %1 on this computer; changed in %2 on the server")
326
3/5
✓ Branch 124 → 125 taken 1 time.
✗ Branch 124 → 127 not taken.
✓ Branch 129 → 130 taken 1 time.
✗ Branch 129 → 132 not taken.
✓ Branch 133 → 134 taken 1 time.
1 .arg(target ? target->displayName : QString(), source ? source->displayName : QString());
327
2/2
✓ Branch 144 → 145 taken 1 time.
✓ Branch 144 → 149 taken 7 times.
9 } else if (m_kind == QLatin1String("raw")) {
328
1/1
✓ Branch 145 → 146 taken 1 time.
1 m_subtitle = tr("Changed on this computer and on the server — can't be compared field by field");
329 } else {
330
1/1
✓ Branch 149 → 150 taken 7 times.
7 m_subtitle = tr("Changed on this computer and on the server");
331 }
332
1/1
✓ Branch 155 → 156 taken 9 times.
9 setPlan(std::move(analysis.plan), false);
333 9 return true;
334 69 }
335
336 12 bool QuickConflictResolver::openRules() {
337
3/3
✓ Branch 3 → 4 taken 11 times.
✓ Branch 3 → 6 taken 1 time.
✓ Branch 4 → 5 taken 11 times.
12 QuickRulesBackend *rules = m_deps.rules ? m_deps.rules() : nullptr;
338
6/6
✓ Branch 7 → 8 taken 11 times.
✓ Branch 7 → 10 taken 1 time.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 11 taken 9 times.
✓ Branch 12 → 13 taken 3 times.
✓ Branch 12 → 14 taken 9 times.
12 if (!rules || !rules->conflictActive())
339 3 return false;
340 9 const QuickRulesBackend::ConflictInputs inputs = rules->conflictInputs();
341
1/1
✓ Branch 15 → 16 taken 9 times.
9 m_serverEtag = identityHash(inputs.theirs);
342
1/1
✓ Branch 18 → 19 taken 9 times.
9 m_title = tr("Rules");
343 9 m_kind = QStringLiteral("changed");
344
1/1
✓ Branch 27 → 28 taken 9 times.
9 m_subtitle = tr("Changed on this computer and on the server");
345
1/1
✓ Branch 30 → 31 taken 9 times.
9 m_deletionChanges.clear();
346
1/1
✓ Branch 31 → 32 taken 9 times.
9 Merge::Plan plan = SieveMergePlan::analyze(inputs.base, inputs.mine, inputs.theirs);
347
1/2
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 43 taken 9 times.
9 if (!plan.comparable) {
348 ✗ m_kind = QStringLiteral("raw");
349 ✗ m_subtitle = tr("Changed on this computer and on the server — can't be compared rule by rule");
350 }
351
1/1
✓ Branch 44 → 45 taken 9 times.
9 setPlan(plan, false);
352 9 return true;
353 9 }
354
355 9 bool QuickConflictResolver::openSettings() {
356
3/3
✓ Branch 3 → 4 taken 8 times.
✓ Branch 3 → 6 taken 1 time.
✓ Branch 4 → 5 taken 8 times.
9 SettingsSyncSession *session = m_deps.settings ? m_deps.settings() : nullptr;
357
8/8
✓ Branch 7 → 8 taken 8 times.
✓ Branch 7 → 12 taken 1 time.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 7 times.
✓ Branch 14 → 15 taken 8 times.
✓ Branch 14 → 16 taken 1 time.
✓ Branch 16 → 17 taken 2 times.
✓ Branch 16 → 18 taken 7 times.
9 if (!session || session->state() != QLatin1String("conflict"))
358 2 return false;
359 7 const SettingsSyncSession::ConflictInputs inputs = session->conflictInputs();
360
3/6
✓ Branch 20 → 21 taken 7 times.
✗ Branch 20 → 23 not taken.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 7 times.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 7 times.
7 if (inputs.local.isEmpty() || inputs.remote.isEmpty())
361 ✗ return false;
362
2/2
✓ Branch 28 → 29 taken 7 times.
✓ Branch 29 → 30 taken 7 times.
7 m_serverEtag = QString::fromLatin1(session->shownRemoteIdentity().toHex());
363
1/1
✓ Branch 34 → 35 taken 7 times.
7 m_title = tr("Synchronized settings");
364 7 m_kind = QStringLiteral("changed");
365
1/1
✓ Branch 43 → 44 taken 7 times.
7 m_subtitle = tr("Changed on this installation and on another one");
366
1/1
✓ Branch 46 → 47 taken 7 times.
7 m_serverLabel = tr("from your other installation");
367
1/1
✓ Branch 49 → 50 taken 7 times.
7 m_deletionChanges.clear();
368 7 std::optional<SyncPayload> base;
369
1/2
✓ Branch 51 → 52 taken 7 times.
✗ Branch 51 → 56 not taken.
7 if (!inputs.base.isEmpty())
370
1/1
✓ Branch 52 → 53 taken 7 times.
7 base = payloadOf(inputs.base);
371
4/4
✓ Branch 56 → 57 taken 7 times.
✓ Branch 57 → 58 taken 7 times.
✓ Branch 58 → 59 taken 7 times.
✓ Branch 59 → 60 taken 7 times.
7 setPlan(SettingsMergePlan::analyze(base, payloadOf(inputs.local), payloadOf(inputs.remote)), false);
372 7 return true;
373 7 }
374
375 102 void QuickConflictResolver::setPlan(Merge::Plan plan, bool reload) {
376 102 m_changedAgain.clear();
377
2/2
✓ Branch 3 → 4 taken 8 times.
✓ Branch 3 → 53 taken 94 times.
102 if (reload) {
378 8 QHash<QString, QString> before;
379
2/2
✓ Branch 11 → 7 taken 18 times.
✓ Branch 11 → 12 taken 8 times.
26 for (const ConflictRows::Row &row : m_rows.rows())
380
1/1
✓ Branch 8 → 9 taken 18 times.
18 before.insert(row.id, row.serverText);
381
5/5
✓ Branch 12 → 13 taken 8 times.
✓ Branch 13 → 14 taken 8 times.
✓ Branch 14 → 15 taken 8 times.
✓ Branch 31 → 16 taken 18 times.
✓ Branch 31 → 32 taken 8 times.
26 for (const ConflictRows::Row &row : ConflictRows::build(plan, m_context))
382
8/8
✓ Branch 18 → 19 taken 8 times.
✓ Branch 18 → 22 taken 10 times.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 21 → 23 taken 7 times.
✓ Branch 24 → 25 taken 8 times.
✓ Branch 24 → 26 taken 10 times.
✓ Branch 26 → 27 taken 11 times.
✓ Branch 26 → 29 taken 7 times.
18 if (!before.contains(row.id) || before.value(row.id) != row.serverText)
383
1/1
✓ Branch 27 → 28 taken 11 times.
19 m_changedAgain.insert(row.id);
384
1/1
✓ Branch 33 → 34 taken 8 times.
8 const int carried = Merge::carryChoices(m_plan, &plan);
385
1/1
✓ Branch 34 → 35 taken 8 times.
8 m_notice = tr("Changed again on the server — your choices were kept where they still apply.");
386
9/9
✓ Branch 37 → 38 taken 8 times.
✓ Branch 41 → 42 taken 8 times.
✓ Branch 42 → 43 taken 8 times.
✓ Branch 43 → 44 taken 8 times.
✓ Branch 44 → 45 taken 8 times.
✓ Branch 45 → 46 taken 8 times.
✓ Branch 46 → 47 taken 8 times.
✓ Branch 50 → 39 taken 8 times.
✓ Branch 50 → 51 taken 8 times.
16 qCInfo(lcConflict) << "resolver reloaded" << m_domain << "carried" << carried << "choices";
387 8 }
388 102 m_plan = std::move(plan);
389 102 ++m_photoSerial;
390 102 }
391
392 147 void QuickConflictResolver::rebuild() {
393
1/1
✓ Branch 2 → 3 taken 147 times.
147 validate();
394
1/1
✓ Branch 3 → 4 taken 147 times.
147 QList<ConflictRows::Row> rows = ConflictRows::build(m_plan, m_context);
395
4/4
✓ Branch 4 → 5 taken 147 times.
✓ Branch 5 → 6 taken 147 times.
✓ Branch 30 → 7 taken 256 times.
✓ Branch 30 → 31 taken 147 times.
403 for (ConflictRows::Row &row : rows) {
396 256 row.changedAgain = m_changedAgain.contains(row.id);
397
4/4
✓ Branch 9 → 10 taken 256 times.
✓ Branch 10 → 11 taken 256 times.
✓ Branch 27 → 12 taken 15 times.
✓ Branch 27 → 28 taken 256 times.
271 for (const Merge::ValidationProblem &problem : m_problems)
398
4/4
✓ Branch 13 → 14 taken 15 times.
✓ Branch 14 → 15 taken 15 times.
✓ Branch 24 → 16 taken 15 times.
✓ Branch 24 → 25 taken 15 times.
30 for (const QString &id : row.itemIds)
399
3/4
✓ Branch 17 → 18 taken 15 times.
✗ Branch 17 → 20 not taken.
✓ Branch 19 → 20 taken 10 times.
✓ Branch 19 → 21 taken 5 times.
15 row.invalid = row.invalid || problem.itemIds.contains(id);
400 }
401
1/1
✓ Branch 31 → 32 taken 147 times.
147 m_rows.setRows(rows);
402
1/1
✓ Branch 32 → 33 taken 147 times.
147 emit changed();
403 147 }
404
405 147 void QuickConflictResolver::validate() {
406
5/5
✓ Branch 3 → 4 taken 137 times.
✓ Branch 3 → 6 taken 10 times.
✓ Branch 4 → 5 taken 137 times.
✓ Branch 5 → 6 taken 92 times.
✓ Branch 5 → 7 taken 45 times.
294 m_problems = m_plan.items.isEmpty() || m_plan.openConflicts() > 0 ? QList<Merge::ValidationProblem>{}
407
1/1
✓ Branch 7 → 8 taken 45 times.
147 : MergeValidation::validate(m_plan);
408 147 m_validationMessage.clear();
409
2/2
✓ Branch 12 → 13 taken 142 times.
✓ Branch 12 → 14 taken 5 times.
147 if (m_problems.isEmpty())
410 142 return;
411 5 QStringList labels;
412
5/5
✓ Branch 14 → 15 taken 5 times.
✓ Branch 15 → 16 taken 5 times.
✓ Branch 16 → 17 taken 5 times.
✓ Branch 36 → 18 taken 15 times.
✓ Branch 36 → 37 taken 5 times.
20 for (const ConflictRows::Row &row : ConflictRows::build(m_plan, m_context))
413
2/2
✓ Branch 33 → 21 taken 15 times.
✓ Branch 33 → 34 taken 15 times.
30 for (const QString &id : row.itemIds)
414
6/7
✓ Branch 22 → 23 taken 15 times.
✓ Branch 24 → 25 taken 10 times.
✓ Branch 24 → 28 taken 5 times.
✓ Branch 26 → 27 taken 10 times.
✗ Branch 26 → 28 not taken.
✓ Branch 29 → 30 taken 10 times.
✓ Branch 29 → 31 taken 5 times.
15 if (m_problems.first().itemIds.contains(id) && !labels.contains(row.label))
415
1/1
✓ Branch 30 → 31 taken 10 times.
15 labels.append(row.label);
416
2/2
✓ Branch 50 → 51 taken 5 times.
✓ Branch 51 → 52 taken 5 times.
10 m_validationMessage = ConflictValueText::problemText(m_problems.first().rule) +
417
9/25
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 41 taken 5 times.
✓ Branch 48 → 49 taken 5 times.
✓ Branch 49 → 50 taken 5 times.
✓ Branch 52 → 53 taken 5 times.
✓ Branch 57 → 58 taken 5 times.
✗ Branch 57 → 59 not taken.
✓ Branch 59 → 60 taken 5 times.
✗ Branch 59 → 61 not taken.
✓ Branch 61 → 62 taken 5 times.
✗ Branch 61 → 63 not taken.
✓ Branch 63 → 64 taken 5 times.
✗ Branch 63 → 65 not taken.
✓ Branch 65 → 66 taken 5 times.
✗ Branch 65 → 67 not taken.
✗ Branch 92 → 93 not taken.
✗ Branch 92 → 94 not taken.
✗ Branch 96 → 97 not taken.
✗ Branch 96 → 98 not taken.
✗ Branch 100 → 101 not taken.
✗ Branch 100 → 102 not taken.
✗ Branch 105 → 106 not taken.
✗ Branch 105 → 107 not taken.
✗ Branch 109 → 110 not taken.
✗ Branch 109 → 111 not taken.
25 (labels.isEmpty() ? QString() : QStringLiteral(" (%1)").arg(labels.join(QStringLiteral(", "))));
418
7/7
✓ Branch 67 → 68 taken 5 times.
✓ Branch 71 → 72 taken 5 times.
✓ Branch 72 → 73 taken 5 times.
✓ Branch 73 → 74 taken 5 times.
✓ Branch 74 → 75 taken 5 times.
✓ Branch 78 → 69 taken 5 times.
✓ Branch 78 → 79 taken 5 times.
10 qCInfo(lcConflict) << "resolver validation:" << m_problems.first().rule;
419 5 }
420
421 75 QList<const Merge::Item *> QuickConflictResolver::membersOf(const QString &rowId) const {
422 75 QList<const Merge::Item *> members;
423
3/3
✓ Branch 3 → 4 taken 75 times.
✓ Branch 4 → 5 taken 68 times.
✓ Branch 4 → 15 taken 7 times.
75 if (const ConflictRows::Row *row = ConflictRows::find(m_rows.rows(), rowId))
424
2/2
✓ Branch 13 → 7 taken 99 times.
✓ Branch 13 → 14 taken 68 times.
167 for (const QString &id : row->itemIds)
425
2/3
✓ Branch 8 → 9 taken 99 times.
✓ Branch 9 → 10 taken 99 times.
✗ Branch 9 → 11 not taken.
99 if (const Merge::Item *item = m_plan.item(id))
426
1/1
✓ Branch 10 → 11 taken 99 times.
99 members.append(item);
427 75 return members;
428 ✗ }
429
430 // ─── choices ─────────────────────────────────────────────────
431
432 37 bool QuickConflictResolver::choose(const QString &rowId, const QString &side) {
433 37 const Merge::Choice choice = sideChoice(side);
434
1/1
✓ Branch 3 → 4 taken 37 times.
37 const QList<const Merge::Item *> members = membersOf(rowId);
435
8/8
✓ Branch 4 → 5 taken 35 times.
✓ Branch 4 → 8 taken 2 times.
✓ Branch 5 → 6 taken 33 times.
✓ Branch 5 → 8 taken 2 times.
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 9 taken 31 times.
✓ Branch 10 → 11 taken 6 times.
✓ Branch 10 → 12 taken 31 times.
37 if (!m_active || choice == Merge::Choice::None || members.isEmpty())
436 6 return false;
437 31 QStringList ids;
438
2/2
✓ Branch 18 → 14 taken 46 times.
✓ Branch 18 → 19 taken 31 times.
77 for (const Merge::Item *item : members)
439
1/1
✓ Branch 15 → 16 taken 46 times.
46 ids.append(item->id);
440 31 bool ok = false;
441
4/4
✓ Branch 19 → 20 taken 31 times.
✓ Branch 20 → 21 taken 31 times.
✓ Branch 30 → 22 taken 46 times.
✓ Branch 30 → 31 taken 31 times.
77 for (const QString &id : ids)
442
2/5
✓ Branch 23 → 24 taken 46 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 46 times.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
46 ok = Merge::choose(&m_plan, id, choice) || ok;
443
1/1
✓ Branch 31 → 32 taken 31 times.
31 rebuild();
444 31 return ok;
445 37 }
446
447 5 void QuickConflictResolver::chooseAll(const QString &side) {
448 5 const Merge::Choice choice = sideChoice(side);
449
6/6
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 6 taken 1 time.
✓ Branch 4 → 5 taken 3 times.
✓ Branch 4 → 7 taken 1 time.
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 7 taken 1 time.
5 if (!m_active || (choice != Merge::Choice::Local && choice != Merge::Choice::Server))
450 3 return;
451 2 Merge::chooseAll(&m_plan, choice);
452 2 rebuild();
453 }
454
455 6 bool QuickConflictResolver::setValueIncluded(const QString &rowId, const QString &valueId, bool included) {
456
1/1
✓ Branch 2 → 3 taken 6 times.
6 const Merge::Item *item = m_plan.item(valueId);
457
9/11
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 7 taken 2 times.
✓ Branch 4 → 5 taken 4 times.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 3 times.
✓ Branch 9 → 10 taken 4 times.
✓ Branch 9 → 11 taken 2 times.
✓ Branch 11 → 12 taken 3 times.
✓ Branch 11 → 13 taken 3 times.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 44 not taken.
6 if (!item || !membersOf(rowId).contains(item))
458 3 return false;
459 3 Merge::Choice choice = Merge::Choice::None;
460
2/2
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 21 taken 2 times.
3 if (included)
461
1/4
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
1 choice = item->local.present ? Merge::Choice::Local : item->server.present ? Merge::Choice::Server
462 : Merge::Choice::Base;
463 else
464
2/4
✓ Branch 21 → 22 taken 2 times.
✗ Branch 21 → 29 not taken.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 27 taken 2 times.
2 choice = !item->local.present ? Merge::Choice::Local : !item->server.present ? Merge::Choice::Server
465 ✗ : !item->base.present ? Merge::Choice::Base
466 : Merge::Choice::None;
467
4/7
✓ Branch 31 → 32 taken 3 times.
✗ Branch 31 → 34 not taken.
✓ Branch 32 → 33 taken 3 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 3 times.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 3 times.
3 if (choice == Merge::Choice::None || !Merge::choose(&m_plan, valueId, choice))
468 ✗ return false;
469
1/1
✓ Branch 38 → 39 taken 3 times.
3 rebuild();
470 3 return true;
471 }
472
473 3 bool QuickConflictResolver::takeBoth(const QString &rowId) {
474
1/1
✓ Branch 2 → 3 taken 3 times.
3 const QList<const Merge::Item *> members = membersOf(rowId);
475
5/6
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 7 not taken.
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 8 taken 1 time.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 11 taken 1 time.
3 if (members.isEmpty() || members.first()->kind != Merge::Kind::SetValue)
476 2 return false;
477 1 QStringList ids;
478
2/2
✓ Branch 19 → 13 taken 3 times.
✓ Branch 19 → 20 taken 1 time.
4 for (const Merge::Item *item : members)
479
3/4
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 16 taken 2 times.
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 17 not taken.
3 if (item->local.present || item->server.present)
480
1/1
✓ Branch 16 → 17 taken 3 times.
3 ids.append(item->id);
481
4/4
✓ Branch 20 → 21 taken 1 time.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 31 → 23 taken 3 times.
✓ Branch 31 → 32 taken 1 time.
4 for (const QString &id : ids) {
482
1/1
✓ Branch 24 → 25 taken 3 times.
3 const Merge::Item *item = m_plan.item(id);
483
3/3
✓ Branch 25 → 26 taken 2 times.
✓ Branch 25 → 27 taken 1 time.
✓ Branch 28 → 29 taken 3 times.
3 Merge::choose(&m_plan, id, item->local.present ? Merge::Choice::Local : Merge::Choice::Server);
484 }
485
1/1
✓ Branch 32 → 33 taken 1 time.
1 rebuild();
486 1 return true;
487 3 }
488
489 3 bool QuickConflictResolver::chooseHunk(const QString &rowId, int hunk, const QString &side) {
490
1/1
✓ Branch 2 → 3 taken 3 times.
3 const QList<const Merge::Item *> members = membersOf(rowId);
491
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 3 times.
3 const Merge::Choice choice = side == QLatin1String("both") ? Merge::Choice::Both : sideChoice(side);
492
6/9
✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 14 taken 1 time.
✓ Branch 10 → 11 taken 2 times.
✗ Branch 10 → 14 not taken.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 15 not taken.
✓ Branch 16 → 17 taken 3 times.
✗ Branch 16 → 18 not taken.
3 if (members.size() != 1 || choice == Merge::Choice::None || !Merge::chooseHunk(&m_plan, members.first()->id, hunk, choice))
493 3 return false;
494 ✗ rebuild();
495 ✗ return true;
496 3 }
497
498 8 bool QuickConflictResolver::acceptText(const QString &rowId) {
499
1/1
✓ Branch 2 → 3 taken 8 times.
8 const QList<const Merge::Item *> members = membersOf(rowId);
500
9/9
✓ Branch 4 → 5 taken 7 times.
✓ Branch 4 → 10 taken 1 time.
✓ Branch 6 → 7 taken 6 times.
✓ Branch 6 → 10 taken 1 time.
✓ Branch 8 → 9 taken 6 times.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 5 times.
✓ Branch 12 → 13 taken 3 times.
✓ Branch 12 → 14 taken 5 times.
8 if (members.size() != 1 || members.first()->kind != Merge::Kind::Text || members.first()->hunks.conflicts() > 0)
501 3 return false;
502 5 const Merge::Item *item = members.first();
503 // D-8: the pre-selected hunks, confirmed by the user.
504
1/1
✓ Branch 15 → 16 taken 5 times.
5 const TextMerge::Pick first = TextMerge::defaultPicks(item->hunks).value(0, TextMerge::Pick::Local);
505
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 5 times.
5 const Merge::Choice choice = first == TextMerge::Pick::Server ? Merge::Choice::Server : Merge::Choice::Local;
506
2/3
✓ Branch 21 → 22 taken 5 times.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 5 times.
5 if (!Merge::chooseHunk(&m_plan, item->id, 0, choice))
507 ✗ return false;
508
1/1
✓ Branch 24 → 25 taken 5 times.
5 rebuild();
509 5 return true;
510 8 }
511
512 3 bool QuickConflictResolver::editText(const QString &rowId, const QString &text) {
513
1/1
✓ Branch 2 → 3 taken 3 times.
3 const QList<const Merge::Item *> members = membersOf(rowId);
514
7/7
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 8 taken 1 time.
✓ Branch 6 → 7 taken 2 times.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 1 time.
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 12 taken 1 time.
3 if (members.size() != 1 || !Merge::editText(&m_plan, members.first()->id, text))
515 2 return false;
516
1/1
✓ Branch 12 → 13 taken 1 time.
1 rebuild();
517 1 return true;
518 3 }
519
520 8 bool QuickConflictResolver::separate(const QString &rowId) {
521
1/2
✓ Branch 2 → 3 taken 8 times.
✗ Branch 2 → 6 not taken.
8 const ConflictRows::Row *row = m_active ? ConflictRows::find(m_rows.rows(), rowId) : nullptr;
522
6/8
✓ Branch 7 → 8 taken 6 times.
✓ Branch 7 → 12 taken 2 times.
✓ Branch 9 → 10 taken 6 times.
✗ Branch 9 → 12 not taken.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 6 times.
✓ Branch 14 → 15 taken 2 times.
✓ Branch 14 → 16 taken 6 times.
8 if (!row || row->group.isEmpty() || m_plan.groupSeparated(row->group))
523 2 return false;
524 6 m_plan.separatedGroups.append(row->group);
525 6 rebuild();
526 6 return true;
527 }
528
529 6 bool QuickConflictResolver::fixProblem() {
530
5/6
✓ Branch 3 → 4 taken 5 times.
✓ Branch 3 → 7 taken 1 time.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 5 times.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 5 times.
6 if (m_problems.isEmpty() || !MergeValidation::fixWithSameSide(&m_plan, m_problems.first()))
531 1 return false;
532 5 rebuild();
533 5 return true;
534 }
535
536 12 QString QuickConflictResolver::resultTextOf(const QString &rowId) const {
537
1/1
✓ Branch 2 → 3 taken 12 times.
12 const QList<const Merge::Item *> members = membersOf(rowId);
538
3/3
✓ Branch 4 → 5 taken 10 times.
✓ Branch 4 → 7 taken 2 times.
✓ Branch 6 → 8 taken 10 times.
24 return members.size() == 1 ? Merge::resultText(*members.first()) : QString();
539 12 }
540
541 5 QByteArray QuickConflictResolver::photoBytes(const QString &rowId, const QString &side) const {
542
5/5
✓ Branch 2 → 3 taken 5 times.
✓ Branch 3 → 4 taken 5 times.
✓ Branch 4 → 5 taken 5 times.
✓ Branch 56 → 6 taken 4 times.
✓ Branch 56 → 57 taken 3 times.
7 for (const Merge::Item *item : membersOf(rowId)) {
543
2/2
✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 11 taken 2 times.
4 if (item->name != QLatin1String("PHOTO"))
544 2 continue;
545 2 const Merge::Value &value = side == QLatin1String("server") ? item->server
546 1 : side == QLatin1String("base") ? item->base
547
3/4
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 15 taken 1 time.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 1 time.
3 : item->local;
548
1/1
✓ Branch 21 → 22 taken 2 times.
2 ContactCard card;
549 // The side's PHOTO lines in a minimal card: the codec decodes them with its limits.
550
8/22
✓ Branch 22 → 23 taken 2 times.
✗ Branch 22 → 33 not taken.
✓ Branch 29 → 30 taken 2 times.
✓ Branch 31 → 32 taken 2 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 2 times.
✓ Branch 35 → 36 taken 2 times.
✗ Branch 35 → 37 not taken.
✓ Branch 37 → 38 taken 2 times.
✗ Branch 37 → 39 not taken.
✓ Branch 39 → 40 taken 2 times.
✗ Branch 39 → 41 not taken.
✓ Branch 41 → 42 taken 2 times.
✗ Branch 41 → 43 not taken.
✗ Branch 64 → 65 not taken.
✗ Branch 64 → 66 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 68 → 70 not taken.
✗ Branch 72 → 73 not taken.
✗ Branch 72 → 74 not taken.
✗ Branch 76 → 77 not taken.
✗ Branch 76 → 78 not taken.
6 if (!value.present || !VCardCodec::parse(QStringLiteral("BEGIN:VCARD\r\nVERSION:4.0\r\n") + value.text +
551
4/11
✓ Branch 30 → 31 taken 2 times.
✓ Branch 43 → 44 taken 2 times.
✗ Branch 43 → 45 not taken.
✓ Branch 45 → 46 taken 2 times.
✗ Branch 45 → 47 not taken.
✗ Branch 47 → 48 not taken.
✓ Branch 47 → 49 taken 2 times.
✗ Branch 80 → 81 not taken.
✗ Branch 80 → 82 not taken.
✗ Branch 84 → 85 not taken.
✗ Branch 84 → 86 not taken.
6 QStringLiteral("\r\nEND:VCARD\r\n"),
552 &card))
553 ✗ return {};
554
1/2
✓ Branch 49 → 50 taken 2 times.
✗ Branch 49 → 51 not taken.
2 return card.photo.kind == ContactPhoto::Kind::Inline ? card.photo.data : QByteArray();
555 7 }
556 3 return {};
557 }
558
559 // ─── writing ─────────────────────────────────────────────────
560
561 27 void QuickConflictResolver::apply() {
562
3/3
✓ Branch 2 → 3 taken 27 times.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 26 times.
27 if (!canApply())
563 25 return;
564 26 QElapsedTimer timer;
565 26 timer.start();
566 26 m_applying = true;
567
1/1
✓ Branch 6 → 7 taken 26 times.
26 emit changed();
568
1/1
✓ Branch 7 → 8 taken 26 times.
26 const Merge::Choice uniform = Merge::uniformSide(m_plan);
569 3 const QString wholeSide = uniform == Merge::Choice::Local ? QStringLiteral("mine")
570
6/6
✓ Branch 12 → 13 taken 6 times.
✓ Branch 12 → 16 taken 17 times.
✓ Branch 17 → 18 taken 6 times.
✓ Branch 17 → 19 taken 20 times.
✓ Branch 19 → 20 taken 3 times.
✓ Branch 19 → 21 taken 23 times.
49 : uniform == Merge::Choice::Server ? QStringLiteral("server")
571
2/2
✓ Branch 8 → 9 taken 3 times.
✓ Branch 8 → 12 taken 23 times.
52 : QString();
572 26 int edited = 0;
573
4/4
✓ Branch 21 → 22 taken 26 times.
✓ Branch 22 → 23 taken 26 times.
✓ Branch 30 → 24 taken 126 times.
✓ Branch 30 → 31 taken 26 times.
152 for (const Merge::Item &item : m_plan.items)
574
2/2
✓ Branch 25 → 26 taken 45 times.
✓ Branch 25 → 27 taken 81 times.
126 edited += item.touched ? 1 : 0;
575
10/10
✓ Branch 31 → 32 taken 26 times.
✓ Branch 35 → 36 taken 26 times.
✓ Branch 36 → 37 taken 26 times.
✓ Branch 37 → 38 taken 26 times.
✓ Branch 39 → 40 taken 17 times.
✓ Branch 39 → 41 taken 9 times.
✓ Branch 40 → 42 taken 17 times.
✓ Branch 42 → 43 taken 26 times.
✓ Branch 49 → 33 taken 26 times.
✓ Branch 49 → 50 taken 26 times.
52 qCInfo(lcConflict) << "resolver apply" << m_domain << (wholeSide.isEmpty() ? "combined" : wholeSide)
576
2/2
✓ Branch 43 → 44 taken 26 times.
✓ Branch 44 → 45 taken 26 times.
26 << "choices made" << edited;
577
2/2
✓ Branch 54 → 55 taken 26 times.
✓ Branch 55 → 56 taken 26 times.
52 const QString uid = m_identity.value(QStringLiteral("uid")).toString();
578
6/6
✓ Branch 62 → 63 taken 13 times.
✓ Branch 62 → 66 taken 13 times.
✓ Branch 65 → 66 taken 5 times.
✓ Branch 65 → 67 taken 8 times.
✓ Branch 68 → 69 taken 18 times.
✓ Branch 68 → 158 taken 8 times.
26 if (m_domain == QLatin1String("event") || m_domain == QLatin1String("task")) {
579
2/3
✓ Branch 70 → 71 taken 18 times.
✗ Branch 70 → 73 not taken.
✓ Branch 71 → 72 taken 18 times.
18 CalendarOutbox *outbox = m_deps.calendarOutbox ? m_deps.calendarOutbox() : nullptr;
580
2/2
✓ Branch 78 → 79 taken 18 times.
✓ Branch 79 → 80 taken 18 times.
36 const QString accountId = m_identity.value(QStringLiteral("accountId")).toString();
581
2/2
✓ Branch 88 → 89 taken 18 times.
✓ Branch 89 → 90 taken 18 times.
36 const QString path = m_identity.value(QStringLiteral("calendarPath")).toString();
582 18 bool ok = false;
583
6/8
✓ Branch 94 → 95 taken 18 times.
✗ Branch 94 → 101 not taken.
✓ Branch 96 → 97 taken 5 times.
✓ Branch 96 → 101 taken 13 times.
✓ Branch 99 → 100 taken 5 times.
✗ Branch 99 → 101 not taken.
✓ Branch 102 → 103 taken 5 times.
✓ Branch 102 → 105 taken 13 times.
18 if (outbox && !wholeSide.isEmpty() && m_kind != QLatin1String("raw")) {
584
1/1
✓ Branch 103 → 104 taken 5 times.
5 ok = outbox->resolveConflict(uid, accountId, path, wholeSide);
585
1/2
✓ Branch 105 → 106 taken 13 times.
✗ Branch 105 → 124 not taken.
13 } else if (outbox) {
586
1/1
✓ Branch 106 → 107 taken 13 times.
13 const ICalMergePlan::Applied result = ICalMergePlan::apply(m_plan);
587
3/5
✓ Branch 107 → 108 taken 13 times.
✗ Branch 107 → 117 not taken.
✓ Branch 114 → 115 taken 13 times.
✓ Branch 115 → 116 taken 13 times.
✗ Branch 115 → 117 not taken.
26 ok = result.ok && outbox->resolveWith(uid, accountId, path, result.text, m_serverEtag,
588
3/10
✓ Branch 109 → 110 taken 13 times.
✗ Branch 109 → 113 not taken.
✓ Branch 118 → 119 taken 13 times.
✗ Branch 118 → 120 not taken.
✓ Branch 120 → 121 taken 13 times.
✗ Branch 120 → 122 not taken.
✗ Branch 394 → 395 not taken.
✗ Branch 394 → 396 not taken.
✗ Branch 398 → 399 not taken.
✗ Branch 398 → 400 not taken.
39 wholeSide.isEmpty() ? QStringLiteral("combined") : wholeSide);
589 13 }
590
2/7
✗ Branch 124 → 125 not taken.
✓ Branch 124 → 128 taken 18 times.
✗ Branch 125 → 126 not taken.
✗ Branch 126 → 127 not taken.
✗ Branch 126 → 128 not taken.
✗ Branch 129 → 130 not taken.
✓ Branch 129 → 132 taken 18 times.
18 if (!ok && reloadIfChanged()) {
591 ✗ finishApply(false, m_notice);
592 ✗ return;
593 }
594
7/7
✓ Branch 132 → 133 taken 18 times.
✓ Branch 136 → 137 taken 18 times.
✓ Branch 137 → 138 taken 18 times.
✓ Branch 139 → 140 taken 18 times.
✓ Branch 140 → 141 taken 18 times.
✓ Branch 144 → 134 taken 18 times.
✓ Branch 144 → 145 taken 18 times.
36 qCInfo(lcConflict) << "resolver apply took" << timer.elapsed() << "ms";
595
5/10
✓ Branch 145 → 146 taken 18 times.
✗ Branch 145 → 149 not taken.
✓ Branch 146 → 147 taken 18 times.
✓ Branch 148 → 150 taken 18 times.
✗ Branch 149 → 150 not taken.
✓ Branch 150 → 151 taken 18 times.
✓ Branch 152 → 153 taken 18 times.
✗ Branch 152 → 154 not taken.
✗ Branch 413 → 414 not taken.
✗ Branch 413 → 415 not taken.
18 finishApply(ok, ok ? tr("Conflict resolved: %1").arg(m_title) : tr("The choices could not be saved."));
596 18 return;
597 18 }
598
2/2
✓ Branch 160 → 161 taken 2 times.
✓ Branch 160 → 226 taken 6 times.
8 if (m_domain == QLatin1String("contact")) {
599
2/3
✓ Branch 162 → 163 taken 2 times.
✗ Branch 162 → 165 not taken.
✓ Branch 163 → 164 taken 2 times.
2 ContactOutbox *outbox = m_deps.contactOutbox ? m_deps.contactOutbox() : nullptr;
600
2/2
✓ Branch 170 → 171 taken 2 times.
✓ Branch 171 → 172 taken 2 times.
4 const qint64 bookId = m_identity.value(QStringLiteral("bookId")).toLongLong();
601 2 bool ok = false;
602
6/8
✓ Branch 176 → 177 taken 2 times.
✗ Branch 176 → 183 not taken.
✓ Branch 178 → 179 taken 1 time.
✓ Branch 178 → 183 taken 1 time.
✓ Branch 181 → 182 taken 1 time.
✗ Branch 181 → 183 not taken.
✓ Branch 184 → 185 taken 1 time.
✓ Branch 184 → 187 taken 1 time.
2 if (outbox && !wholeSide.isEmpty() && m_kind != QLatin1String("raw")) {
603
1/1
✓ Branch 185 → 186 taken 1 time.
1 ok = outbox->resolveConflict(bookId, uid, wholeSide);
604
1/2
✓ Branch 187 → 188 taken 1 time.
✗ Branch 187 → 208 not taken.
1 } else if (outbox) {
605
1/1
✓ Branch 189 → 190 taken 1 time.
1 const VCardMergePlan::Applied result = VCardMergePlan::apply(m_plan);
606
3/5
✓ Branch 191 → 192 taken 1 time.
✗ Branch 191 → 201 not taken.
✓ Branch 198 → 199 taken 1 time.
✓ Branch 199 → 200 taken 1 time.
✗ Branch 199 → 201 not taken.
2 ok = result.ok && outbox->resolveWith(bookId, uid, result.text, m_serverEtag,
607
3/10
✓ Branch 193 → 194 taken 1 time.
✗ Branch 193 → 197 not taken.
✓ Branch 202 → 203 taken 1 time.
✗ Branch 202 → 204 not taken.
✓ Branch 204 → 205 taken 1 time.
✗ Branch 204 → 206 not taken.
✗ Branch 438 → 439 not taken.
✗ Branch 438 → 440 not taken.
✗ Branch 442 → 443 not taken.
✗ Branch 442 → 444 not taken.
3 wholeSide.isEmpty() ? QStringLiteral("combined") : wholeSide);
608 1 }
609
2/7
✗ Branch 208 → 209 not taken.
✓ Branch 208 → 212 taken 2 times.
✗ Branch 209 → 210 not taken.
✗ Branch 210 → 211 not taken.
✗ Branch 210 → 212 not taken.
✗ Branch 213 → 214 not taken.
✓ Branch 213 → 216 taken 2 times.
2 if (!ok && reloadIfChanged()) {
610 ✗ finishApply(false, m_notice);
611 ✗ return;
612 }
613
5/10
✓ Branch 216 → 217 taken 2 times.
✗ Branch 216 → 220 not taken.
✓ Branch 217 → 218 taken 2 times.
✓ Branch 219 → 221 taken 2 times.
✗ Branch 220 → 221 not taken.
✓ Branch 221 → 222 taken 2 times.
✓ Branch 223 → 224 taken 2 times.
✗ Branch 223 → 225 not taken.
✗ Branch 452 → 453 not taken.
✗ Branch 452 → 454 not taken.
2 finishApply(ok, ok ? tr("Conflict resolved: %1").arg(m_title) : tr("The choices could not be saved."));
614 2 return;
615 }
616
2/2
✓ Branch 228 → 229 taken 4 times.
✓ Branch 228 → 289 taken 2 times.
6 if (m_domain == QLatin1String("rules")) {
617
2/3
✓ Branch 230 → 231 taken 4 times.
✗ Branch 230 → 233 not taken.
✓ Branch 231 → 232 taken 4 times.
4 QuickRulesBackend *rules = m_deps.rules ? m_deps.rules() : nullptr;
618
1/2
✗ Branch 234 → 235 not taken.
✓ Branch 234 → 239 taken 4 times.
4 if (!rules) {
619 ✗ finishApply(false, tr("The choices could not be saved."));
620 ✗ return;
621 }
622
5/6
✓ Branch 240 → 241 taken 2 times.
✓ Branch 240 → 243 taken 2 times.
✓ Branch 241 → 242 taken 2 times.
✗ Branch 241 → 243 not taken.
✓ Branch 244 → 245 taken 2 times.
✓ Branch 244 → 267 taken 2 times.
4 if (!wholeSide.isEmpty() && m_plan.comparable) {
623
7/11
✓ Branch 247 → 248 taken 1 time.
✓ Branch 247 → 251 taken 1 time.
✓ Branch 254 → 255 taken 2 times.
✓ Branch 256 → 257 taken 1 time.
✓ Branch 256 → 258 taken 1 time.
✓ Branch 258 → 259 taken 1 time.
✓ Branch 258 → 260 taken 1 time.
✗ Branch 462 → 463 not taken.
✗ Branch 462 → 464 not taken.
✗ Branch 466 → 467 not taken.
✗ Branch 466 → 468 not taken.
4 rules->resolveConflict(wholeSide == QLatin1String("mine") ? QStringLiteral("mine") : QStringLiteral("theirs"));
624
3/3
✓ Branch 260 → 261 taken 2 times.
✓ Branch 262 → 263 taken 2 times.
✓ Branch 263 → 264 taken 2 times.
4 finishApply(true, tr("Conflict resolved: %1").arg(m_title));
625 2 return;
626 }
627
1/1
✓ Branch 267 → 268 taken 2 times.
2 const SieveMergePlan::Applied result = SieveMergePlan::apply(m_plan);
628
1/2
✗ Branch 268 → 269 not taken.
✓ Branch 268 → 278 taken 2 times.
2 if (!result.ok) {
629 ✗ finishApply(false, ConflictValueText::problemText(QStringLiteral("script-does-not-parse")));
630 ✗ return;
631 }
632
1/1
✓ Branch 278 → 279 taken 2 times.
2 QPointer<QuickConflictResolver> guard(this);
633
3/3
✓ Branch 280 → 281 taken 2 times.
✓ Branch 281 → 282 taken 2 times.
✓ Branch 282 → 283 taken 2 times.
2 rules->resolveWith(result.script, m_plan.serverText.toUtf8(),
634 4 [guard](bool ok, const QString &kind, const QString &message, const QVariantList &errors) {
635
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 if (!guard)
636 2 return;
637
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 15 taken 1 time.
2 if (ok) {
638
3/3
✓ Branch 7 → 8 taken 1 time.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 11 → 12 taken 1 time.
2 guard->finishApply(true, tr("Conflict resolved: %1").arg(guard->m_title));
639 1 return;
640 }
641
4/7
✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 22 not taken.
✓ Branch 19 → 20 taken 1 time.
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 22 not taken.
✓ Branch 23 → 24 taken 1 time.
✗ Branch 23 → 28 not taken.
1 if (kind == QLatin1String("conflict") && guard->reloadIfChanged()) {
642
1/1
✓ Branch 26 → 27 taken 1 time.
1 guard->finishApply(false, guard->m_notice);
643 1 return;
644 }
645 ✗ QString text = message;
646 ✗ if (!errors.isEmpty())
647 ✗ text += QStringLiteral(" ") + errors.first().toMap().value(QStringLiteral("message")).toString();
648 ✗ guard->m_validationMessage = text;
649 ✗ guard->finishApply(false, text);
650 ✗ });
651 2 return;
652 2 }
653
1/2
✓ Branch 291 → 292 taken 2 times.
✗ Branch 291 → 338 not taken.
2 if (m_domain == QLatin1String("settings")) {
654
2/3
✓ Branch 293 → 294 taken 2 times.
✗ Branch 293 → 296 not taken.
✓ Branch 294 → 295 taken 2 times.
2 SettingsSyncSession *session = m_deps.settings ? m_deps.settings() : nullptr;
655 2 bool ok = false;
656
3/6
✓ Branch 297 → 298 taken 2 times.
✗ Branch 297 → 302 not taken.
✗ Branch 300 → 301 not taken.
✓ Branch 300 → 302 taken 2 times.
✗ Branch 303 → 304 not taken.
✓ Branch 303 → 306 taken 2 times.
2 if (session && wholeSide == QLatin1String("mine")) {
657 ✗ session->useLocal();
658 ✗ ok = true;
659
5/6
✓ Branch 306 → 307 taken 2 times.
✗ Branch 306 → 311 not taken.
✓ Branch 309 → 310 taken 1 time.
✓ Branch 309 → 311 taken 1 time.
✓ Branch 312 → 313 taken 1 time.
✓ Branch 312 → 315 taken 1 time.
2 } else if (session && wholeSide == QLatin1String("server")) {
660
1/1
✓ Branch 313 → 314 taken 1 time.
1 session->useRemote();
661 1 ok = true;
662
1/2
✓ Branch 315 → 316 taken 1 time.
✗ Branch 315 → 324 not taken.
1 } else if (session) {
663
4/4
✓ Branch 316 → 317 taken 1 time.
✓ Branch 317 → 318 taken 1 time.
✓ Branch 318 → 319 taken 1 time.
✓ Branch 319 → 320 taken 1 time.
1 ok = session->resolveWith(SettingsMergePlan::apply(m_plan), QByteArray::fromHex(m_serverEtag.toLatin1()));
664 }
665
2/7
✗ Branch 324 → 325 not taken.
✓ Branch 324 → 328 taken 2 times.
✗ Branch 325 → 326 not taken.
✗ Branch 326 → 327 not taken.
✗ Branch 326 → 328 not taken.
✗ Branch 329 → 330 not taken.
✓ Branch 329 → 332 taken 2 times.
2 if (!ok && reloadIfChanged()) {
666 ✗ finishApply(false, m_notice);
667 ✗ return;
668 }
669
3/5
✓ Branch 332 → 333 taken 2 times.
✗ Branch 332 → 334 not taken.
✓ Branch 333 → 335 taken 2 times.
✗ Branch 334 → 335 not taken.
✓ Branch 335 → 336 taken 2 times.
2 finishApply(ok, ok ? tr("Settings conflict resolved") : tr("The choices could not be saved."));
670 }
671 50 }
672
673 15 void QuickConflictResolver::applyWholeSide(const QString &choice) {
674
3/4
✓ Branch 2 → 3 taken 13 times.
✓ Branch 2 → 4 taken 2 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 13 times.
15 if (!m_active || m_applying)
675 2 return;
676
4/8
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 14 taken 12 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 14 taken 1 time.
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 14 not taken.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 13 times.
13 if (choice == QLatin1String("both") && !canKeepBoth() && m_kind != QLatin1String("changed"))
677 ✗ return;
678
2/2
✓ Branch 21 → 22 taken 13 times.
✓ Branch 22 → 23 taken 13 times.
26 const QString uid = m_identity.value(QStringLiteral("uid")).toString();
679 13 bool ok = false;
680
7/7
✓ Branch 27 → 28 taken 13 times.
✓ Branch 31 → 32 taken 13 times.
✓ Branch 32 → 33 taken 13 times.
✓ Branch 33 → 34 taken 13 times.
✓ Branch 34 → 35 taken 13 times.
✓ Branch 38 → 29 taken 13 times.
✓ Branch 38 → 39 taken 13 times.
26 qCInfo(lcConflict) << "resolver whole side" << m_domain << choice;
681
6/6
✓ Branch 41 → 42 taken 9 times.
✓ Branch 41 → 45 taken 4 times.
✓ Branch 44 → 45 taken 1 time.
✓ Branch 44 → 46 taken 8 times.
✓ Branch 47 → 48 taken 5 times.
✓ Branch 47 → 78 taken 8 times.
13 if (m_domain == QLatin1String("event") || m_domain == QLatin1String("task")) {
682
3/5
✓ Branch 49 → 50 taken 5 times.
✗ Branch 49 → 52 not taken.
✓ Branch 50 → 51 taken 5 times.
✓ Branch 53 → 54 taken 5 times.
✗ Branch 53 → 143 not taken.
5 if (CalendarOutbox *outbox = m_deps.calendarOutbox ? m_deps.calendarOutbox() : nullptr)
683
3/3
✓ Branch 64 → 65 taken 5 times.
✓ Branch 65 → 66 taken 5 times.
✓ Branch 66 → 67 taken 5 times.
10 ok = outbox->resolveConflict(uid, m_identity.value(QStringLiteral("accountId")).toString(),
684
2/2
✓ Branch 58 → 59 taken 5 times.
✓ Branch 59 → 60 taken 5 times.
15 m_identity.value(QStringLiteral("calendarPath")).toString(), choice);
685
2/2
✓ Branch 80 → 81 taken 4 times.
✓ Branch 80 → 99 taken 4 times.
8 } else if (m_domain == QLatin1String("contact")) {
686
3/5
✓ Branch 82 → 83 taken 4 times.
✗ Branch 82 → 85 not taken.
✓ Branch 83 → 84 taken 4 times.
✓ Branch 86 → 87 taken 4 times.
✗ Branch 86 → 143 not taken.
4 if (ContactOutbox *outbox = m_deps.contactOutbox ? m_deps.contactOutbox() : nullptr)
687
3/3
✓ Branch 91 → 92 taken 4 times.
✓ Branch 92 → 93 taken 4 times.
✓ Branch 93 → 94 taken 4 times.
8 ok = outbox->resolveConflict(m_identity.value(QStringLiteral("bookId")).toLongLong(), uid, choice);
688
2/2
✓ Branch 101 → 102 taken 2 times.
✓ Branch 101 → 126 taken 2 times.
4 } else if (m_domain == QLatin1String("rules")) {
689
3/5
✓ Branch 103 → 104 taken 2 times.
✗ Branch 103 → 106 not taken.
✓ Branch 104 → 105 taken 2 times.
✓ Branch 107 → 108 taken 2 times.
✗ Branch 107 → 143 not taken.
2 if (QuickRulesBackend *rules = m_deps.rules ? m_deps.rules() : nullptr) {
690
4/11
✗ Branch 110 → 111 not taken.
✓ Branch 110 → 114 taken 2 times.
✓ Branch 117 → 118 taken 2 times.
✓ Branch 119 → 120 taken 2 times.
✗ Branch 119 → 121 not taken.
✗ Branch 121 → 122 not taken.
✓ Branch 121 → 123 taken 2 times.
✗ Branch 217 → 218 not taken.
✗ Branch 217 → 219 not taken.
✗ Branch 221 → 222 not taken.
✗ Branch 221 → 223 not taken.
4 rules->resolveConflict(choice == QLatin1String("server") ? QStringLiteral("theirs") : QStringLiteral("mine"));
691 2 ok = choice != QLatin1String("both");
692 }
693
1/2
✓ Branch 128 → 129 taken 2 times.
✗ Branch 128 → 143 not taken.
2 } else if (m_domain == QLatin1String("settings")) {
694
3/5
✓ Branch 130 → 131 taken 2 times.
✗ Branch 130 → 133 not taken.
✓ Branch 131 → 132 taken 2 times.
✓ Branch 134 → 135 taken 2 times.
✗ Branch 134 → 143 not taken.
2 if (SettingsSyncSession *session = m_deps.settings ? m_deps.settings() : nullptr) {
695
2/2
✓ Branch 137 → 138 taken 1 time.
✓ Branch 137 → 139 taken 1 time.
2 if (choice == QLatin1String("server"))
696
1/1
✓ Branch 138 → 140 taken 1 time.
1 session->useRemote();
697 else
698
1/1
✓ Branch 139 → 140 taken 1 time.
1 session->useLocal();
699 2 ok = choice != QLatin1String("both");
700 }
701 }
702
5/10
✓ Branch 143 → 144 taken 13 times.
✗ Branch 143 → 147 not taken.
✓ Branch 144 → 145 taken 13 times.
✓ Branch 146 → 148 taken 13 times.
✗ Branch 147 → 148 not taken.
✓ Branch 148 → 149 taken 13 times.
✓ Branch 150 → 151 taken 13 times.
✗ Branch 150 → 152 not taken.
✗ Branch 230 → 231 not taken.
✗ Branch 230 → 232 not taken.
13 finishApply(ok, ok ? tr("Conflict resolved: %1").arg(m_title) : tr("The choices could not be saved."));
703 13 }
704
705 39 void QuickConflictResolver::finishApply(bool ok, const QString &message) {
706 39 m_applying = false;
707
2/2
✓ Branch 2 → 3 taken 38 times.
✓ Branch 2 → 6 taken 1 time.
39 if (ok) {
708 38 emit applied(message);
709 38 close();
710 38 return;
711 }
712 1 emit changed();
713 1 emit applyFailed(message);
714 }
715
716 21 bool QuickConflictResolver::reloadIfChanged() {
717
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 21 times.
21 if (!m_active)
718 ✗ return false;
719 21 const QString previousEtag = m_serverEtag;
720 21 const Merge::Plan previous = m_plan;
721 21 const QString previousKind = m_kind;
722 21 const QVariantMap identity = m_identity;
723 21 const QString domain = m_domain;
724 21 bool ok = false;
725
6/6
✓ Branch 11 → 12 taken 11 times.
✓ Branch 11 → 15 taken 10 times.
✓ Branch 14 → 15 taken 4 times.
✓ Branch 14 → 16 taken 7 times.
✓ Branch 17 → 18 taken 14 times.
✓ Branch 17 → 22 taken 7 times.
21 if (domain == QLatin1String("event") || domain == QLatin1String("task"))
726
1/1
✓ Branch 20 → 21 taken 14 times.
14 ok = openEvent(identity, domain == QLatin1String("task"));
727
2/2
✓ Branch 24 → 25 taken 4 times.
✓ Branch 24 → 27 taken 3 times.
7 else if (domain == QLatin1String("contact"))
728
1/1
✓ Branch 25 → 26 taken 4 times.
4 ok = openContact(identity);
729
1/2
✓ Branch 29 → 30 taken 3 times.
✗ Branch 29 → 32 not taken.
3 else if (domain == QLatin1String("rules"))
730
1/1
✓ Branch 30 → 31 taken 3 times.
3 ok = openRules();
731 ✗ else if (domain == QLatin1String("settings"))
732 ✗ ok = openSettings();
733
2/2
✓ Branch 37 → 38 taken 12 times.
✓ Branch 37 → 62 taken 9 times.
21 if (!ok) {
734 // Resolved elsewhere (the banner, another window) or combined with the
735 // server's newer version by the check on opening: nothing left to ask —
736 // said, so the sheet does not just flash and go (F108-12).
737
5/5
✓ Branch 38 → 39 taken 12 times.
✓ Branch 42 → 43 taken 12 times.
✓ Branch 43 → 44 taken 12 times.
✓ Branch 47 → 40 taken 12 times.
✓ Branch 47 → 48 taken 12 times.
24 qCInfo(lcConflict) << "resolver: the conflict is gone";
738 12 const QString title = m_title;
739
1/1
✓ Branch 49 → 50 taken 12 times.
12 close();
740
3/5
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 53 taken 12 times.
✗ Branch 52 → 56 not taken.
✓ Branch 55 → 56 taken 12 times.
✓ Branch 56 → 57 taken 12 times.
24 emit vanished(title.isEmpty() ? tr("Nothing left to decide — the conflict was settled meanwhile.")
741
2/5
✓ Branch 53 → 54 taken 12 times.
✓ Branch 58 → 59 taken 12 times.
✗ Branch 58 → 60 not taken.
✗ Branch 96 → 97 not taken.
✗ Branch 96 → 98 not taken.
24 : tr("Nothing left to decide for “%1” — the conflict was settled meanwhile.").arg(title));
742 12 return true;
743 12 }
744
5/6
✓ Branch 63 → 64 taken 1 time.
✓ Branch 63 → 67 taken 8 times.
✓ Branch 65 → 66 taken 1 time.
✗ Branch 65 → 67 not taken.
✓ Branch 68 → 69 taken 1 time.
✓ Branch 68 → 73 taken 8 times.
9 if (m_serverEtag == previousEtag && previousKind == m_kind) {
745 1 Merge::Plan current = m_plan;
746 1 m_plan = previous; // unchanged server version: keep everything
747 1 return false;
748 1 }
749 8 Merge::Plan next = m_plan;
750 8 m_plan = previous;
751
1/1
✓ Branch 76 → 77 taken 8 times.
8 setPlan(next, true);
752
1/1
✓ Branch 78 → 79 taken 8 times.
8 rebuild();
753 8 return true;
754 21 }
755
756 38 void QuickConflictResolver::cancel() {
757
2/2
✓ Branch 2 → 3 taken 37 times.
✓ Branch 2 → 21 taken 1 time.
38 if (m_active)
758
11/11
✓ Branch 3 → 4 taken 37 times.
✓ Branch 7 → 8 taken 37 times.
✓ Branch 8 → 9 taken 37 times.
✓ Branch 9 → 10 taken 37 times.
✓ Branch 10 → 11 taken 37 times.
✓ Branch 11 → 12 taken 37 times.
✓ Branch 12 → 13 taken 6 times.
✓ Branch 12 → 14 taken 31 times.
✓ Branch 15 → 16 taken 37 times.
✓ Branch 19 → 5 taken 37 times.
✓ Branch 19 → 20 taken 37 times.
74 qCInfo(lcConflict) << "resolver cancelled" << m_domain << "after" << (touched() ? "choices" : "no choices");
759 38 close();
760 38 }
761
762 179 void QuickConflictResolver::close() {
763 179 const bool wasActive = m_active;
764 179 m_active = false;
765 179 m_applying = false;
766 179 m_plan = {};
767 179 m_problems.clear();
768 179 m_validationMessage.clear();
769 179 m_deletionChanges.clear();
770 179 m_serverLabel.clear();
771 // The rows stay until the next open() replaces them: the sheet's list
772 // lets go of them when `active` turns false, and clearing them while its
773 // rows are being torn down made those rows evaluate empty entries.
774 179 disconnect(this, nullptr, this, nullptr);
775
3/3
✓ Branch 10 → 11 taken 130 times.
✓ Branch 10 → 13 taken 49 times.
✓ Branch 11 → 12 taken 130 times.
1074 for (QObject *source : {static_cast<QObject *>(m_deps.calendarOutbox ? m_deps.calendarOutbox() : nullptr),
776
3/3
✓ Branch 15 → 16 taken 109 times.
✓ Branch 15 → 18 taken 70 times.
✓ Branch 16 → 17 taken 109 times.
179 static_cast<QObject *>(m_deps.contactOutbox ? m_deps.contactOutbox() : nullptr),
777
3/3
✓ Branch 20 → 21 taken 105 times.
✓ Branch 20 → 23 taken 74 times.
✓ Branch 21 → 22 taken 105 times.
179 static_cast<QObject *>(m_deps.rules ? m_deps.rules() : nullptr),
778
5/5
✓ Branch 25 → 26 taken 102 times.
✓ Branch 25 → 28 taken 77 times.
✓ Branch 26 → 27 taken 102 times.
✓ Branch 35 → 32 taken 716 times.
✓ Branch 35 → 36 taken 179 times.
1611 static_cast<QObject *>(m_deps.settings ? m_deps.settings() : nullptr)})
779
2/2
✓ Branch 32 → 33 taken 446 times.
✓ Branch 32 → 34 taken 270 times.
716 if (source)
780
1/1
✓ Branch 33 → 34 taken 446 times.
446 disconnect(source, nullptr, this, nullptr);
781 179 emit changed();
782
2/2
✓ Branch 37 → 38 taken 76 times.
✓ Branch 37 → 39 taken 103 times.
179 if (wasActive)
783 76 emit closed();
784 179 }
785
786 85 void QuickConflictResolver::connectSources() {
787 44 const auto onChange = [this] {
788
3/4
✓ Branch 2 → 3 taken 44 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 20 times.
✓ Branch 3 → 5 taken 24 times.
44 if (m_active && !m_applying)
789 20 reloadIfChanged();
790 129 };
791
6/6
✓ Branch 4 → 5 taken 41 times.
✓ Branch 4 → 8 taken 44 times.
✓ Branch 7 → 8 taken 15 times.
✓ Branch 7 → 9 taken 26 times.
✓ Branch 10 → 11 taken 59 times.
✓ Branch 10 → 20 taken 26 times.
85 if (m_domain == QLatin1String("event") || m_domain == QLatin1String("task")) {
792
3/5
✓ Branch 12 → 13 taken 59 times.
✗ Branch 12 → 15 not taken.
✓ Branch 13 → 14 taken 59 times.
✓ Branch 16 → 17 taken 59 times.
✗ Branch 16 → 56 not taken.
59 if (CalendarOutbox *outbox = m_deps.calendarOutbox ? m_deps.calendarOutbox() : nullptr)
793
1/1
✓ Branch 17 → 18 taken 59 times.
59 connect(outbox, &CalendarOutbox::storeChanged, this, onChange);
794
2/2
✓ Branch 22 → 23 taken 11 times.
✓ Branch 22 → 32 taken 15 times.
26 } else if (m_domain == QLatin1String("contact")) {
795
3/5
✓ Branch 24 → 25 taken 11 times.
✗ Branch 24 → 27 not taken.
✓ Branch 25 → 26 taken 11 times.
✓ Branch 28 → 29 taken 11 times.
✗ Branch 28 → 56 not taken.
11 if (ContactOutbox *outbox = m_deps.contactOutbox ? m_deps.contactOutbox() : nullptr)
796
1/1
✓ Branch 29 → 30 taken 11 times.
11 connect(outbox, &ContactOutbox::cardsChanged, this, onChange);
797
2/2
✓ Branch 34 → 35 taken 8 times.
✓ Branch 34 → 44 taken 7 times.
15 } else if (m_domain == QLatin1String("rules")) {
798
3/5
✓ Branch 36 → 37 taken 8 times.
✗ Branch 36 → 39 not taken.
✓ Branch 37 → 38 taken 8 times.
✓ Branch 40 → 41 taken 8 times.
✗ Branch 40 → 56 not taken.
8 if (QuickRulesBackend *rules = m_deps.rules ? m_deps.rules() : nullptr)
799
1/1
✓ Branch 41 → 42 taken 8 times.
8 connect(rules, &QuickRulesBackend::conflictChanged, this, onChange);
800
1/2
✓ Branch 46 → 47 taken 7 times.
✗ Branch 46 → 56 not taken.
7 } else if (m_domain == QLatin1String("settings")) {
801
3/5
✓ Branch 48 → 49 taken 7 times.
✗ Branch 48 → 51 not taken.
✓ Branch 49 → 50 taken 7 times.
✓ Branch 52 → 53 taken 7 times.
✗ Branch 52 → 56 not taken.
7 if (SettingsSyncSession *session = m_deps.settings ? m_deps.settings() : nullptr)
802
1/1
✓ Branch 53 → 54 taken 7 times.
7 connect(session, &SettingsSyncSession::changed, this, onChange);
803 }
804 85 }
805
806 88 void QuickConflictResolver::checkServer() {
807
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 87 times.
88 if (!m_active)
808 1 return;
809
2/2
✓ Branch 8 → 9 taken 87 times.
✓ Branch 9 → 10 taken 87 times.
174 const QString uid = m_identity.value(QStringLiteral("uid")).toString();
810
6/6
✓ Branch 16 → 17 taken 42 times.
✓ Branch 16 → 20 taken 45 times.
✓ Branch 19 → 20 taken 15 times.
✓ Branch 19 → 21 taken 27 times.
✓ Branch 22 → 23 taken 60 times.
✓ Branch 22 → 53 taken 27 times.
87 if (m_domain == QLatin1String("event") || m_domain == QLatin1String("task")) {
811
3/5
✓ Branch 24 → 25 taken 60 times.
✗ Branch 24 → 27 not taken.
✓ Branch 25 → 26 taken 60 times.
✓ Branch 28 → 29 taken 60 times.
✗ Branch 28 → 74 not taken.
60 if (CalendarOutbox *outbox = m_deps.calendarOutbox ? m_deps.calendarOutbox() : nullptr)
812
3/3
✓ Branch 39 → 40 taken 60 times.
✓ Branch 40 → 41 taken 60 times.
✓ Branch 41 → 42 taken 60 times.
120 outbox->refreshConflict(uid, m_identity.value(QStringLiteral("accountId")).toString(),
813
2/2
✓ Branch 33 → 34 taken 60 times.
✓ Branch 34 → 35 taken 60 times.
180 m_identity.value(QStringLiteral("calendarPath")).toString());
814
2/2
✓ Branch 55 → 56 taken 12 times.
✓ Branch 55 → 74 taken 15 times.
27 } else if (m_domain == QLatin1String("contact")) {
815
3/5
✓ Branch 57 → 58 taken 12 times.
✗ Branch 57 → 60 not taken.
✓ Branch 58 → 59 taken 12 times.
✓ Branch 61 → 62 taken 12 times.
✗ Branch 61 → 74 not taken.
12 if (ContactOutbox *outbox = m_deps.contactOutbox ? m_deps.contactOutbox() : nullptr)
816
3/3
✓ Branch 66 → 67 taken 12 times.
✓ Branch 67 → 68 taken 12 times.
✓ Branch 68 → 69 taken 12 times.
24 outbox->refreshConflict(m_identity.value(QStringLiteral("bookId")).toLongLong(), uid);
817 }
818 87 }
819