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% 30 / 0 / 30
Functions: 100.0% 9 / 0 / 9
Branches: 69.0% 29 / 0 / 42

data/SqliteInterruptScope.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QByteArray>
4 #include <QElapsedTimer>
5 #include <QHash>
6 #include <QSqlDatabase>
7 #include <QSqlDriver>
8 #include <atomic>
9 #include <sqlite3.h>
10
11 // Interrupts long SQLite statements on a QSQLITE connection (Sprint 86
12 // search pages, Sprint 101 summary): while the scope lives, the progress
13 // handler stops the statement when `cancelled` turns true or the time limit
14 // passes. SQLite holds one handler per connection: a nested scope on the
15 // same connection takes it over and hands it back to the enclosing scope
16 // on destruction (S106-113), so scopes nest in LIFO order. A connection is
17 // used by one thread, hence the thread-local registry.
18 class SqliteInterruptScope {
19 public:
20 1058 SqliteInterruptScope(QSqlDatabase &database, const std::atomic_bool &cancelled, int timeLimitMs)
21 1058 : m_cancelled(cancelled), m_timeLimitMs(timeLimitMs) {
22
4/5
✓ Branch 3 → 4 taken 1058 times.
✓ Branch 4 → 5 taken 1058 times.
✗ Branch 4 → 7 not taken.
✓ Branch 5 → 6 taken 1058 times.
✓ Branch 6 → 8 taken 1058 times.
1058 const auto handle = database.driver() ? database.driver()->handle() : QVariant();
23
7/13
✓ Branch 8 → 9 taken 1058 times.
✓ Branch 9 → 10 taken 1058 times.
✗ Branch 9 → 14 not taken.
✓ Branch 10 → 11 taken 1058 times.
✓ Branch 11 → 12 taken 1058 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 1058 times.
✓ Branch 16 → 17 taken 1058 times.
✗ Branch 16 → 18 not taken.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 1058 times.
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 36 not taken.
1058 if (!handle.isValid() || QByteArray(handle.typeName()) != "sqlite3*") return;
24 1058 m_database = *static_cast<sqlite3 *const *>(handle.data());
25 1058 m_elapsed.start();
26 1058 m_enclosing = active().value(m_database, nullptr);
27
1/1
✓ Branch 25 → 26 taken 1058 times.
1058 active().insert(m_database, this);
28
1/1
✓ Branch 26 → 27 taken 1058 times.
1058 sqlite3_progress_handler(m_database, 1000, &SqliteInterruptScope::onProgress, this);
29 1058 }
30 2116 ~SqliteInterruptScope() {
31
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1058 times.
1058 if (!m_database) return;
32
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 8 taken 1057 times.
1058 if (m_enclosing) {
33 1 active().insert(m_database, m_enclosing);
34 1 sqlite3_progress_handler(m_database, 1000, &SqliteInterruptScope::onProgress, m_enclosing);
35 } else {
36 1057 active().remove(m_database);
37 1057 sqlite3_progress_handler(m_database, 0, nullptr, nullptr);
38 }
39 1058 }
40 SqliteInterruptScope(const SqliteInterruptScope &) = delete;
41 SqliteInterruptScope &operator=(const SqliteInterruptScope &) = delete;
42
43 // False when the connection is no interruptible SQLite connection.
44 1057 bool valid() const { return m_database != nullptr; }
45 // True when the handler stopped a statement because of the time limit.
46 1034 bool timedOut() const { return m_timedOut; }
47 // True once the limit passed, also between statements (row loops in C++).
48
3/6
✓ Branch 2 → 3 taken 105962 times.
✗ Branch 2 → 6 not taken.
✓ Branch 3 → 4 taken 105962 times.
✗ Branch 3 → 7 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 105962 times.
105962 bool overTime() const { return m_timedOut || (m_database && m_elapsed.elapsed() >= m_timeLimitMs); }
49
3/4
✓ Branch 3 → 4 taken 105962 times.
✓ Branch 3 → 6 taken 4 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 105962 times.
105966 bool stopped() const { return m_cancelled.load() || overTime(); }
50
1/2
✓ Branch 2 → 3 taken 514 times.
✗ Branch 2 → 4 not taken.
514 qint64 elapsedMs() const { return m_database ? m_elapsed.elapsed() : 0; }
51
52 private:
53 3174 static QHash<sqlite3 *, SqliteInterruptScope *> &active() {
54
2/2
✓ Branch 2 → 3 taken 177 times.
✓ Branch 2 → 4 taken 2997 times.
3174 thread_local QHash<sqlite3 *, SqliteInterruptScope *> scopes;
55 3174 return scopes;
56 }
57 20962 static int onProgress(void *context) {
58 20962 auto &scope = *static_cast<SqliteInterruptScope *>(context);
59 20962 scope.m_timedOut = scope.m_elapsed.elapsed() >= scope.m_timeLimitMs;
60
4/4
✓ Branch 4 → 5 taken 20925 times.
✓ Branch 4 → 6 taken 37 times.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 20924 times.
20962 return scope.m_cancelled.load() || scope.m_timedOut ? 1 : 0;
61 }
62 const std::atomic_bool &m_cancelled;
63 int m_timeLimitMs;
64 sqlite3 *m_database = nullptr;
65 SqliteInterruptScope *m_enclosing = nullptr;
66 QElapsedTimer m_elapsed;
67 bool m_timedOut = false;
68 };
69