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

presentation/ComposePasteJob.h
Line Branch Exec Source
1 #pragma once
2
3 #include "data/ComposeHtmlImport.h"
4
5 #include <QFutureWatcher>
6 #include <QList>
7 #include <QObject>
8 #include <QPointer>
9 #include <QTextCursor>
10 #include <QTextDocumentFragment>
11 #include <atomic>
12 #include <memory>
13
14 class RichTextController;
15
16 // Sprint 108 S108.10.4 (G11): a large HTML paste or drop into a formatted
17 // message without a frozen window. The import (ComposeHtmlImport, the
18 // bounded parser of untrusted HTML) and the split into parts run on a
19 // worker; the GUI thread then inserts one part per turn of the event loop,
20 // so frames keep coming. Parts end only at top-level paragraph boundaries
21 // outside tables and lists, each with the paragraph break after it, so the
22 // result equals one whole insertion (test_compose_paste). The first part
23 // replaces the selection and opens the undo step; later parts join it — the
24 // paste is one undo step. ComposeSession keeps the editor read-only while a
25 // job runs and finishes it at once before a format switch or a send.
26 class ComposePasteJob : public QObject {
27 Q_OBJECT
28 public:
29 // HTML up to this size is inserted at once (the stall stays small).
30 static constexpr qsizetype AsyncBytes = 64 * 1024;
31 // About this many characters per part (one part ≈ 1–3 ms on the GUI
32 // thread); parts go in until a turn of the event loop has used
33 // TurnBudgetMs, then a frame comes before the next ones.
34 static constexpr int PartCharacters = 4 * 1024;
35 static constexpr int TurnBudgetMs = 20;
36
37 struct Prepared {
38 ComposeHtmlImport::Result imported; // outcome, images and notices; `fragment` is split into `parts`
39 QList<QTextDocumentFragment> parts;
40 };
41 // The worker half: import, then split. Any thread; `cancelled` stops early.
42 static Prepared prepare(const QByteArray &html, const ComposeHtmlImport::Options &options,
43 const std::atomic_bool *cancelled = nullptr, int partCharacters = PartCharacters);
44
45 explicit ComposePasteJob(RichTextController &editor, QObject *parent = nullptr);
46 ~ComposePasteJob() override;
47
48 // Starts the import of `html` for the editor's current caret or selection.
49 void start(const QByteArray &html, const ComposeHtmlImport::Options &options);
50 // Inserts every part still waiting right now (blocking for a running
51 // import). Used before anything reads or converts the whole document.
52 void finishNow();
53 21 bool running() const { return m_running; }
54
55 signals:
56 // Before the first part goes in: the caller registers the images and
57 // tells about remote images left out.
58 void imported(const ComposeHtmlImport::Result &imported);
59 // The last part is in, `caret` after it (-1 when the editor's document
60 // went away meanwhile).
61 void finished(int caret);
62 // The import refused the HTML: the caller inserts the clipboard text.
63 void failed();
64
65 private:
66 void onPrepared();
67 void insertNext();
68 void finish(int caret);
69
70 RichTextController &m_editor;
71 QFutureWatcher<Prepared> m_watcher;
72 std::shared_ptr<std::atomic_bool> m_cancelled;
73 QList<QTextDocumentFragment> m_parts;
74 QTextCursor m_at;
75 int m_inserted = 0;
76 bool m_running = false;
77 bool m_prepared = false; // onPrepared ran for the current job
78 qint64 m_startedMs = 0;
79 double m_longestPartMs = 0;
80 };
81