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: 45.9% 39 / 0 / 85
Functions: 40.0% 2 / 0 / 5
Branches: 19.5% 55 / 0 / 282

presentation/QuickCalendarIcsTasks.cpp
Line Branch Exec Source
1 // S94.5.9: the task half of QuickCalendarIcs (import target, export of a
2 // task or a list). Split from QuickCalendarIcs.cpp for the Sprint 93 unit
3 // size limit, like CalendarOutboxTasks.cpp.
4 #include "QuickCalendarIcs.h"
5
6 #include "app/core/CalendarSyncCoordinator.h"
7 #include "app/core/MailSessionCore.h"
8 #include "data/CalendarStore.h"
9 #include "data/ICalTodoCodec.h"
10
11 #include <QFileInfo>
12 #include <QStandardPaths>
13
14 1 bool QuickCalendarIcs::resolveTaskList(const QString &key, QString *accountId, QString *path) const {
15
2/3
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
1 const QString lastKey = m_deps.lastTaskList ? m_deps.lastTaskList() : QString();
16 1 QVariantMap chosen, remembered, first;
17
5/5
✓ Branch 6 → 7 taken 1 time.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 74 → 10 taken 1 time.
✓ Branch 74 → 75 taken 1 time.
2 for (const QVariant &value : m_deps.calendarChoices()) {
18
1/1
✓ Branch 11 → 12 taken 1 time.
1 const QVariantMap choice = value.toMap();
19
15/42
✓ Branch 16 → 17 taken 1 time.
✓ Branch 17 → 18 taken 1 time.
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 26 not taken.
✓ Branch 23 → 24 taken 1 time.
✓ Branch 24 → 25 taken 1 time.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 1 time.
✓ Branch 28 → 29 taken 1 time.
✗ Branch 28 → 30 not taken.
✓ Branch 30 → 31 taken 1 time.
✗ Branch 30 → 32 not taken.
✓ Branch 32 → 33 taken 1 time.
✗ Branch 32 → 34 not taken.
✓ Branch 34 → 35 taken 1 time.
✗ Branch 34 → 36 not taken.
✓ Branch 36 → 37 taken 1 time.
✗ Branch 36 → 38 not taken.
✓ Branch 38 → 39 taken 1 time.
✗ Branch 38 → 40 not taken.
✓ Branch 40 → 41 taken 1 time.
✗ Branch 40 → 42 not taken.
✓ Branch 42 → 43 taken 1 time.
✗ Branch 42 → 44 not taken.
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 46 taken 1 time.
✗ Branch 120 → 121 not taken.
✗ Branch 120 → 122 not taken.
✗ Branch 124 → 125 not taken.
✗ Branch 124 → 126 not taken.
✗ Branch 128 → 129 not taken.
✗ Branch 128 → 130 not taken.
✗ Branch 132 → 133 not taken.
✗ Branch 132 → 134 not taken.
✗ Branch 136 → 137 not taken.
✗ Branch 136 → 138 not taken.
✗ Branch 140 → 141 not taken.
✗ Branch 140 → 142 not taken.
✗ Branch 144 → 145 not taken.
✗ Branch 144 → 146 not taken.
✗ Branch 148 → 149 not taken.
✗ Branch 148 → 150 not taken.
3 if (choice.value(QStringLiteral("readOnly")).toBool() || !choice.value(QStringLiteral("supportsTasks")).toBool()) continue;
20
2/2
✓ Branch 50 → 51 taken 1 time.
✓ Branch 51 → 52 taken 1 time.
2 const QString candidate = choice.value(QStringLiteral("key")).toString();
21
1/2
✓ Branch 57 → 58 taken 1 time.
✗ Branch 57 → 59 not taken.
1 if (candidate == key) chosen = choice;
22
1/2
✗ Branch 60 → 61 not taken.
✓ Branch 60 → 62 taken 1 time.
1 if (candidate == lastKey) remembered = choice;
23
2/3
✓ Branch 62 → 63 taken 1 time.
✓ Branch 63 → 64 taken 1 time.
✗ Branch 63 → 65 not taken.
1 if (first.isEmpty()) first = choice;
24 2 }
25
2/6
✓ Branch 76 → 77 taken 1 time.
✓ Branch 77 → 78 taken 1 time.
✗ Branch 77 → 79 not taken.
✗ Branch 79 → 80 not taken.
✗ Branch 80 → 81 not taken.
✗ Branch 80 → 82 not taken.
1 const QVariantMap target = !chosen.isEmpty() ? chosen : !remembered.isEmpty() ? remembered : first;
26
2/3
✓ Branch 85 → 86 taken 1 time.
✗ Branch 86 → 87 not taken.
✓ Branch 86 → 88 taken 1 time.
1 if (target.isEmpty()) return false;
27
2/2
✓ Branch 92 → 93 taken 1 time.
✓ Branch 93 → 94 taken 1 time.
2 *accountId = target.value(QStringLiteral("accountId")).toString();
28
2/2
✓ Branch 104 → 105 taken 1 time.
✓ Branch 105 → 106 taken 1 time.
2 *path = target.value(QStringLiteral("path")).toString();
29 1 return true;
30 1 }
31
32 7 int QuickCalendarIcs::importTodos(const QString &calendarKey, bool updateExisting, int *updated, int *skipped, int *failed) {
33 7 auto *sync = m_session.calendarSync();
34
2/2
✓ Branch 4 → 5 taken 6 times.
✓ Branch 4 → 6 taken 1 time.
7 if (!m_todoChecked.contains(true)) return 0;
35 1 QString accountId, path;
36
2/3
✓ Branch 6 → 7 taken 1 time.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 10 taken 1 time.
1 if (!resolveTaskList(calendarKey, &accountId, &path)) {
37 ✗ *failed += int(m_todoChecked.count(true));
38 ✗ return 0;
39 }
40 1 int imported = 0;
41
2/2
✓ Branch 54 → 11 taken 1 time.
✓ Branch 54 → 55 taken 1 time.
2 for (int i = 0; i < m_todos.size(); ++i) {
42
2/3
✓ Branch 11 → 12 taken 1 time.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 1 time.
1 if (!m_todoChecked.value(i)) continue;
43 1 ICalTodoCodec::Resource resource;
44
4/7
✓ Branch 15 → 16 taken 1 time.
✓ Branch 16 → 17 taken 1 time.
✗ Branch 16 → 19 not taken.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 1 time.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 1 time.
1 if (!ICalTodoCodec::parseResource(m_todos.at(i).resource, &resource) || resource.tasks.isEmpty()) {
45 ✗ ++*failed;
46 ✗ continue;
47 }
48
1/1
✓ Branch 23 → 24 taken 1 time.
1 CalendarTask task = resource.tasks.first();
49
1/1
✓ Branch 26 → 27 taken 1 time.
1 const bool exists = sync->store()->taskFor(task.uid, accountId, path).has_value();
50
1/4
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 32 taken 1 time.
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 32 not taken.
1 if (exists && !updateExisting) {
51 ✗ ++*skipped;
52 ✗ continue;
53 }
54 1 task.accountId = accountId;
55 1 task.calendarPath = path;
56 1 task.etag.clear();
57 1 task.resourceHref.clear();
58
2/3
✓ Branch 36 → 37 taken 1 time.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 1 time.
1 if (!sync->saveTask(task, !exists)) {
59 ✗ ++*failed;
60 ✗ continue;
61 }
62
1/2
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 41 taken 1 time.
1 ++(exists ? *updated : imported);
63 1 }
64 1 return imported;
65 1 }
66
67 ✗ QString QuickCalendarIcs::saveExport(const QString &title, const QByteArray &bytes) {
68 ✗ const QString directory = m_deps.downloadDirectory.isEmpty()
69 ✗ ? QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) : m_deps.downloadDirectory;
70 ✗ return writeFile(directory, IcsFile::fileName(title), bytes, true);
71 ✗ }
72
73 ✗ bool QuickCalendarIcs::exportTask(const QVariantMap &identity) {
74 ✗ auto *sync = m_session.calendarSync();
75 ✗ if (!sync || !sync->store()) return false;
76 ✗ const auto task = sync->store()->taskFor(identity.value(QStringLiteral("uid")).toString(),
77 ✗ identity.value(QStringLiteral("accountId")).toString(),
78 ✗ identity.value(QStringLiteral("calendarPath")).toString());
79 ✗ if (!task) return false;
80 ✗ const QString resource = task->resourceIcal.isEmpty() ? QString::fromUtf8(ICalTodoCodec::buildResource(*task)) : task->resourceIcal;
81 ✗ const QString path = saveExport(task->summary, IcsFile::writeTodos({resource}));
82 ✗ m_deps.setStatus(path.isEmpty() ? tr("The calendar file could not be saved.") : tr("Saved %1.").arg(QFileInfo(path).fileName()));
83 ✗ return !path.isEmpty();
84 ✗ }
85
86 ✗ bool QuickCalendarIcs::exportTaskList(const QString &accountId, const QString &calendarPath) {
87 ✗ auto *sync = m_session.calendarSync();
88 ✗ if (!sync || !sync->store()) return false;
89 ✗ QStringList resources;
90 ✗ for (const CalendarTask &listed : sync->store()->tasksForCalendar(accountId, calendarPath)) {
91 ✗ const auto task = sync->store()->taskFor(listed.uid, accountId, calendarPath);
92 ✗ if (!task) continue;
93 ✗ resources << (task->resourceIcal.isEmpty() ? QString::fromUtf8(ICalTodoCodec::buildResource(*task)) : task->resourceIcal);
94 ✗ }
95 ✗ QString name = calendarPath.section(QLatin1Char('/'), -2, -1, QString::SectionSkipEmpty);
96 ✗ for (const QVariant &value : m_deps.calendarChoices()) {
97 ✗ const QVariantMap choice = value.toMap();
98 ✗ if (choice.value(QStringLiteral("accountId")) == accountId && choice.value(QStringLiteral("path")) == calendarPath)
99 ✗ name = choice.value(QStringLiteral("displayName")).toString();
100 ✗ }
101 ✗ const QString path = saveExport(name, IcsFile::writeTodos(resources));
102 ✗ m_deps.setStatus(path.isEmpty() ? tr("The calendar file could not be saved.")
103 ✗ : resources.size() == 1 ? tr("Saved %1 with 1 task.").arg(QFileInfo(path).fileName())
104 ✗ : tr("Saved %1 with %2 tasks.").arg(QFileInfo(path).fileName()).arg(resources.size()));
105 ✗ return !path.isEmpty();
106 ✗ }
107