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: 89.5% 229 / 0 / 256
Functions: 88.6% 31 / 0 / 35
Branches: 78.5% 437 / 0 / 557

presentation/QuickTaskModel.cpp
Line Branch Exec Source
1 #include "PimEditorTime.h"
2 #include "QuickTaskModel.h"
3 #include "data/CalendarStore.h"
4 #include <QLocale>
5 #include <QMetaObject>
6 #include <QRegularExpression>
7 #include <algorithm>
8
9 807 QuickTaskModel::QuickTaskModel(QString databasePath, QObject *parent)
10
5/11
✓ Branch 5 → 6 taken 807 times.
✓ Branch 7 → 8 taken 807 times.
✓ Branch 17 → 18 taken 807 times.
✓ Branch 19 → 20 taken 1614 times.
✓ Branch 19 → 21 taken 807 times.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 38 not taken.
✗ Branch 45 → 46 not taken.
✗ Branch 45 → 49 not taken.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 49 not taken.
4035 : QAbstractListModel(parent), m_databasePath(std::move(databasePath)) {
11
1/1
✓ Branch 29 → 30 taken 807 times.
807 m_pool.setMaxThreadCount(1);
12
2/2
✓ Branch 30 → 31 taken 807 times.
✓ Branch 31 → 32 taken 807 times.
807 m_options.weekStart = QLocale().firstDayOfWeek();
13
1/1
✓ Branch 33 → 34 taken 807 times.
807 reload();
14 807 }
15 807 QuickTaskModel::~QuickTaskModel() { m_pool.waitForDone(); }
16 83 QHash<int, QByteArray> QuickTaskModel::roleNames() const {
17 ✗ return {{TaskSummary, "taskSummary"}, {TaskNotes, "taskNotes"}, {TaskDueText, "taskDueText"},
18 ✗ {TaskOverdue, "taskOverdue"}, {TaskProgress, "taskProgress"}, {TaskStarred, "taskStarred"},
19 ✗ {TaskCompleted, "taskCompleted"}, {TaskCalendar, "taskCalendar"}, {TaskPriorityText, "taskPriorityText"},
20 ✗ {TaskColor, "taskColor"}, {TaskIdentity, "taskIdentity"}, {TaskKey, "taskKey"}, {Group, "group"},
21 ✗ {Depth, "depth"}, {HasChildren, "hasChildren"}, {Expanded, "expanded"}, {ChildProgress, "childProgress"},
22 ✗ {ChecklistProgress, "checklistProgress"}, {DueKind, "dueKind"}, {PriorityLevel, "priorityLevel"},
23 ✗ {StatusKind, "statusKind"}, {Tags, "tags"}, {HasReminder, "hasReminder"}, {Recurring, "recurring"},
24 ✗ {HasLinkedMail, "hasLinkedMail"}, {SyncState, "syncState"}, {ReadOnly, "readOnly"},
25
2/4
✓ Branch 34 → 35 taken 2490 times.
✓ Branch 34 → 36 taken 83 times.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
2573 {StartsLater, "startsLater"}, {StartText, "startText"}, {Orphan, "orphan"}};
26 83 }
27
2/2
✓ Branch 3 → 4 taken 22 times.
✓ Branch 3 → 5 taken 15777 times.
15799 int QuickTaskModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_rows.size(); }
28 10979 QVariant QuickTaskModel::data(const QModelIndex &index, int role) const {
29
6/8
✓ Branch 3 → 4 taken 10976 times.
✓ Branch 3 → 9 taken 3 times.
✓ Branch 5 → 6 taken 10976 times.
✗ Branch 5 → 9 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 10976 times.
✓ Branch 11 → 12 taken 3 times.
✓ Branch 11 → 13 taken 10976 times.
10979 if (!index.isValid() || index.row() < 0 || index.row() >= m_rows.size()) return {};
30 10976 const auto &row = m_rows[index.row()]; const auto &task = row.task;
31
2/2
✓ Branch 15 → 16 taken 10976 times.
✓ Branch 16 → 17 taken 10976 times.
10976 const QString key = taskSelectionKey(task); const QDate today = QDate::currentDate();
32
6/6
✓ Branch 17 → 18 taken 10976 times.
✓ Branch 18 → 19 taken 10456 times.
✓ Branch 18 → 32 taken 520 times.
✓ Branch 19 → 20 taken 10456 times.
✓ Branch 20 → 21 taken 7319 times.
✓ Branch 20 → 32 taken 3137 times.
18295 const bool overdue = TaskListLayout::open(task) && task.due.isValid() &&
33
14/18
✓ Branch 21 → 22 taken 5712 times.
✓ Branch 21 → 26 taken 1607 times.
✓ Branch 22 → 23 taken 5712 times.
✓ Branch 25 → 31 taken 466 times.
✓ Branch 25 → 32 taken 5246 times.
✓ Branch 26 → 27 taken 1607 times.
✓ Branch 27 → 28 taken 1607 times.
✓ Branch 28 → 29 taken 1607 times.
✓ Branch 30 → 31 taken 744 times.
✓ Branch 30 → 32 taken 863 times.
✓ Branch 33 → 34 taken 1607 times.
✓ Branch 33 → 35 taken 9369 times.
✓ Branch 35 → 36 taken 1607 times.
✓ Branch 35 → 37 taken 9369 times.
✗ Branch 188 → 189 not taken.
✗ Branch 188 → 190 not taken.
✗ Branch 192 → 193 not taken.
✗ Branch 192 → 194 not taken.
18295 (task.dueAllDay ? task.due.date() < today : task.due.toLocalTime() < QDateTime::currentDateTime());
34
1/1
✓ Branch 37 → 38 taken 10976 times.
10976 const int priority = TaskListLayout::priorityLevel(task.priority);
35
29/31
✓ Branch 38 → 39 taken 377 times.
✓ Branch 38 → 50 taken 378 times.
✓ Branch 38 → 51 taken 380 times.
✗ Branch 38 → 52 not taken.
✓ Branch 38 → 53 taken 378 times.
✓ Branch 38 → 59 taken 3 times.
✓ Branch 38 → 60 taken 378 times.
✗ Branch 38 → 61 not taken.
✓ Branch 38 → 63 taken 1 time.
✓ Branch 38 → 67 taken 378 times.
✓ Branch 38 → 68 taken 377 times.
✓ Branch 38 → 69 taken 1 time.
✓ Branch 38 → 79 taken 1756 times.
✓ Branch 38 → 80 taken 377 times.
✓ Branch 38 → 81 taken 377 times.
✓ Branch 38 → 82 taken 379 times.
✓ Branch 38 → 84 taken 377 times.
✓ Branch 38 → 98 taken 381 times.
✓ Branch 38 → 139 taken 377 times.
✓ Branch 38 → 157 taken 377 times.
✓ Branch 38 → 158 taken 377 times.
✓ Branch 38 → 159 taken 377 times.
✓ Branch 38 → 160 taken 377 times.
✓ Branch 38 → 162 taken 377 times.
✓ Branch 38 → 164 taken 378 times.
✓ Branch 38 → 166 taken 377 times.
✓ Branch 38 → 167 taken 377 times.
✓ Branch 38 → 168 taken 377 times.
✓ Branch 38 → 170 taken 377 times.
✓ Branch 38 → 171 taken 377 times.
✓ Branch 38 → 184 taken 153 times.
10976 switch (role) {
36
2/4
✓ Branch 47 → 48 taken 1508 times.
✓ Branch 47 → 49 taken 377 times.
✗ Branch 198 → 199 not taken.
✗ Branch 198 → 200 not taken.
1885 case TaskIdentity: return QVariantMap{{"uid", task.uid}, {"accountId", task.accountId}, {"calendarPath", task.calendarPath}, {"key", key}};
37 378 case TaskKey: return key;
38 380 case TaskSummary: return task.summary;
39 ✗ case TaskNotes: return task.description;
40
1/1
✓ Branch 55 → 56 taken 378 times.
378 case TaskDueText: return TaskListLayout::dueText(task, today, m_options.grouping == QLatin1String("due"));
41 3 case TaskOverdue: return overdue;
42 378 case TaskProgress: return task.percentComplete;
43 ✗ case TaskStarred: return task.isStarred();
44 1 case TaskCompleted: return task.status == QLatin1String("COMPLETED");
45 378 case TaskCalendar: return task.calendarDisplayName;
46 377 case TaskColor: return task.color;
47
2/9
✓ Branch 69 → 70 taken 1 time.
✗ Branch 69 → 71 not taken.
✓ Branch 70 → 76 taken 1 time.
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 73 not taken.
✗ Branch 72 → 76 not taken.
✗ Branch 73 → 74 not taken.
✗ Branch 73 → 75 not taken.
✗ Branch 74 → 76 not taken.
1 case TaskPriorityText: return priority == 1 ? tr("High") : priority == 2 ? tr("Normal") : priority == 3 ? tr("Low") : QString{};
48 1756 case Group: return row.group;
49 377 case Depth: return row.depth;
50 377 case HasChildren: return row.children > 0;
51 379 case Expanded: return !m_options.collapsedParents.contains(key);
52
9/13
✓ Branch 84 → 85 taken 19 times.
✓ Branch 84 → 90 taken 358 times.
✓ Branch 85 → 86 taken 19 times.
✓ Branch 87 → 88 taken 19 times.
✓ Branch 89 → 91 taken 19 times.
✓ Branch 93 → 94 taken 19 times.
✓ Branch 93 → 95 taken 358 times.
✓ Branch 95 → 96 taken 19 times.
✓ Branch 95 → 97 taken 358 times.
✗ Branch 212 → 213 not taken.
✗ Branch 212 → 214 not taken.
✗ Branch 217 → 218 not taken.
✗ Branch 217 → 219 not taken.
377 case ChildProgress: return row.children ? QString("%1/%2").arg(row.completedChildren).arg(row.children) : QString{};
53 381 case ChecklistProgress: {
54
4/7
✓ Branch 98 → 99 taken 10 times.
✓ Branch 98 → 111 taken 371 times.
✓ Branch 100 → 101 taken 10 times.
✗ Branch 100 → 111 not taken.
✓ Branch 105 → 106 taken 10 times.
✗ Branch 229 → 230 not taken.
✗ Branch 229 → 231 not taken.
393 static const QRegularExpression expression(QStringLiteral("(?m)^\\s*[-*] \\[([ xX])\\] "));
55
1/1
✓ Branch 112 → 113 taken 381 times.
381 auto matches = expression.globalMatch(task.description); int total = 0, done = 0;
56
7/7
✓ Branch 115 → 116 taken 102 times.
✓ Branch 116 → 117 taken 102 times.
✓ Branch 120 → 121 taken 42 times.
✓ Branch 120 → 122 taken 60 times.
✓ Branch 122 → 123 taken 483 times.
✓ Branch 123 → 114 taken 102 times.
✓ Branch 123 → 124 taken 381 times.
483 while (matches.hasNext()) { ++total; if (matches.next().captured(1) != QLatin1String(" ")) ++done; }
57
9/13
✓ Branch 124 → 125 taken 39 times.
✓ Branch 124 → 130 taken 342 times.
✓ Branch 125 → 126 taken 39 times.
✓ Branch 127 → 128 taken 39 times.
✓ Branch 129 → 131 taken 39 times.
✓ Branch 133 → 134 taken 39 times.
✓ Branch 133 → 135 taken 342 times.
✓ Branch 135 → 136 taken 39 times.
✓ Branch 135 → 137 taken 342 times.
✗ Branch 239 → 240 not taken.
✗ Branch 239 → 241 not taken.
✗ Branch 244 → 245 not taken.
✗ Branch 244 → 246 not taken.
381 return total ? QString("%1/%2").arg(done).arg(total) : QString{};
58 381 }
59
9/9
✓ Branch 139 → 140 taken 377 times.
✓ Branch 140 → 141 taken 107 times.
✓ Branch 140 → 142 taken 270 times.
✓ Branch 141 → 152 taken 107 times.
✓ Branch 142 → 143 taken 41 times.
✓ Branch 142 → 144 taken 229 times.
✓ Branch 143 → 152 taken 41 times.
✓ Branch 150 → 152 taken 40 times.
✓ Branch 151 → 152 taken 189 times.
983 case DueKind: return !task.due.isValid() ? QString("none") : overdue ? QString("overdue") :
60
9/11
✓ Branch 144 → 145 taken 198 times.
✓ Branch 144 → 146 taken 31 times.
✓ Branch 145 → 148 taken 198 times.
✓ Branch 146 → 147 taken 31 times.
✓ Branch 147 → 148 taken 31 times.
✓ Branch 149 → 150 taken 40 times.
✓ Branch 149 → 151 taken 189 times.
✓ Branch 154 → 155 taken 31 times.
✓ Branch 154 → 156 taken 346 times.
✗ Branch 253 → 254 not taken.
✗ Branch 253 → 255 not taken.
983 (task.dueAllDay ? task.due.date() : task.due.toLocalTime().date()) == today ? QString("today") : QString("later");
61 377 case PriorityLevel: return priority;
62 377 case StatusKind: return task.status;
63 377 case Tags: return task.tags;
64 377 case HasReminder: return !task.alarmTimes.isEmpty();
65 377 case Recurring: return !task.rrule.isEmpty();
66 378 case HasLinkedMail: return !task.relatedMessageId.isEmpty();
67 377 case SyncState: return task.syncState;
68 377 case ReadOnly: return task.readOnly;
69
1/1
✓ Branch 168 → 169 taken 377 times.
377 case StartsLater: return TaskListLayout::startsLater(task, today);
70 377 case Orphan: return row.orphan;
71 377 case StartText: {
72
2/3
✓ Branch 171 → 172 taken 377 times.
✓ Branch 172 → 173 taken 377 times.
✗ Branch 172 → 177 not taken.
377 if (!task.dtStart.isValid()) return QString{};
73 ✗ CalendarTask start = task; start.due = task.dtStart; start.dueAllDay = task.startAllDay;
74 ✗ return TaskListLayout::dueText(start, today);
75 ✗ }
76 153 default: return {};
77 }
78 11353 }
79 94 QVariantMap QuickTaskModel::taskAt(int row) const {
80
2/4
✓ Branch 2 → 3 taken 94 times.
✗ Branch 2 → 7 not taken.
✓ Branch 4 → 5 taken 94 times.
✗ Branch 4 → 7 not taken.
94 return row >= 0 && row < m_rows.size() ? record(m_rows[row].task) : QVariantMap{};
81 }
82 655 QVariantMap QuickTaskModel::record(const CalendarTask &task) {
83 1310 return {{QStringLiteral("uid"), task.uid},
84 1310 {QStringLiteral("calendarPath"), task.calendarPath},
85 1310 {QStringLiteral("accountId"), task.accountId},
86 1310 {QStringLiteral("summary"), task.summary},
87 1310 {QStringLiteral("description"), task.description},
88 1310 {QStringLiteral("due"), task.due},
89
1/1
✓ Branch 29 → 30 taken 655 times.
1310 {QStringLiteral("dueText"), PimEditorTime::display(task.due, task.dueAllDay)},
90 1310 {QStringLiteral("dueAllDay"), task.dueAllDay},
91 1310 {QStringLiteral("percentComplete"), task.percentComplete},
92 1310 {QStringLiteral("priority"), task.priority},
93 1310 {QStringLiteral("status"), task.status},
94 1965 {QStringLiteral("completed"), task.status == QStringLiteral("COMPLETED")},
95 1310 {QStringLiteral("calendarDisplayName"), task.calendarDisplayName},
96 1310 {QStringLiteral("color"), task.color},
97 1310 {QStringLiteral("relatedMessageId"), task.relatedMessageId},
98 1310 {QStringLiteral("etag"), task.etag},
99 1310 {QStringLiteral("resourceHref"), task.resourceHref},
100 655 {"taskKey", taskSelectionKey(task)}, {"startText", PimEditorTime::display(task.dtStart, task.startAllDay)},
101 1965 {"startAllDay", task.startAllDay}, {"parentUid", task.parentUid}, {"rrule", task.rrule},
102
2/4
✓ Branch 90 → 91 taken 17685 times.
✓ Branch 90 → 92 taken 655 times.
✗ Branch 135 → 136 not taken.
✗ Branch 135 → 137 not taken.
18995 {"tags", task.tags}, {"alarmTimes", QVariant::fromValue(task.alarmTimes)}, {"sortOrder", task.sortOrder}, {"syncState", task.syncState}, {"readOnly", task.readOnly}};
103 11790 }
104
105
106 1008 void QuickTaskModel::reload() {
107 1008 ++m_generation;
108
6/6
✓ Branch 3 → 4 taken 1006 times.
✓ Branch 3 → 5 taken 2 times.
✓ Branch 4 → 5 taken 76 times.
✓ Branch 4 → 6 taken 930 times.
✓ Branch 7 → 8 taken 78 times.
✓ Branch 7 → 9 taken 930 times.
1008 if (m_databasePath.isEmpty() || m_loading) return;
109 930 const quint64 generation = m_generation; const QString path = m_databasePath;
110
1/1
✓ Branch 10 → 11 taken 930 times.
930 m_loading = true; emit loadingChanged();
111
1/1
✓ Branch 12 → 13 taken 930 times.
930 m_pool.start([this, generation, path] {
112 930 QList<CalendarTask> tasks;
113
4/5
✓ Branch 2 → 3 taken 930 times.
✓ Branch 3 → 4 taken 930 times.
✓ Branch 4 → 5 taken 930 times.
✗ Branch 4 → 9 not taken.
✓ Branch 5 → 6 taken 930 times.
930 if (CalendarStore store; store.open(path)) tasks = store.listTasks();
114
1/1
✓ Branch 12 → 13 taken 930 times.
930 QMetaObject::invokeMethod(this, [this, generation, tasks = std::move(tasks)]() mutable {
115 840 m_loading = false;
116
2/2
✓ Branch 2 → 3 taken 6 times.
✓ Branch 2 → 5 taken 834 times.
840 if (generation != m_generation) { reload(); return; }
117
1/1
✓ Branch 7 → 8 taken 834 times.
834 applyTasks(std::move(tasks)); emit loadingChanged();
118 }, Qt::QueuedConnection);
119 930 });
120 930 }
121 835 void QuickTaskModel::applyTasks(QList<CalendarTask> tasks) {
122
1/1
✓ Branch 2 → 3 taken 835 times.
835 updateCalendarPaths(tasks); m_loaded = std::move(tasks);
123
2/2
✓ Branch 5 → 6 taken 835 times.
✓ Branch 6 → 7 taken 835 times.
835 const auto counts = TaskListLayout::counts(m_loaded, QDate::currentDate(), m_options.weekStart);
124
4/4
✓ Branch 7 → 8 taken 835 times.
✓ Branch 8 → 9 taken 277 times.
✓ Branch 8 → 11 taken 558 times.
✓ Branch 10 → 11 taken 277 times.
835 if (counts != m_counts) { m_counts = counts; emit countsChanged(); }
125
1/1
✓ Branch 11 → 12 taken 835 times.
835 project();
126 835 }
127 1668 void QuickTaskModel::project() {
128 1668 auto tasks = m_loaded;
129
5/5
✓ Branch 3 → 4 taken 1668 times.
✓ Branch 5 → 6 taken 1668 times.
✓ Branch 6 → 7 taken 1668 times.
✓ Branch 7 → 8 taken 1668 times.
✓ Branch 9 → 10 taken 1668 times.
1668 tasks.erase(std::remove_if(tasks.begin(), tasks.end(), [this](const auto &task) {
130
4/9
✓ Branch 3 → 4 taken 123666 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 123666 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 123666 times.
✓ Branch 10 → 11 taken 123666 times.
✗ Branch 10 → 12 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 16 not taken.
123666 return m_hiddenCalendars.contains(task.calendarPath) || m_hiddenCalendars.contains(calendarSelectionKey(task.accountId, task.calendarPath));
131 }), tasks.end());
132 1668 m_options.filter = m_filterText; m_options.includeCompleted = m_showCompleted;
133
2/2
✓ Branch 11 → 12 taken 1668 times.
✓ Branch 12 → 13 taken 1668 times.
1668 const int hidden = TaskListLayout::hiddenCompleted(tasks, m_options, QDate::currentDate());
134
3/3
✓ Branch 13 → 14 taken 12 times.
✓ Branch 13 → 15 taken 1656 times.
✓ Branch 14 → 15 taken 12 times.
1668 if (hidden != m_hiddenCompleted) { m_hiddenCompleted = hidden; emit countsChanged(); }
135
2/2
✓ Branch 15 → 16 taken 1668 times.
✓ Branch 18 → 19 taken 1668 times.
1668 auto rows = TaskListLayout::project(std::move(tasks), m_options, QDate::currentDate());
136 1668 QVariantMap groups;
137
7/7
✓ Branch 20 → 21 taken 1668 times.
✓ Branch 21 → 22 taken 1668 times.
✓ Branch 25 → 26 taken 99169 times.
✓ Branch 26 → 27 taken 99169 times.
✓ Branch 28 → 29 taken 99169 times.
✓ Branch 35 → 23 taken 99169 times.
✓ Branch 35 → 36 taken 1668 times.
100837 for (const auto &row : rows) groups[row.group] = groups.value(row.group).toInt() + 1;
138
4/4
✓ Branch 36 → 37 taken 1668 times.
✓ Branch 37 → 38 taken 300 times.
✓ Branch 37 → 40 taken 1368 times.
✓ Branch 39 → 40 taken 300 times.
1668 if (groups != m_groupCounts) { m_groupCounts = groups; emit countsChanged(); }
139
1/1
✓ Branch 42 → 43 taken 1668 times.
1668 publish(std::move(rows));
140 1668 }
141 1668 void QuickTaskModel::publish(QList<TaskListLayout::Row> rows) {
142 // Keep the unchanged prefix/suffix alive. Same-key reloads only change data;
143 // insert/remove spans never reset ListView's viewport or selection identity.
144 1668 int prefix = 0, suffix = 0;
145
6/6
✓ Branch 5 → 6 taken 49134 times.
✓ Branch 5 → 15 taken 1632 times.
✓ Branch 7 → 8 taken 49125 times.
✓ Branch 7 → 15 taken 9 times.
✓ Branch 13 → 14 taken 49098 times.
✓ Branch 13 → 15 taken 27 times.
99891 while (prefix < m_rows.size() && prefix < rows.size() &&
146
10/14
✓ Branch 8 → 9 taken 49125 times.
✓ Branch 9 → 10 taken 49125 times.
✓ Branch 10 → 11 taken 49125 times.
✓ Branch 11 → 12 taken 49125 times.
✓ Branch 16 → 17 taken 49125 times.
✓ Branch 16 → 18 taken 1641 times.
✓ Branch 18 → 19 taken 49125 times.
✓ Branch 18 → 20 taken 1641 times.
✓ Branch 20 → 3 taken 49098 times.
✓ Branch 20 → 21 taken 1668 times.
✗ Branch 165 → 166 not taken.
✗ Branch 165 → 167 not taken.
✗ Branch 169 → 170 not taken.
✗ Branch 169 → 171 not taken.
99891 taskSelectionKey(m_rows[prefix].task) == taskSelectionKey(rows[prefix].task)) ++prefix;
147
6/6
✓ Branch 24 → 25 taken 147 times.
✓ Branch 24 → 36 taken 1640 times.
✓ Branch 26 → 27 taken 137 times.
✓ Branch 26 → 36 taken 10 times.
✓ Branch 34 → 35 taken 119 times.
✓ Branch 34 → 36 taken 18 times.
1924 while (suffix < m_rows.size() - prefix && suffix < rows.size() - prefix &&
148
10/14
✓ Branch 28 → 29 taken 137 times.
✓ Branch 29 → 30 taken 137 times.
✓ Branch 31 → 32 taken 137 times.
✓ Branch 32 → 33 taken 137 times.
✓ Branch 37 → 38 taken 137 times.
✓ Branch 37 → 39 taken 1650 times.
✓ Branch 39 → 40 taken 137 times.
✓ Branch 39 → 41 taken 1650 times.
✓ Branch 41 → 22 taken 119 times.
✓ Branch 41 → 42 taken 1668 times.
✗ Branch 173 → 174 not taken.
✗ Branch 173 → 175 not taken.
✗ Branch 177 → 178 not taken.
✗ Branch 177 → 179 not taken.
1924 taskSelectionKey(m_rows[m_rows.size()-1-suffix].task) == taskSelectionKey(rows[rows.size()-1-suffix].task)) ++suffix;
149
6/6
✓ Branch 44 → 45 taken 1381 times.
✓ Branch 44 → 48 taken 287 times.
✓ Branch 46 → 47 taken 10 times.
✓ Branch 46 → 48 taken 1371 times.
✓ Branch 49 → 50 taken 10 times.
✓ Branch 49 → 107 taken 1658 times.
1668 if (m_rows.size() == rows.size() && prefix < rows.size()) {
150 17 const auto sameKeys = [&rows](const QList<TaskListLayout::Row> &candidate) {
151
2/2
✓ Branch 14 → 3 taken 150 times.
✓ Branch 14 → 15 taken 3 times.
153 for (int i = 0; i < rows.size(); ++i)
152
5/5
✓ Branch 3 → 4 taken 150 times.
✓ Branch 4 → 5 taken 150 times.
✓ Branch 6 → 7 taken 150 times.
✓ Branch 10 → 11 taken 14 times.
✓ Branch 10 → 12 taken 136 times.
150 if (taskSelectionKey(candidate[i].task) != taskSelectionKey(rows[i].task)) return false;
153 3 return true;
154 10 };
155 10 int from = -1, to = -1;
156
2/2
✓ Branch 69 → 51 taken 45 times.
✓ Branch 69 → 70 taken 1 time.
46 for (int i = prefix + 1; i < m_rows.size(); ++i) {
157
6/6
✓ Branch 51 → 52 taken 45 times.
✓ Branch 52 → 53 taken 45 times.
✓ Branch 53 → 54 taken 45 times.
✓ Branch 54 → 55 taken 45 times.
✓ Branch 58 → 59 taken 9 times.
✓ Branch 58 → 67 taken 36 times.
45 if (taskSelectionKey(m_rows[i].task) == taskSelectionKey(rows[prefix].task)) {
158
1/1
✓ Branch 60 → 61 taken 9 times.
9 auto candidate = m_rows; candidate.move(i, prefix);
159
3/3
✓ Branch 61 → 62 taken 9 times.
✓ Branch 62 → 63 taken 1 time.
✓ Branch 62 → 64 taken 8 times.
9 if (sameKeys(candidate)) { from = i; to = prefix; } break;
160 9 }
161 }
162
2/2
✓ Branch 70 → 71 taken 9 times.
✓ Branch 70 → 91 taken 1 time.
10 if (from < 0) {
163
2/2
✓ Branch 90 → 72 taken 60 times.
✓ Branch 90 → 91 taken 1 time.
61 for (int i = prefix + 1; i < rows.size(); ++i)
164
6/6
✓ Branch 72 → 73 taken 60 times.
✓ Branch 73 → 74 taken 60 times.
✓ Branch 74 → 75 taken 60 times.
✓ Branch 75 → 76 taken 60 times.
✓ Branch 79 → 80 taken 8 times.
✓ Branch 79 → 88 taken 52 times.
60 if (taskSelectionKey(rows[i].task) == taskSelectionKey(m_rows[prefix].task)) {
165
1/1
✓ Branch 81 → 82 taken 8 times.
8 auto candidate = m_rows; candidate.move(prefix, i);
166
3/3
✓ Branch 82 → 83 taken 8 times.
✓ Branch 83 → 84 taken 2 times.
✓ Branch 83 → 85 taken 6 times.
8 if (sameKeys(candidate)) { from = prefix; to = i; } break;
167 8 }
168 }
169
8/9
✓ Branch 91 → 92 taken 3 times.
✓ Branch 91 → 100 taken 7 times.
✓ Branch 92 → 93 taken 2 times.
✓ Branch 92 → 94 taken 1 time.
✓ Branch 97 → 98 taken 3 times.
✓ Branch 98 → 99 taken 3 times.
✗ Branch 98 → 100 not taken.
✓ Branch 101 → 102 taken 3 times.
✓ Branch 101 → 106 taken 7 times.
10 if (from >= 0 && beginMoveRows({}, from, from, {}, to > from ? to + 1 : to)) {
170
2/2
✓ Branch 102 → 103 taken 3 times.
✓ Branch 103 → 104 taken 3 times.
3 m_rows.move(from, to); endMoveRows(); prefix = rows.size(); suffix = 0;
171 }
172 }
173 1668 const int removed = m_rows.size() - prefix - suffix, inserted = rows.size() - prefix - suffix;
174
3/3
✓ Branch 109 → 110 taken 25 times.
✓ Branch 109 → 114 taken 1643 times.
✓ Branch 111 → 112 taken 25 times.
1668 if (removed) { beginRemoveRows({}, prefix, prefix + removed - 1); m_rows.remove(prefix, removed); endRemoveRows(); }
175
3/3
✓ Branch 114 → 115 taken 284 times.
✓ Branch 114 → 123 taken 1384 times.
✓ Branch 116 → 117 taken 284 times.
1668 if (inserted) { beginInsertRows({}, prefix, prefix + inserted - 1);
176
2/2
✓ Branch 121 → 118 taken 49919 times.
✓ Branch 121 → 122 taken 284 times.
50203 for (int i = 0; i < inserted; ++i) m_rows.insert(prefix + i, rows[prefix + i]);
177 284 endInsertRows(); }
178
2/2
✓ Branch 154 → 124 taken 99169 times.
✓ Branch 154 → 155 taken 1668 times.
100837 for (int i = 0; i < rows.size(); ++i) {
179
4/4
✓ Branch 124 → 125 taken 49997 times.
✓ Branch 124 → 127 taken 49172 times.
✓ Branch 125 → 126 taken 49919 times.
✓ Branch 125 → 127 taken 78 times.
99169 if (i >= prefix && i < prefix + inserted) continue;
180 49250 const auto &old = m_rows[i]; const auto &next = rows[i];
181
2/2
✓ Branch 130 → 131 taken 54 times.
✓ Branch 130 → 140 taken 49196 times.
49250 if (old.group != next.group) {
182 // F94-22: ListView recomputes section headers only on row insertion
183 // and removal; a group change through dataChanged left stale and
184 // duplicated headers ("Completed" twice). Replace the row instead.
185
1/1
✓ Branch 132 → 133 taken 54 times.
54 beginRemoveRows({}, i, i); m_rows.removeAt(i); endRemoveRows();
186
1/1
✓ Branch 136 → 137 taken 54 times.
54 beginInsertRows({}, i, i); m_rows.insert(i, next); endInsertRows();
187 54 continue;
188 }
189 49196 const bool changed = !sameDisplay(old, next);
190 49196 m_rows[i] = next;
191
5/5
✓ Branch 143 → 144 taken 28 times.
✓ Branch 143 → 152 taken 49168 times.
✓ Branch 146 → 147 taken 28 times.
✓ Branch 148 → 149 taken 28 times.
✓ Branch 149 → 150 taken 28 times.
49196 if (changed) emit dataChanged(index(i, 0), index(i, 0));
192 }
193 1668 m_indices.clear();
194
5/5
✓ Branch 157 → 158 taken 99169 times.
✓ Branch 158 → 159 taken 99169 times.
✓ Branch 159 → 160 taken 99169 times.
✓ Branch 163 → 157 taken 99169 times.
✓ Branch 163 → 164 taken 1668 times.
100837 for (int i = 0; i < m_rows.size(); ++i) m_indices.insert(taskSelectionKey(m_rows[i].task), i);
195 1668 }
196 // Field-wise comparison of everything a row or its editor record shows;
197 // replaces comparing two QVariantMap records per row on every publish (G9).
198 49196 bool QuickTaskModel::sameDisplay(const TaskListLayout::Row &a, const TaskListLayout::Row &b) {
199 49196 const auto &x = a.task;
200 49196 const auto &y = b.task;
201
3/6
✓ Branch 4 → 5 taken 49196 times.
✗ Branch 4 → 47 not taken.
✓ Branch 5 → 6 taken 49196 times.
✗ Branch 5 → 47 not taken.
✓ Branch 6 → 7 taken 49196 times.
✗ Branch 6 → 47 not taken.
98392 return a.group == b.group && a.depth == b.depth && a.children == b.children && a.orphan == b.orphan &&
202
5/6
✓ Branch 7 → 8 taken 49196 times.
✗ Branch 7 → 47 not taken.
✓ Branch 9 → 10 taken 49191 times.
✓ Branch 9 → 47 taken 5 times.
✓ Branch 11 → 12 taken 49180 times.
✓ Branch 11 → 47 taken 11 times.
49196 a.completedChildren == b.completedChildren && x.summary == y.summary && x.description == y.description &&
203
4/8
✓ Branch 13 → 14 taken 49180 times.
✗ Branch 13 → 47 not taken.
✓ Branch 14 → 15 taken 49180 times.
✗ Branch 14 → 47 not taken.
✓ Branch 16 → 17 taken 49180 times.
✗ Branch 16 → 47 not taken.
✓ Branch 17 → 18 taken 49180 times.
✗ Branch 17 → 47 not taken.
49180 x.due == y.due && x.dueAllDay == y.dueAllDay && x.dtStart == y.dtStart && x.startAllDay == y.startAllDay &&
204
5/6
✓ Branch 18 → 19 taken 49174 times.
✓ Branch 18 → 47 taken 6 times.
✓ Branch 19 → 20 taken 49173 times.
✓ Branch 19 → 47 taken 1 time.
✓ Branch 21 → 22 taken 49173 times.
✗ Branch 21 → 47 not taken.
49180 x.percentComplete == y.percentComplete && x.priority == y.priority && x.status == y.status &&
205
5/8
✓ Branch 23 → 24 taken 49173 times.
✗ Branch 23 → 47 not taken.
✓ Branch 25 → 26 taken 49172 times.
✓ Branch 25 → 47 taken 1 time.
✓ Branch 27 → 28 taken 49172 times.
✗ Branch 27 → 47 not taken.
✓ Branch 29 → 30 taken 49172 times.
✗ Branch 29 → 47 not taken.
98345 x.completedAt == y.completedAt && x.color == y.color && x.calendarDisplayName == y.calendarDisplayName &&
206
3/6
✓ Branch 31 → 32 taken 49172 times.
✗ Branch 31 → 47 not taken.
✓ Branch 33 → 34 taken 49172 times.
✗ Branch 33 → 47 not taken.
✓ Branch 35 → 36 taken 49172 times.
✗ Branch 35 → 47 not taken.
147516 x.relatedMessageId == y.relatedMessageId && x.etag == y.etag && x.resourceHref == y.resourceHref &&
207
3/6
✓ Branch 37 → 38 taken 49172 times.
✗ Branch 37 → 47 not taken.
✓ Branch 39 → 40 taken 49172 times.
✗ Branch 39 → 47 not taken.
✓ Branch 41 → 42 taken 49172 times.
✗ Branch 41 → 47 not taken.
98344 x.parentUid == y.parentUid && x.rrule == y.rrule && x.tags == y.tags && x.alarmTimes == y.alarmTimes &&
208
6/8
✓ Branch 3 → 4 taken 49196 times.
✗ Branch 3 → 47 not taken.
✓ Branch 42 → 43 taken 49171 times.
✓ Branch 42 → 47 taken 1 time.
✓ Branch 44 → 45 taken 49168 times.
✓ Branch 44 → 47 taken 3 times.
✓ Branch 45 → 46 taken 49168 times.
✗ Branch 45 → 47 not taken.
98392 x.sortOrder == y.sortOrder && x.syncState == y.syncState && x.readOnly == y.readOnly;
209 }
210 56 int QuickTaskModel::indexOfTask(const QString &key) const { return m_indices.value(key, -1); }
211 775 void QuickTaskModel::setShowCompleted(bool show) {
212
2/2
✓ Branch 2 → 3 taken 768 times.
✓ Branch 2 → 4 taken 7 times.
775 if (m_showCompleted == show) return; m_showCompleted = show;
213 7 emit showCompletedChanged(); emit stateChanged(); project();
214 }
215 17 void QuickTaskModel::setFilterText(const QString &text) {
216
4/4
✓ Branch 2 → 3 taken 17 times.
✓ Branch 3 → 4 taken 17 times.
✓ Branch 6 → 7 taken 3 times.
✓ Branch 6 → 8 taken 14 times.
17 const QString filter = text.left(256).trimmed(); if (filter == m_filterText) return;
217
3/3
✓ Branch 9 → 10 taken 14 times.
✓ Branch 10 → 11 taken 14 times.
✓ Branch 11 → 12 taken 14 times.
14 m_filterText = filter; emit filterTextChanged(); emit stateChanged(); project();
218 17 }
219 ✗ void QuickTaskModel::setHiddenCalendars(const QStringList &paths) {
220 ✗ if (m_hiddenCalendars == paths) return; m_hiddenCalendars = paths; emit hiddenCalendarsChanged(); project();
221 }
222 ✗ void QuickTaskModel::toggleCalendar(const QString &path) {
223 ✗ auto paths = m_hiddenCalendars; if (paths.contains(path)) paths.removeAll(path); else paths << path;
224 ✗ setHiddenCalendars(paths);
225 ✗ }
226 817 void QuickTaskModel::setView(const QString &view) {
227
2/2
✓ Branch 3 → 4 taken 784 times.
✓ Branch 3 → 5 taken 33 times.
817 if (view == m_options.view) return; m_options.view = view; emit stateChanged(); project();
228 }
229 775 void QuickTaskModel::setGrouping(const QString &grouping) {
230
9/18
✓ Branch 8 → 9 taken 775 times.
✗ Branch 8 → 11 not taken.
✓ Branch 10 → 11 taken 767 times.
✓ Branch 10 → 12 taken 8 times.
✓ Branch 13 → 14 taken 775 times.
✗ Branch 13 → 15 not taken.
✓ Branch 15 → 16 taken 775 times.
✗ Branch 15 → 19 not taken.
✓ Branch 17 → 18 taken 3100 times.
✓ Branch 17 → 19 taken 775 times.
✓ Branch 19 → 20 taken 767 times.
✓ Branch 19 → 21 taken 8 times.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 33 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 33 not taken.
3875 if (!QStringList{"due", "list", "priority", "none"}.contains(grouping) || grouping == m_options.grouping) return;
231 8 m_options.grouping = grouping; emit stateChanged(); project();
232 775 }
233 769 void QuickTaskModel::setSort(const QString &sort) {
234
9/18
✓ Branch 9 → 10 taken 769 times.
✗ Branch 9 → 12 not taken.
✓ Branch 11 → 12 taken 767 times.
✓ Branch 11 → 13 taken 2 times.
✓ Branch 14 → 15 taken 769 times.
✗ Branch 14 → 16 not taken.
✓ Branch 16 → 17 taken 769 times.
✗ Branch 16 → 20 not taken.
✓ Branch 18 → 19 taken 3845 times.
✓ Branch 18 → 20 taken 769 times.
✓ Branch 20 → 21 taken 767 times.
✓ Branch 20 → 22 taken 2 times.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 34 not taken.
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 34 not taken.
4614 if (!QStringList{"priority", "due", "title", "created", "manual"}.contains(sort) || sort == m_options.sort) return;
235 2 m_options.sort = sort; emit stateChanged(); project();
236 769 }
237 ✗ void QuickTaskModel::setHideFutureStarts(bool hide) {
238 ✗ if (hide == m_options.hideFutureStarts) return;
239 ✗ m_options.hideFutureStarts = hide; emit stateChanged(); project();
240 }
241 4 void QuickTaskModel::setWeekStart(int day) {
242
3/9
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 4 times.
✗ Branch 3 → 4 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 6 → 7 taken 4 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 4 times.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
4 day = day == 0 ? QLocale().firstDayOfWeek() : qBound(1, day, 7);
243
3/3
✓ Branch 10 → 11 taken 3 times.
✓ Branch 10 → 12 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
4 if (day == m_options.weekStart) return; m_options.weekStart = day; emit stateChanged(); applyTasks(m_loaded);
244 }
245 2 void QuickTaskModel::toggleParent(const QString &key) {
246
3/3
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
2 if (m_options.collapsedParents.contains(key)) m_options.collapsedParents.remove(key); else m_options.collapsedParents.insert(key);
247
5/6
✓ Branch 10 → 11 taken 2 times.
✗ Branch 10 → 19 not taken.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 15 → 16 taken 2 times.
✓ Branch 16 → 17 taken 2 times.
2 emit stateChanged(); project(); const int row = indexOfTask(key); if (row >= 0) emit dataChanged(index(row, 0), index(row, 0), {Expanded});
248 2 }
249 19 void QuickTaskModel::holdCompleted(const QString &key) {
250
1/1
✓ Branch 2 → 3 taken 19 times.
19 m_options.graceKeys.insert(key);
251 // S106-352: only the newest hold of a key ends its grace — an earlier
252 // timer (complete → reopen → complete) no longer cuts the second short.
253 19 const quint64 hold = ++m_graceHolds[key];
254
1/3
✓ Branch 5 → 6 taken 19 times.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 13 not taken.
19 QTimer::singleShot(3000, this, [this, key, hold] {
255 ✗ if (m_graceHolds.value(key) != hold) return;
256 ✗ m_graceHolds.remove(key);
257 ✗ m_options.graceKeys.remove(key);
258 ✗ project();
259 });
260 19 }
261 315 QVariantMap QuickTaskModel::state() const {
262 315 return {{"editorWidth", m_editorWidth}, {"view", view()}, {"grouping", grouping()}, {"sort", sort()}, {"showCompleted", m_showCompleted},
263 315 {"collapsedGroups", m_collapsedGroups}, {"collapsedParents", QStringList(m_options.collapsedParents.values())},
264
2/4
✓ Branch 18 → 19 taken 3150 times.
✓ Branch 18 → 20 taken 315 times.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
3780 {"hideFutureStarts", m_options.hideFutureStarts}, {"filter", m_filterText}, {"tagsExpanded", m_tagsExpanded}};
265 315 }
266 767 void QuickTaskModel::restoreState(const QVariantMap &state) {
267
4/4
✓ Branch 3 → 4 taken 767 times.
✓ Branch 4 → 5 taken 767 times.
✓ Branch 5 → 6 taken 767 times.
✓ Branch 6 → 7 taken 767 times.
767 setEditorWidth(state.value("editorWidth", 460).toDouble());
268
10/10
✓ Branch 10 → 11 taken 767 times.
✓ Branch 11 → 12 taken 767 times.
✓ Branch 12 → 13 taken 767 times.
✓ Branch 13 → 14 taken 767 times.
✓ Branch 14 → 15 taken 767 times.
✓ Branch 19 → 20 taken 767 times.
✓ Branch 20 → 21 taken 767 times.
✓ Branch 21 → 22 taken 767 times.
✓ Branch 22 → 23 taken 767 times.
✓ Branch 23 → 24 taken 767 times.
767 setView(state.value("view", "mine").toString()); setGrouping(state.value("grouping", "due").toString());
269
9/9
✓ Branch 28 → 29 taken 767 times.
✓ Branch 29 → 30 taken 767 times.
✓ Branch 30 → 31 taken 767 times.
✓ Branch 31 → 32 taken 767 times.
✓ Branch 32 → 33 taken 767 times.
✓ Branch 38 → 39 taken 767 times.
✓ Branch 39 → 40 taken 767 times.
✓ Branch 40 → 41 taken 767 times.
✓ Branch 41 → 42 taken 767 times.
767 setSort(state.value("sort", "priority").toString()); setShowCompleted(state.value("showCompleted").toBool());
270
3/3
✓ Branch 46 → 47 taken 767 times.
✓ Branch 47 → 48 taken 767 times.
✓ Branch 48 → 49 taken 767 times.
767 m_options.hideFutureStarts = state.value("hideFutureStarts").toBool();
271 // F94-54/F94-55: the filter and the tag section survive restarts.
272
6/6
✓ Branch 53 → 54 taken 767 times.
✓ Branch 54 → 55 taken 767 times.
✓ Branch 55 → 56 taken 767 times.
✓ Branch 56 → 57 taken 767 times.
✓ Branch 57 → 58 taken 767 times.
✓ Branch 65 → 66 taken 767 times.
767 m_filterText = state.value("filter").toString().left(256).trimmed(); emit filterTextChanged();
273
4/4
✓ Branch 67 → 68 taken 767 times.
✓ Branch 68 → 69 taken 767 times.
✓ Branch 69 → 70 taken 767 times.
✓ Branch 70 → 71 taken 767 times.
767 setTagsExpanded(state.value("tagsExpanded").toBool());
274
3/3
✓ Branch 75 → 76 taken 767 times.
✓ Branch 76 → 77 taken 767 times.
✓ Branch 77 → 78 taken 767 times.
767 m_collapsedGroups = state.value("collapsedGroups", m_collapsedGroups).toStringList();
275
5/5
✓ Branch 84 → 85 taken 767 times.
✓ Branch 85 → 86 taken 767 times.
✓ Branch 86 → 87 taken 767 times.
✓ Branch 92 → 93 taken 767 times.
✓ Branch 95 → 96 taken 767 times.
767 const auto collapsed = state.value("collapsedParents").toStringList(); m_options.collapsedParents = QSet<QString>(collapsed.begin(), collapsed.end()); project();
276 767 }
277 835 void QuickTaskModel::updateCalendarPaths(const QList<CalendarTask> &tasks) {
278 835 QMap<QString, QString> calendars;
279
3/3
✓ Branch 5 → 6 taken 123293 times.
✓ Branch 9 → 4 taken 123293 times.
✓ Branch 9 → 10 taken 835 times.
124128 for (const auto &task : tasks) calendars[task.calendarPath] = task.calendarDisplayName;
280
2/2
✓ Branch 10 → 11 taken 835 times.
✓ Branch 11 → 12 taken 835 times.
835 const auto paths = calendars.keys(), names = calendars.values();
281
4/4
✓ Branch 12 → 13 taken 835 times.
✓ Branch 13 → 14 taken 257 times.
✓ Branch 13 → 16 taken 578 times.
✓ Branch 15 → 16 taken 257 times.
835 if (paths != m_taskCalendarPaths) { m_taskCalendarPaths = paths; emit taskCalendarPathsChanged(); }
282
4/4
✓ Branch 16 → 17 taken 835 times.
✓ Branch 17 → 18 taken 257 times.
✓ Branch 17 → 20 taken 578 times.
✓ Branch 19 → 20 taken 257 times.
835 if (names != m_taskCalendarNames) { m_taskCalendarNames = names; emit taskCalendarNamesChanged(); }
283 835 }
284
285 3 void QuickTaskModel::toggleGroup(const QString &group) {
286
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 2 times.
3 if (m_collapsedGroups.contains(group)) m_collapsedGroups.removeAll(group); else m_collapsedGroups << group;
287 3 emit stateChanged();
288 3 }
289
290
2/3
✓ Branch 2 → 3 taken 767 times.
✓ Branch 3 → 4 taken 767 times.
✗ Branch 3 → 5 not taken.
767 void QuickTaskModel::setEditorWidth(double width) { width = qBound(360.0, width, 640.0); if (width == m_editorWidth) return; m_editorWidth = width; emit stateChanged(); }
291
292 768 void QuickTaskModel::setTagsExpanded(bool expanded) {
293
2/2
✓ Branch 2 → 3 taken 766 times.
✓ Branch 2 → 4 taken 2 times.
768 if (expanded == m_tagsExpanded) return;
294 2 m_tagsExpanded = expanded;
295 2 emit stateChanged();
296 }
297