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: 84.4% 54 / 0 / 64
Functions: 100.0% 6 / 0 / 6
Branches: 61.1% 80 / 0 / 131

presentation/QuickContactActionsPhoto.cpp
Line Branch Exec Source
1 // Photos of QuickContactActions (Sprint 103 S103.3.6, D-20, interaction.md
2 // §3.8–§3.9): an image from a file (chosen or dropped) or the clipboard is
3 // held as the photo draft, shown to the framing step as a preview, and the
4 // framed square becomes the card photo with the Sprint 97 limits (256 px
5 // JPEG ≤ 64 KiB). Nothing is fetched from the network: a dropped web image
6 // arrives as a file or as image data, never as a URL to load.
7
8 #include "QuickContactActions.h"
9
10 #include "ContactPhotoProvider.h"
11
12 #include <QBuffer>
13 #include <QClipboard>
14 #include <QFile>
15 #include <QGuiApplication>
16 #include <QImage>
17 #include <QLoggingCategory>
18 #include <QMimeData>
19 #include <QUrl>
20
21 Q_DECLARE_LOGGING_CATEGORY(lcContactRelationActions)
22
23 namespace {
24
25 constexpr qint64 MaxPhotoBytes = 20 * 1024 * 1024; // interaction.md §3.8
26
27
3/9
✓ Branch 10 → 11 taken 3 times.
✓ Branch 12 → 13 taken 6 times.
✓ Branch 12 → 14 taken 3 times.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 41 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
15 QVariantMap failure(const QString &error) { return {{QStringLiteral("ok"), false}, {QStringLiteral("error"), error}}; }
28
29 } // namespace
30
31 3 QVariantMap QuickContactActions::holdPhoto(const QByteArray &bytes) {
32
3/6
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 6 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 3 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 14 taken 3 times.
3 if (bytes.isEmpty() || bytes.size() > MaxPhotoBytes)
33 ✗ return failure(tr("This image cannot be used."));
34 3 QString error;
35
1/1
✓ Branch 14 → 15 taken 3 times.
3 const QImage preview = ContactPhotoProvider::preview(bytes, 1024, &error);
36
2/3
✓ Branch 15 → 16 taken 3 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 22 taken 3 times.
3 if (preview.isNull())
37 ✗ return failure(tr("This image cannot be used."));
38 3 m_photoDraft = bytes;
39 3 QByteArray jpeg;
40
1/1
✓ Branch 23 → 24 taken 3 times.
3 QBuffer out(&jpeg);
41
1/1
✓ Branch 25 → 26 taken 3 times.
3 out.open(QIODevice::WriteOnly);
42
2/2
✓ Branch 27 → 28 taken 3 times.
✓ Branch 28 → 29 taken 3 times.
3 preview.convertToFormat(QImage::Format_RGB32).save(&out, "JPEG", 85);
43
9/9
✓ Branch 30 → 31 taken 3 times.
✓ Branch 34 → 35 taken 3 times.
✓ Branch 35 → 36 taken 3 times.
✓ Branch 37 → 38 taken 3 times.
✓ Branch 38 → 39 taken 3 times.
✓ Branch 40 → 41 taken 3 times.
✓ Branch 41 → 42 taken 3 times.
✓ Branch 47 → 32 taken 3 times.
✓ Branch 47 → 48 taken 3 times.
6 qCInfo(lcContactRelationActions) << "Photo draft held:" << bytes.size() << "bytes," << preview.size();
44 6 return {{QStringLiteral("ok"), true},
45
2/2
✓ Branch 56 → 57 taken 3 times.
✓ Branch 57 → 58 taken 3 times.
9 {QStringLiteral("preview"), QString::fromLatin1(jpeg.toBase64())},
46
1/1
✓ Branch 62 → 63 taken 3 times.
6 {QStringLiteral("width"), preview.width()},
47
3/5
✓ Branch 67 → 68 taken 3 times.
✓ Branch 71 → 72 taken 12 times.
✓ Branch 71 → 73 taken 3 times.
✗ Branch 111 → 112 not taken.
✗ Branch 111 → 113 not taken.
18 {QStringLiteral("height"), preview.height()}};
48 21 }
49
50 3 QVariantMap QuickContactActions::loadPhoto(const QUrl &file) {
51
3/3
✓ Branch 2 → 3 taken 3 times.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 9 taken 2 times.
3 if (!file.isLocalFile())
52
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
1 return failure(tr("This image cannot be used."));
53
2/2
✓ Branch 9 → 10 taken 2 times.
✓ Branch 10 → 11 taken 2 times.
2 QFile input(file.toLocalFile());
54
7/8
✓ Branch 12 → 13 taken 2 times.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 17 taken 1 time.
✓ Branch 15 → 16 taken 1 time.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 1 time.
✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 25 taken 1 time.
2 if (input.size() > MaxPhotoBytes || !input.open(QIODevice::ReadOnly))
55
2/2
✓ Branch 20 → 21 taken 1 time.
✓ Branch 21 → 22 taken 1 time.
1 return failure(tr("This image cannot be used."));
56
2/2
✓ Branch 25 → 26 taken 1 time.
✓ Branch 26 → 27 taken 1 time.
1 return holdPhoto(input.readAll());
57 2 }
58
59 82 bool QuickContactActions::clipboardHasImage() const {
60
1/2
✓ Branch 3 → 4 taken 82 times.
✗ Branch 3 → 7 not taken.
82 const QMimeData *data = QGuiApplication::clipboard() ? QGuiApplication::clipboard()->mimeData() : nullptr;
61
2/2
✓ Branch 8 → 9 taken 54 times.
✓ Branch 8 → 10 taken 28 times.
82 if (!data)
62 54 return false;
63
2/2
✓ Branch 11 → 12 taken 26 times.
✓ Branch 11 → 13 taken 2 times.
28 if (data->hasImage())
64 26 return true;
65
4/5
✓ Branch 13 → 14 taken 2 times.
✓ Branch 14 → 15 taken 2 times.
✓ Branch 15 → 16 taken 2 times.
✗ Branch 23 → 17 not taken.
✓ Branch 23 → 24 taken 2 times.
2 for (const QUrl &url : data->urls())
66 ✗ if (url.isLocalFile())
67
1/2
✓ Branch 26 → 27 taken 2 times.
✗ Branch 26 → 29 not taken.
2 return true;
68 2 return false;
69 }
70
71 2 QVariantMap QuickContactActions::loadPhotoFromClipboard() {
72
1/2
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 7 not taken.
2 const QMimeData *data = QGuiApplication::clipboard() ? QGuiApplication::clipboard()->mimeData() : nullptr;
73
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 14 taken 2 times.
2 if (!data)
74 ✗ return failure(tr("This image cannot be used."));
75
1/2
✓ Branch 15 → 16 taken 2 times.
✗ Branch 15 → 40 not taken.
2 if (data->hasImage()) {
76
2/2
✓ Branch 16 → 17 taken 2 times.
✓ Branch 17 → 18 taken 2 times.
2 const QImage image = qvariant_cast<QImage>(data->imageData());
77 2 QByteArray png;
78
1/1
✓ Branch 19 → 20 taken 2 times.
2 QBuffer out(&png);
79
1/1
✓ Branch 21 → 22 taken 2 times.
2 out.open(QIODevice::WriteOnly);
80
5/8
✓ Branch 22 → 23 taken 2 times.
✓ Branch 23 → 24 taken 2 times.
✗ Branch 23 → 26 not taken.
✓ Branch 24 → 25 taken 2 times.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 2 times.
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 34 taken 2 times.
2 if (image.isNull() || !image.save(&out, "PNG"))
81 ✗ return failure(tr("This image cannot be used."));
82
1/1
✓ Branch 34 → 35 taken 2 times.
2 return holdPhoto(png);
83 2 }
84 ✗ for (const QUrl &url : data->urls())
85 ✗ if (url.isLocalFile())
86 ✗ return loadPhoto(url);
87 ✗ return failure(tr("This image cannot be used."));
88 }
89
90 4 QVariantMap QuickContactActions::framePhoto(qreal x, qreal y, qreal width, qreal height) {
91
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 9 taken 3 times.
4 if (m_photoDraft.isEmpty())
92
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
1 return failure(tr("This image cannot be used."));
93 3 QString error;
94
1/1
✓ Branch 10 → 11 taken 3 times.
3 const QByteArray jpeg = ContactPhotoProvider::cropForCard(m_photoDraft, QRectF(x, y, width, height), &error);
95
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 18 taken 3 times.
3 if (jpeg.isEmpty())
96 ✗ return failure(tr("This image cannot be used."));
97
1/1
✓ Branch 18 → 19 taken 3 times.
3 m_photoDraft.clear();
98
4/6
✓ Branch 27 → 28 taken 3 times.
✓ Branch 28 → 29 taken 3 times.
✓ Branch 32 → 33 taken 6 times.
✓ Branch 32 → 34 taken 3 times.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 55 not taken.
21 return {{QStringLiteral("ok"), true}, {QStringLiteral("data"), QString::fromLatin1(jpeg.toBase64())}};
99 15 }
100