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: 100.0% 4 / 0 / 4
Functions: 100.0% 4 / 0 / 4
Branches: -% 0 / 0 / 0

presentation/QuickContactVcf.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QObject>
4 #include <QStringList>
5 #include <QUrl>
6 #include <QVariantList>
7 #include <functional>
8 #include <optional>
9
10 #include "data/MailRequest.h"
11 #include "service/MailPayloadExportService.h"
12
13 class ContactSyncCoordinator;
14 class MailSessionCore;
15
16 // vCard files (Sprint 97 S97.5.8, CJ10; QML `backend.vcf`).
17 //
18 // Import: openFile() (file dialog) or openAttachment() (a .vcf in the
19 // displayed mail) read the cards and fill a preview — name, address and
20 // whether the card already exists in the target book (same UID or same
21 // address). importAll(bookId, duplicates) writes them through the contact
22 // outbox: "skip" leaves existing cards, "update" replaces the card with the
23 // same UID (a card that only shares an address is left alone), "both" adds a
24 // second card. 3.0/4.0 cards are stored as written.
25 //
26 // Export: exportKeys() / exportBook() / exportGroup() write the stored raw
27 // cards unchanged into one .vcf in the download folder.
28 // Sprint 103 S103.3.9 (D-22, interaction.md §11): when a card holds
29 // personal details (VCardExportFilter), the export waits for an answer:
30 // `question` describes it ({title, action, count}), answerQuestion(go,
31 // include) runs or drops it; without the checkbox the details are left out.
32 // "Send as vCard" then reports its file through attachmentReady().
33 class QuickContactVcf final : public QObject {
34 37 Q_OBJECT
35 Q_PROPERTY(bool active READ active NOTIFY changed)
36 Q_PROPERTY(QString sourceName READ sourceName NOTIFY changed)
37 // [{name, address, duplicate}] — duplicate is "", "uid" or "address".
38 Q_PROPERTY(QVariantList items READ items NOTIFY changed)
39 Q_PROPERTY(int duplicateCount READ duplicateCount NOTIFY changed)
40 // {title, action, count} while an export waits for the personal-details answer; {} otherwise.
41 Q_PROPERTY(QVariantMap question READ question NOTIFY questionChanged)
42 public:
43 struct Deps {
44 std::function<MailId()> currentMail;
45 std::function<void(const QString &)> setStatus;
46 QString downloadDirectory; // "" = the platform download folder
47 };
48 static constexpr qint64 MaxBytes = 16 * 1024 * 1024;
49 // S106-286 (INV-14): the preview parses every card and checks it for
50 // duplicates on the GUI thread; past this many cards the rest of the
51 // file is left out (the ICS import's IcsFile::MaxEntries rule).
52 static constexpr int MaxCards = 5000;
53 // A mail attachment is read in one payload window, which the export
54 // service limits to 8 MiB (a larger request is refused outright).
55 static constexpr qint64 AttachmentMaxBytes = MailPayloadExportService::MaximumWindowBytes;
56 QuickContactVcf(MailSessionCore &session, Deps deps, QObject *parent = nullptr);
57
58 26 bool active() const { return !m_cards.isEmpty(); }
59 775 QString sourceName() const { return m_sourceName; }
60 2327 QVariantList items() const { return m_items; }
61 int duplicateCount() const;
62 QVariantMap question() const;
63
64 Q_INVOKABLE bool openFile(const QUrl &url);
65 Q_INVOKABLE bool openAttachment(qint64 attachmentId, const QString &name);
66 bool openText(const QString &text, const QString &name);
67 // Recomputes the duplicate column for the chosen target book.
68 Q_INVOKABLE void setTargetBook(qint64 bookId);
69 // {ok, imported, updated, skipped}
70 Q_INVOKABLE QVariantMap importAll(qint64 bookId, const QString &duplicates);
71 Q_INVOKABLE void close();
72
73 Q_INVOKABLE bool exportKeys(const QStringList &keys, const QString &title);
74 Q_INVOKABLE bool exportBook(qint64 bookId);
75 Q_INVOKABLE bool exportGroup(qint64 bookId, const QString &group);
76 // S97.5.8 "Send as vCard": the cards as "<title>.vcf" in a fresh folder
77 // under the cache directory (owner-only, removed after 7 days); the path
78 // for the composer's attachment list, "" on failure.
79 Q_INVOKABLE QString attachmentFile(const QStringList &keys, const QString &title);
80 // The answer to `question`: `go` false drops the export.
81 Q_INVOKABLE void answerQuestion(bool go, bool includePersonal);
82 // Worker-safe file name: "Mara Klein.vcf", controls and separators removed.
83 static QString fileName(const QString &title);
84
85 signals:
86 void changed();
87 // The cards that were added or updated (the workspace selects the first).
88 void imported(const QStringList &keys);
89 void questionChanged();
90 // "Send as vCard" after the question: the file for the composer.
91 void attachmentReady(const QString &path);
92
93 private:
94 // An export waiting for the personal-details answer.
95 struct Pending {
96 QStringList raws;
97 QString title;
98 QString sheetTitle;
99 bool attachment = false;
100 };
101 // Runs the export now, or holds it and asks when a card has personal details.
102 bool startExport(const QStringList &raws, const QString &title, const QString &sheetTitle, bool attachment);
103 bool writeExport(const QStringList &raws, const QString &title, const QString &status = {});
104 QString writeAttachment(const QStringList &raws, const QString &title);
105 QStringList rawCards(const QStringList &keys) const;
106 ContactSyncCoordinator *coordinator() const;
107
108 MailSessionCore &m_session;
109 Deps m_deps;
110 QStringList m_cards;
111 QVariantList m_items;
112 QString m_sourceName;
113 qint64 m_targetBook = 0;
114 quint64 m_readGeneration = 0;
115 std::optional<Pending> m_pending;
116 };
117