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

data/MergePlan.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QList>
4 #include <QString>
5 #include <QStringList>
6
7 #include "data/TextMerge3.h"
8
9 // The generic plan of a three-way merge (Sprint 107 S107.2.1, §5.2).
10 //
11 // Every merge engine (ICalMergePlan, VCardMergePlan, SieveMergePlan,
12 // SettingsMergePlan) splits into analyze(), which describes each mergeable
13 // item with its three values and who changed it, and apply(), which renders
14 // bytes from a plan plus the choices made on it. The old merge() functions
15 // are apply(analyze()) with the default choices; the resolver UI changes
16 // choices in between.
17 //
18 // Item identity (`id`) is stable for the same input: it names the
19 // component and the property or value ("VEVENT:uid:rid::SUMMARY",
20 // "SET:EMAIL\x1fkim@example\x1fwork"), never a position. `carryChoices`
21 // relies on it (D-9).
22 //
23 // Invariants (unit + fuzz, gate G4):
24 // - defaultChoice(state) never picks a value that neither side wrote:
25 // LocalOnly → Local, ServerOnly → Server, Same/BothSame → Local,
26 // Conflict → None.
27 // - openConflicts() counts every row the user must still decide; a
28 // coupled group (D-4) counts once unless its members are chosen
29 // separately.
30 // - Nothing in this unit parses or renders domain bytes.
31 namespace Merge {
32
33 // One side's value of one item.
34 struct Value {
35 bool present = false;
36 QString text; // the raw lines / bytes of this item on that side
37 };
38
39 enum class Kind {
40 Scalar, // one property (title, start time, priority …)
41 SetValue, // one value of a multi-valued property (attendee, e-mail …)
42 Text, // long text chosen per line hunk (notes, a whole script)
43 Component, // a sub-component present on one side (occurrence, rule block)
44 Order, // the order of blocks (Sieve)
45 Deletion, // removed on one side, changed on the other
46 };
47
48 enum class State {
49 Same, // all three equal (or unchanged on both sides)
50 LocalOnly, // only this computer changed it
51 ServerOnly, // only the server changed it
52 BothSame, // both changed it the same way
53 Conflict, // both changed it differently
54 };
55
56 enum class Choice {
57 None, // open conflict
58 Local, // the value on this computer
59 Server, // the value on the server
60 Both, // set values and hunks: keep both sides' additions
61 Base, // the value both started from
62 Edited, // text: hunk choices and/or manual edits (see Item::edited)
63 };
64
65 struct Item {
66 QString id;
67 QString name; // machine label: property name, block name, settings key
68 QString group; // coupled group id ("time", "completion", …) or ""
69 QString parent; // component item id of a nested row, or ""
70 QString context; // domain note, e.g. the RECURRENCE-ID of an occurrence
71 Kind kind = Kind::Scalar;
72 Value base;
73 Value local;
74 Value server;
75 State state = State::Same;
76 Choice choice = Choice::None;
77 bool touched = false; // chosen by the user (D-9 carry-over)
78 // Kind::Text: the line hunks against the base and one choice per hunk
79 // (Choice::None for an open conflicting hunk). `edited` (non-null)
80 // replaces the hunk result by text the user typed.
81 TextMerge::Result hunks;
82 QList<Choice> hunkChoices;
83 QString edited;
84 // Kind::Text: the plain text of each side the hunks were computed from
85 // (an iCalendar DESCRIPTION unescaped); `Value::text` stays the raw line.
86 QString basePlain;
87 QString localPlain;
88 QString serverPlain;
89 };
90
91 struct ValidationProblem {
92 QString rule; // "event-end-before-start", "task-completion", …
93 QStringList itemIds;
94 QString message; // English; the resolver translates through tr()
95 };
96
97 struct Plan {
98 QString domain; // "ical", "vcard", "sieve", "settings"
99 QList<Item> items;
100 bool comparable = true; // false: only a raw text merge is possible (D-12)
101 QString error; // why it is not comparable
102 QStringList separatedGroups; // coupled groups the user chooses per item
103 // The three inputs; apply() renders from them and the choices.
104 QString baseText;
105 QString localText;
106 QString serverText;
107
108 int indexOf(const QString &id) const;
109 const Item *item(const QString &id) const;
110 // Rows the user must still decide (open Kind::Text hunks included).
111 int openConflicts() const;
112 // Items that changed and merged without a question (D-5).
113 int combinedCount() const;
114 int conflictCount() const; // items in State::Conflict (decided or not)
115 21624 bool groupSeparated(const QString &group) const { return separatedGroups.contains(group); }
116 // A coupled group whose members belong to different components (Sprint
117 // 107 J3: "occurrence:<id>" = an occurrence and the master's EXDATE value);
118 // every other group stays inside one parent.
119
1/1
✓ Branch 3 → 4 taken 147174 times.
147174 static bool crossParentGroup(const QString &group) { return group.startsWith(QLatin1String("occurrence:")); }
120 };
121
122 Choice defaultChoice(State state);
123 // The state of three values: unchanged / one side / both alike / conflict.
124 State stateOf(const Value &base, const Value &local, const Value &server);
125 // Sets every item's choice to its default (fresh plan).
126 void applyDefaults(Plan *plan);
127 // The user picks `choice` for `id`; members of a coupled group follow
128 // unless the group is separated. Returns false for an unknown id or a
129 // choice the item kind does not offer.
130 bool choose(Plan *plan, const QString &id, Choice choice);
131 // Picks Local (or Server) for every item: "Keep all mine" / "Take all
132 // from the server" (D-6). Text items follow as whole values.
133 void chooseAll(Plan *plan, Choice side);
134 // Text items: the choice of one hunk, or the user's text.
135 bool chooseHunk(Plan *plan, const QString &id, int hunk, Choice choice);
136 bool editText(Plan *plan, const QString &id, const QString &text);
137 // The value an item contributes to the result; `*present` false when the
138 // chosen side has no value (removed). Open conflicts resolve to the local
139 // value so a preview always renders.
140 QString resultText(const Item &item, bool *present = nullptr);
141 bool itemOpen(const Plan &plan, const Item &item);
142 // When every item's result equals one side's value: Local or Server, else
143 // None. The resolver then writes that side's bytes unchanged (G2).
144 Choice uniformSide(const Plan &plan);
145 // D-9: copies the user's choices of `previous` onto `next` for every item
146 // whose id exists in both and whose local and server values are unchanged.
147 // Returns the number of choices carried over.
148 int carryChoices(const Plan &previous, Plan *next);
149
150 const char *kindName(Kind kind);
151 const char *stateName(State state);
152 const char *choiceName(Choice choice);
153 Choice choiceFromName(const QString &name);
154
155 } // namespace Merge
156