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: 83.3% 60 / 0 / 72
Functions: 100.0% 6 / 0 / 6
Branches: 42.8% 62 / 0 / 145

presentation/QuickComposeBackendTemplates.cpp
Line Branch Exec Source
1 // Sprint 108 S108.9.5 (D-27): message templates — saved from a composer's
2 // new text, inserted into another (`template <name>`, Ctrl+Shift+T, the ⋯
3 // menu) and managed in Settings → Mail. The store is TemplateStore
4 // (<profile>/templates.json); this file is the QML-facing part.
5 #include "QuickComposeBackend.h"
6 #include "ComposeLog.h"
7 #include "ComposeSession.h"
8
9 namespace {
10
11 1 QVariantMap refusal(const QString &error) {
12
2/4
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 14 taken 1 time.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
6 return {{QStringLiteral("ok"), false}, {QStringLiteral("error"), error}};
13 4 }
14
15 } // namespace
16
17 32 QVariantList QuickComposeBackend::templates() const {
18 32 QVariantList out;
19
2/2
✓ Branch 41 → 5 taken 5 times.
✓ Branch 41 → 42 taken 32 times.
37 for (const MailTemplate &value : m_templateStore.templates())
20
1/1
✓ Branch 25 → 26 taken 5 times.
15 out.append(QVariantMap{{QStringLiteral("id"), value.id},
21 10 {QStringLiteral("name"), value.name},
22 10 {QStringLiteral("subject"), value.subject},
23
2/4
✓ Branch 29 → 30 taken 20 times.
✓ Branch 29 → 31 taken 5 times.
✗ Branch 49 → 50 not taken.
✗ Branch 49 → 51 not taken.
35 {QStringLiteral("formatted"), !value.html.isEmpty()}});
24 32 return out;
25 25 }
26
27 3 QVariantMap QuickComposeBackend::saveTemplate(QObject *sessionObject, const QString &name) {
28
1/1
✓ Branch 2 → 3 taken 3 times.
3 auto *session = qobject_cast<ComposeSession *>(sessionObject);
29
1/4
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 9 taken 3 times.
✗ Branch 4 → 5 not taken.
✗ Branch 5 → 6 not taken.
3 if (!session) return refusal(tr("There is no message to save as a template."));
30
1/1
✓ Branch 9 → 10 taken 3 times.
3 const QVariantMap content = session->templateContent();
31 3 MailTemplate value;
32 3 value.name = name;
33
2/2
✓ Branch 15 → 16 taken 3 times.
✓ Branch 16 → 17 taken 3 times.
6 value.subject = content.value(QStringLiteral("subject")).toString();
34
2/2
✓ Branch 27 → 28 taken 3 times.
✓ Branch 28 → 29 taken 3 times.
6 value.html = content.value(QStringLiteral("html")).toString();
35
2/2
✓ Branch 39 → 40 taken 3 times.
✓ Branch 40 → 41 taken 3 times.
6 value.text = content.value(QStringLiteral("text")).toString();
36
5/16
✓ Branch 47 → 48 taken 3 times.
✗ Branch 49 → 50 not taken.
✓ Branch 49 → 54 taken 3 times.
✗ Branch 50 → 51 not taken.
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 54 not taken.
✗ Branch 55 → 56 not taken.
✓ Branch 55 → 57 taken 3 times.
✓ Branch 57 → 58 taken 3 times.
✗ Branch 57 → 59 not taken.
✗ Branch 59 → 60 not taken.
✓ Branch 59 → 65 taken 3 times.
✗ Branch 199 → 200 not taken.
✗ Branch 199 → 201 not taken.
✗ Branch 203 → 204 not taken.
✗ Branch 203 → 205 not taken.
3 if (value.text.trimmed().isEmpty() && value.subject.trimmed().isEmpty())
37 ✗ return refusal(tr("The message is empty — write the text first."));
38 3 QString error;
39
1/1
✓ Branch 66 → 67 taken 3 times.
3 const auto saved = m_templateStore.upsert(value, &error);
40
2/2
✓ Branch 69 → 70 taken 1 time.
✓ Branch 69 → 94 taken 2 times.
3 if (!saved) {
41
1/4
✗ Branch 72 → 73 not taken.
✓ Branch 72 → 78 taken 1 time.
✗ Branch 73 → 74 not taken.
✗ Branch 74 → 75 not taken.
1 if (error == QLatin1String("size")) return refusal(tr("The text is too long for a template."));
42
1/5
✗ Branch 80 → 81 not taken.
✓ Branch 80 → 89 taken 1 time.
✗ Branch 81 → 82 not taken.
✗ Branch 83 → 84 not taken.
✗ Branch 84 → 85 not taken.
1 if (error == QLatin1String("count")) return refusal(tr("At most %1 templates.").arg(TemplateStore::MaxTemplates));
43
2/2
✓ Branch 89 → 90 taken 1 time.
✓ Branch 90 → 91 taken 1 time.
1 return refusal(tr("Give the template a name on one line."));
44 }
45
2/3
✓ Branch 94 → 95 taken 2 times.
✗ Branch 95 → 96 not taken.
✓ Branch 95 → 116 taken 2 times.
2 if (!m_templateStore.save(&error)) {
46 ✗ qCWarning(lcCompose) << "templates not saved" << error;
47 ✗ m_templateStore.load();
48 ✗ return refusal(tr("Could not save: %1").arg(error));
49 }
50
2/8
✓ Branch 116 → 117 taken 2 times.
✗ Branch 120 → 121 not taken.
✗ Branch 121 → 122 not taken.
✗ Branch 123 → 124 not taken.
✗ Branch 124 → 125 not taken.
✗ Branch 127 → 128 not taken.
✗ Branch 131 → 118 not taken.
✓ Branch 131 → 132 taken 2 times.
2 qCInfo(lcCompose) << "template saved" << saved->id << "formatted" << !saved->html.isEmpty();
51
1/1
✓ Branch 132 → 133 taken 2 times.
2 emit templatesChanged();
52
2/4
✓ Branch 144 → 145 taken 4 times.
✓ Branch 144 → 146 taken 2 times.
✗ Branch 244 → 245 not taken.
✗ Branch 244 → 246 not taken.
12 return {{QStringLiteral("ok"), true}, {QStringLiteral("id"), saved->id}};
53 11 }
54
55 4 bool QuickComposeBackend::insertTemplate(QObject *sessionObject, const QString &idOrName) {
56
1/1
✓ Branch 2 → 3 taken 4 times.
4 auto *session = qobject_cast<ComposeSession *>(sessionObject);
57
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 4 times.
4 if (!session) return false;
58
1/1
✓ Branch 5 → 6 taken 4 times.
4 auto value = m_templateStore.find(idOrName);
59
3/3
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 12 taken 2 times.
✓ Branch 8 → 9 taken 2 times.
4 if (!value) value = m_templateStore.findByName(idOrName);
60
2/2
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 25 taken 3 times.
4 if (!value) {
61
2/5
✓ Branch 14 → 15 taken 1 time.
✗ Branch 18 → 19 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 23 → 16 not taken.
✓ Branch 23 → 24 taken 1 time.
1 qCInfo(lcCompose) << "template not found";
62 1 return false;
63 }
64
3/5
✓ Branch 41 → 42 taken 3 times.
✓ Branch 45 → 46 taken 9 times.
✓ Branch 45 → 47 taken 3 times.
✗ Branch 66 → 67 not taken.
✗ Branch 66 → 68 not taken.
21 return session->insertTemplate({{QStringLiteral("subject"), value->subject},
65 6 {QStringLiteral("html"), value->html},
66 9 {QStringLiteral("text"), value->text}});
67 16 }
68
69 1 QVariantMap QuickComposeBackend::renameTemplate(const QString &id, const QString &name) {
70 1 QString error;
71
2/3
✓ Branch 2 → 3 taken 1 time.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 25 taken 1 time.
1 if (!m_templateStore.rename(id, name, &error)) {
72 ✗ if (error == QLatin1String("taken")) return refusal(tr("Another template has this name."));
73 ✗ if (error == QLatin1String("missing")) return refusal(tr("This template no longer exists."));
74 ✗ return refusal(tr("Give the template a name on one line."));
75 }
76
2/3
✓ Branch 25 → 26 taken 1 time.
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 36 taken 1 time.
1 if (!m_templateStore.save(&error)) {
77 ✗ m_templateStore.load();
78 ✗ return refusal(tr("Could not save: %1").arg(error));
79 }
80
1/1
✓ Branch 36 → 37 taken 1 time.
1 emit templatesChanged();
81
2/4
✓ Branch 47 → 48 taken 2 times.
✓ Branch 47 → 49 taken 1 time.
✗ Branch 74 → 75 not taken.
✗ Branch 74 → 76 not taken.
6 return {{QStringLiteral("ok"), true}, {QStringLiteral("id"), id}};
82 5 }
83
84 1 bool QuickComposeBackend::removeTemplate(const QString &id) {
85
2/3
✓ Branch 2 → 3 taken 1 time.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 if (!m_templateStore.remove(id)) return false;
86 1 QString error;
87
2/3
✓ Branch 5 → 6 taken 1 time.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 20 taken 1 time.
1 if (!m_templateStore.save(&error)) {
88 ✗ qCWarning(lcCompose) << "templates not saved" << error;
89 ✗ m_templateStore.load();
90 ✗ return false;
91 }
92
2/6
✓ Branch 20 → 21 taken 1 time.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 30 → 22 not taken.
✓ Branch 30 → 31 taken 1 time.
1 qCInfo(lcCompose) << "template removed" << id;
93
1/1
✓ Branch 31 → 32 taken 1 time.
1 emit templatesChanged();
94 1 return true;
95 1 }
96