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: 38.9% 37 / 0 / 95
Functions: 44.4% 4 / 0 / 9
Branches: 19.3% 65 / 0 / 336

presentation/QuickTaskMenu.cpp
Line Branch Exec Source
1 #include "QuickTaskMenu.h"
2
3 #include "util/MenuText.h"
4
5 #include <QApplication>
6 #include <QColor>
7 #include <QIcon>
8 #include <QMenu>
9 #include <QPainter>
10 #include <QPixmap>
11 #include <QWindow>
12
13 namespace {
14 // A list's colour as a menu icon (F94-35): a filled dot like the sidebar's.
15 ✗ QIcon colorDot(const QString &color) {
16 ✗ QPixmap pixmap(32, 32);
17 ✗ pixmap.fill(Qt::transparent);
18 ✗ QPainter painter(&pixmap);
19 ✗ painter.setRenderHint(QPainter::Antialiasing);
20 ✗ painter.setPen(Qt::NoPen);
21 ✗ painter.setBrush(QColor(color));
22 ✗ painter.drawEllipse(QRectF(8, 8, 16, 16));
23 ✗ return QIcon(pixmap);
24 ✗ }
25 ✗ QIcon iconFor(const QVariantMap &entry) {
26 ✗ const QString color = entry.value("color").toString();
27 ✗ if (!color.isEmpty() && QColor::isValidColorName(color)) return colorDot(color);
28 ✗ return QIcon::fromTheme(entry.value("icon").toString());
29 ✗ }
30 1 QMenu *nativeMenu(const QString &name) {
31
2/5
✓ Branch 3 → 4 taken 1 time.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
1 auto *menu = new QMenu;
32 1 menu->setObjectName(name);
33 1 menu->setAttribute(Qt::WA_DeleteOnClose);
34 1 return menu;
35 }
36 1 void popupAt(QMenu *menu, QWindow *parent, qreal x, qreal y) {
37 1 menu->winId();
38
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 6 not taken.
1 if (auto *handle = menu->windowHandle()) handle->setTransientParent(parent);
39
2/2
✓ Branch 7 → 8 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
1 menu->popup(parent->mapToGlobal(QPointF(x, y)).toPoint());
40 1 }
41 }
42
43 1 bool QuickTaskMenu::show(const QVariantMap &task, const QVariantList &lists, QObject *window, qreal x, qreal y, const QString &section) {
44 1 auto *parent = qobject_cast<QWindow *>(window);
45
4/7
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 7 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1 time.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 1 time.
1 if (!parent || !qobject_cast<QApplication *>(QCoreApplication::instance())) return false;
46
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 12 → 13 taken 1 time.
1 auto *menu = nativeMenu("nativeTaskMenu");
47
3/3
✓ Branch 15 → 16 taken 1 time.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 17 → 18 taken 1 time.
1 const bool writable = !task.value("readOnly").toBool();
48 5 const auto add = [this](QMenu *target, const QString &icon, const QString &label, const QString &action, const QVariantMap &fields = {}) {
49 // S106-280: list and account names are shown as written.
50
3/3
✓ Branch 2 → 3 taken 5 times.
✓ Branch 3 → 4 taken 5 times.
✓ Branch 4 → 5 taken 5 times.
5 auto *entry = target->addAction(QIcon::fromTheme(icon), MenuText::plain(label));
51
2/2
✓ Branch 7 → 8 taken 5 times.
✓ Branch 8 → 9 taken 5 times.
5 entry->setObjectName("taskMenu-" + action);
52
1/3
✓ Branch 12 → 13 taken 5 times.
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 30 not taken.
5 connect(entry, &QAction::triggered, this, [this, action, fields] { emit actionRequested(action, fields); });
53 5 return entry;
54 1 };
55
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 60 taken 1 time.
1 if (section.isEmpty()) {
56 ✗ add(menu, writable ? "document-edit" : "document-open", writable ? tr("Edit") : tr("Open"), "edit");
57 ✗ if (writable) {
58 ✗ const bool completed = task.value("completed").toBool();
59 ✗ add(menu, completed ? "edit-undo" : "checkmark", completed ? tr("Reopen") : tr("Complete"), "toggle");
60 }
61 }
62
4/8
✓ Branch 60 → 61 taken 1 time.
✗ Branch 60 → 66 not taken.
✓ Branch 62 → 63 taken 1 time.
✗ Branch 62 → 65 not taken.
✓ Branch 64 → 65 taken 1 time.
✗ Branch 64 → 66 not taken.
✓ Branch 67 → 68 taken 1 time.
✗ Branch 67 → 141 not taken.
1 if (writable && (section.isEmpty() || section == "due")) {
63 1 QMenu *due = menu;
64
1/6
✗ Branch 69 → 70 not taken.
✓ Branch 69 → 78 taken 1 time.
✗ Branch 70 → 71 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 72 → 73 not taken.
✗ Branch 73 → 74 not taken.
1 if (section != "due") { due = menu->addMenu(QIcon::fromTheme("view-calendar-day"), tr("Due")); }
65
6/8
✓ Branch 80 → 81 taken 1 time.
✓ Branch 81 → 82 taken 1 time.
✓ Branch 82 → 83 taken 1 time.
✓ Branch 83 → 84 taken 1 time.
✓ Branch 89 → 90 taken 1 time.
✓ Branch 89 → 91 taken 1 time.
✗ Branch 472 → 473 not taken.
✗ Branch 472 → 474 not taken.
2 add(due, "go-jump-today", tr("Today"), "due", {{"due", "today"}});
66
6/8
✓ Branch 93 → 94 taken 1 time.
✓ Branch 94 → 95 taken 1 time.
✓ Branch 95 → 96 taken 1 time.
✓ Branch 96 → 97 taken 1 time.
✓ Branch 102 → 103 taken 1 time.
✓ Branch 102 → 104 taken 1 time.
✗ Branch 494 → 495 not taken.
✗ Branch 494 → 496 not taken.
2 add(due, "go-next", tr("Tomorrow"), "due", {{"due", "tomorrow"}});
67
6/8
✓ Branch 106 → 107 taken 1 time.
✓ Branch 107 → 108 taken 1 time.
✓ Branch 108 → 109 taken 1 time.
✓ Branch 109 → 110 taken 1 time.
✓ Branch 115 → 116 taken 1 time.
✓ Branch 115 → 117 taken 1 time.
✗ Branch 516 → 517 not taken.
✗ Branch 516 → 518 not taken.
2 add(due, "view-calendar-week", tr("Next week"), "due", {{"due", "nextweek"}});
68
4/4
✓ Branch 118 → 119 taken 1 time.
✓ Branch 119 → 120 taken 1 time.
✓ Branch 120 → 121 taken 1 time.
✓ Branch 121 → 122 taken 1 time.
1 add(due, "view-calendar", tr("Pick date…"), "pick");
69
1/1
✓ Branch 126 → 127 taken 1 time.
1 due->addSeparator();
70
6/8
✓ Branch 129 → 130 taken 1 time.
✓ Branch 130 → 131 taken 1 time.
✓ Branch 131 → 132 taken 1 time.
✓ Branch 132 → 133 taken 1 time.
✓ Branch 138 → 139 taken 1 time.
✓ Branch 138 → 140 taken 1 time.
✗ Branch 550 → 551 not taken.
✗ Branch 550 → 552 not taken.
2 add(due, "edit-clear", tr("No date"), "due", {{"due", ""}});
71 }
72
4/8
✓ Branch 141 → 142 taken 1 time.
✗ Branch 141 → 147 not taken.
✓ Branch 143 → 144 taken 1 time.
✗ Branch 143 → 146 not taken.
✗ Branch 145 → 146 not taken.
✓ Branch 145 → 147 taken 1 time.
✗ Branch 148 → 149 not taken.
✓ Branch 148 → 212 taken 1 time.
1 if (writable && (section.isEmpty() || section == "priority")) {
73 ✗ QMenu *priority = menu;
74 ✗ if (section != "priority") priority = menu->addMenu(QIcon::fromTheme("flag"), tr("Priority"));
75 ✗ add(priority, "arrow-up", tr("High"), "priority", {{"priority", 1}});
76 ✗ add(priority, "arrow-right", tr("Normal"), "priority", {{"priority", 5}});
77 ✗ add(priority, "arrow-down", tr("Low"), "priority", {{"priority", 9}});
78 ✗ add(priority, "edit-clear", tr("None"), "priority", {{"priority", 0}});
79 }
80
4/8
✓ Branch 212 → 213 taken 1 time.
✗ Branch 212 → 218 not taken.
✓ Branch 214 → 215 taken 1 time.
✗ Branch 214 → 217 not taken.
✗ Branch 216 → 217 not taken.
✓ Branch 216 → 218 taken 1 time.
✗ Branch 219 → 220 not taken.
✓ Branch 219 → 329 taken 1 time.
1 if (writable && (section.isEmpty() || section == "move")) {
81 ✗ QMenu *move = menu;
82 ✗ if (section != "move") move = menu->addMenu(QIcon::fromTheme("go-jump"), tr("Move to list"));
83 ✗ for (const auto &value : lists) {
84 ✗ const auto list = value.toMap();
85 ✗ if (list.value("readOnly").toBool() || !list.value("supportsTasks").toBool()) continue;
86 ✗ auto *entry = add(move, {}, list.value("displayName").toString() + " · " + list.value("accountName").toString(), "move",
87 ✗ {{"accountId", list.value("accountId")}, {"calendarPath", list.value("path")}});
88 ✗ entry->setIcon(iconFor({{"color", list.value("color")}}));
89 ✗ }
90 }
91
1/2
✗ Branch 330 → 331 not taken.
✓ Branch 330 → 402 taken 1 time.
1 if (section.isEmpty()) {
92 ✗ menu->addSeparator();
93 ✗ if (writable) add(menu, "list-add", tr("Add subtask"), "subtask");
94 ✗ if (!task.value("relatedMessageId").toString().isEmpty()) add(menu, "mail-message", tr("Open linked mail"), "mail");
95 ✗ add(menu, "appointment-new", tr("Create appointment from task"), "appointment");
96 ✗ add(menu, "edit-copy", tr("Copy as text"), "copy");
97 ✗ add(menu, "document-export", tr("Export as .ics"), "export");
98 ✗ if (writable) { menu->addSeparator(); add(menu, "edit-delete", tr("Delete"), "delete"); }
99 }
100
1/1
✓ Branch 402 → 403 taken 1 time.
1 popupAt(menu, parent, x, y);
101 1 return true;
102 4 }
103
104 ✗ bool QuickTaskMenu::showChoices(const QString &requester, const QVariantList &entries, QObject *window, qreal x, qreal y) {
105 ✗ auto *parent = qobject_cast<QWindow *>(window);
106 ✗ if (!parent || !qobject_cast<QApplication *>(QCoreApplication::instance())) return false;
107 ✗ auto *menu = nativeMenu("nativeTaskChoices-" + requester);
108 ✗ for (const auto &value : entries) {
109 ✗ const auto entry = value.toMap();
110 ✗ if (entry.value("separator").toBool()) { menu->addSeparator(); continue; }
111 ✗ auto *action = menu->addAction(iconFor(entry), MenuText::plain(entry.value("label").toString()));
112 ✗ const QString id = entry.value("id").toString();
113 ✗ action->setObjectName("taskChoice-" + id);
114 ✗ action->setCheckable(entry.value("checkable").toBool());
115 ✗ action->setChecked(entry.value("checked").toBool());
116 // F100-45: single choices without check marks open on their current value.
117 ✗ if (entry.value("current").toBool()) menu->setActiveAction(action);
118 ✗ connect(action, &QAction::triggered, this, [this, requester, id] { emit choiceTriggered(requester, id); });
119 ✗ }
120 ✗ popupAt(menu, parent, x, y);
121 ✗ return true;
122 }
123