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: 100.0% 16 / 0 / 16
Functions: 100.0% 16 / 0 / 16
Branches: -% 0 / 0 / 0

presentation/QuickConflictResolver.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QObject>
4 #include <QPointer>
5 #include <QSet>
6 #include <QVariantMap>
7 #include <functional>
8
9 #include "ConflictItemModel.h"
10 #include "ConflictValueText.h"
11 #include "data/MergePlan.h"
12
13 class CalendarOutbox;
14 class CalendarStore;
15 class ContactOutbox;
16 class ContactStore;
17 class QuickRulesBackend;
18 class SettingsSyncSession;
19
20 // The conflict resolver behind the sheet (Sprint 107 S107.4.3, §5.1): opens
21 // one conflict of any domain as a merge plan, keeps the user's choices,
22 // validates (D-10), applies through the domain's write path and reloads
23 // when the server changed again (D-9). Nothing is written before apply()
24 // (D-7); cancel() leaves stores and outboxes untouched (G5).
25 //
26 // Domains and identities (open()):
27 // "event", "task" {uid, accountId, calendarPath}
28 // "contact" {bookId, uid} or {key: "<bookId>/<uid>"}
29 // "rules" {} (the conflict QuickRulesBackend holds)
30 // "settings" {} (the conflict SettingsSyncSession shows)
31 // Whole-side results are written through the existing whole-side paths
32 // (resolveConflict "mine"/"server", useLocal/useRemote) so their bytes are
33 // exactly the banner's (G2).
34 //
35 // Logs (mailjd.conflict): domain, counts, durations, rule ids; never values.
36 class QuickConflictResolver : public QObject {
37 175 Q_OBJECT
38 Q_PROPERTY(bool active READ active NOTIFY changed)
39 Q_PROPERTY(QString domain READ domain NOTIFY changed)
40 Q_PROPERTY(QString title READ title NOTIFY changed)
41 Q_PROPERTY(QString subtitle READ subtitle NOTIFY changed)
42 Q_PROPERTY(QString kind READ kind NOTIFY changed)
43 Q_PROPERTY(int openCount READ openCount NOTIFY changed)
44 Q_PROPERTY(int questionCount READ questionCount NOTIFY changed)
45 Q_PROPERTY(int combinedCount READ combinedCount NOTIFY changed)
46 Q_PROPERTY(bool canApply READ canApply NOTIFY changed)
47 Q_PROPERTY(bool canKeepBoth READ canKeepBoth NOTIFY changed)
48 Q_PROPERTY(bool comparable READ comparable NOTIFY changed)
49 Q_PROPERTY(bool showOriginal READ showOriginal WRITE setShowOriginal NOTIFY changed)
50 Q_PROPERTY(bool touched READ touched NOTIFY changed)
51 Q_PROPERTY(bool applying READ applying NOTIFY changed)
52 Q_PROPERTY(QString validationMessage READ validationMessage NOTIFY changed)
53 Q_PROPERTY(QString notice READ notice NOTIFY changed)
54 Q_PROPERTY(QString serverLabel READ serverLabel NOTIFY changed)
55 Q_PROPERTY(QVariantList deletionChanges READ deletionChanges NOTIFY changed)
56 Q_PROPERTY(ConflictItemModel *rows READ rows CONSTANT)
57 // Bumped with every new plan: the photo cells' URLs change with it.
58 Q_PROPERTY(int photoSerial READ photoSerial NOTIFY changed)
59
60 public:
61 struct Deps {
62 std::function<CalendarOutbox *()> calendarOutbox;
63 std::function<CalendarStore *()> calendarStore;
64 std::function<ContactOutbox *()> contactOutbox;
65 std::function<ContactStore *()> contactStore;
66 std::function<QuickRulesBackend *()> rules;
67 std::function<SettingsSyncSession *()> settings;
68 };
69 explicit QuickConflictResolver(Deps deps, QObject *parent = nullptr);
70
71 2340 bool active() const { return m_active; }
72 1044 QString domain() const { return m_domain; }
73 1894 QString title() const { return m_title; }
74 948 QString subtitle() const { return m_subtitle; }
75 // "changed", "deletedOnServer", "deletedHere", "moved", "raw"
76 2829 QString kind() const { return m_kind; }
77 4937 int openCount() const { return m_plan.openConflicts(); }
78 int questionCount() const;
79 int combinedCount() const;
80 bool canApply() const;
81 bool canKeepBoth() const;
82 1 bool comparable() const { return m_plan.comparable; }
83 3025 bool showOriginal() const { return m_showOriginal; }
84 void setShowOriginal(bool show);
85 bool touched() const;
86 3781 bool applying() const { return m_applying; }
87 2861 QString validationMessage() const { return m_validationMessage; }
88 1912 QString notice() const { return m_notice; }
89 947 QString serverLabel() const { return m_serverLabel; }
90 1895 QVariantList deletionChanges() const { return m_deletionChanges; }
91 257 ConflictItemModel *rows() { return &m_rows; }
92 486 int photoSerial() const { return m_photoSerial; }
93 const Merge::Plan &plan() const { return m_plan; }
94
95 Q_INVOKABLE bool open(const QString &domain, const QVariantMap &identity);
96 // The banner's count line (§4.2) without opening anything:
97 // {kind, questions, combined, keepBoth}; {} when there is no conflict.
98 Q_INVOKABLE QVariantMap preview(const QString &domain, const QVariantMap &identity) const;
99 Q_INVOKABLE bool choose(const QString &rowId, const QString &side);
100 Q_INVOKABLE void chooseAll(const QString &side);
101 Q_INVOKABLE bool setValueIncluded(const QString &rowId, const QString &valueId, bool included);
102 Q_INVOKABLE bool takeBoth(const QString &rowId);
103 Q_INVOKABLE bool chooseHunk(const QString &rowId, int hunk, const QString &side);
104 Q_INVOKABLE bool acceptText(const QString &rowId);
105 Q_INVOKABLE bool editText(const QString &rowId, const QString &text);
106 Q_INVOKABLE bool separate(const QString &rowId);
107 Q_INVOKABLE bool fixProblem();
108 Q_INVOKABLE QString resultTextOf(const QString &rowId) const;
109 // The inline photo bytes of a photo row's side ("local", "server",
110 // "base"); empty for a linked or absent photo (ConflictPhotoProvider).
111 QByteArray photoBytes(const QString &rowId, const QString &side) const;
112 // Writes the result; the outcome arrives as applied()/applyFailed().
113 Q_INVOKABLE void apply();
114 // Whole sides of the banner and the deletion card (D-6): "mine",
115 // "server", "both".
116 Q_INVOKABLE void applyWholeSide(const QString &choice);
117 Q_INVOKABLE void cancel();
118 // S107.3.6: ask the server again (also done once on open).
119 Q_INVOKABLE void checkServer();
120
121 signals:
122 void changed();
123 void applied(const QString &message);
124 void applyFailed(const QString &message);
125 // The open conflict went away by itself (resolved elsewhere, or combined
126 // with the server's newer version); the sheet closed.
127 void vanished(const QString &message);
128 void closed();
129
130 private:
131 bool openEvent(const QVariantMap &identity, bool task);
132 bool openContact(const QVariantMap &identity);
133 bool openRules();
134 bool openSettings();
135 void setPlan(Merge::Plan plan, bool reload);
136 void rebuild();
137 void validate();
138 void finishApply(bool ok, const QString &message);
139 bool reloadIfChanged();
140 void close();
141 void connectSources();
142 QList<const Merge::Item *> membersOf(const QString &rowId) const;
143
144 Deps m_deps;
145 ConflictItemModel m_rows;
146 ConflictValueText::Context m_context;
147 Merge::Plan m_plan;
148 QList<Merge::ValidationProblem> m_problems;
149 QVariantMap m_identity;
150 QString m_domain;
151 QString m_title;
152 QString m_subtitle;
153 QString m_kind;
154 QString m_serverEtag; // identity of the shown server version
155 QString m_validationMessage;
156 QString m_notice;
157 QString m_serverLabel;
158 QVariantList m_deletionChanges;
159 QSet<QString> m_changedAgain; // row ids whose server value changed in the last reload
160 bool m_active = false;
161 bool m_showOriginal = false;
162 bool m_applying = false;
163 qint64 m_openedAt = 0;
164 int m_photoSerial = 0;
165 };
166