• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IJHack / QtPass / 35159963146

16 Sep 2026 10:55PM UTC coverage: 72.009% (-0.3%) from 72.336%
35159963146

push

github

web-flow
One "qtpass" logging category instead of dbg() and 69 QT_DEBUG guards (#1785)

* One "qtpass" logging category instead of dbg() and 69 QT_DEBUG guards

debughelper.h promised file:line output "only in debug builds" while
src.pro already compiled qDebug out of release builds, and the #ifdef
blocks spliced control flow (an else in trayicon.cpp, an else-if in
sshauthsock.cpp) and have broken the build before (55149eca1, 0b6a80bdf).

Q_LOGGING_CATEGORY(lcQtPass, "qtpass", QtInfoMsg): every dbg() is a
qCDebug(lcQtPass), the debug-only qWarning()s are qCWarning(lcQtPass), the
guards are gone, and QT_NO_DEBUG_OUTPUT is no longer defined for release,
so a shipped build can produce a trace with
QT_LOGGING_RULES="qtpass.debug=true". FAQ entry and fixing skill updated;
tst_util checks the category is quiet until asked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JuQsrHonihp1nARE7bzstc

* Convert the last bare qDebug(); FAQ: key IDs

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JuQsrHonihp1nARE7bzstc

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

36 of 78 new or added lines in 15 files covered. (46.15%)

23 existing lines in 8 files now uncovered.

5513 of 7656 relevant lines covered (72.01%)

60.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.37
/src/singleapplication.cpp
1
// SPDX-FileCopyrightText: 2014 Anne Jan Brouwer
2
// SPDX-License-Identifier: GPL-3.0-or-later
3
#include "singleapplication.h"
4
#include "qtpasslogging.h"
5
#include <QDebug>
6
#include <QLocalSocket>
7
#include <QSharedPointer>
8
#include <QThread>
9
#include <utility>
10

11
/**
12
 * @brief SingleApplication::SingleApplication this replaces the QApplication
13
 * allowing for local socket based communications.
14
 * @param argc
15
 * @param argv
16
 * @param uniqueKey
17
 */
18
SingleApplication::SingleApplication(
1 ✔
19
    int &argc, char *argv[], // NOLINT(modernize-avoid-c-arrays)
20
    QString uniqueKey)
1 ✔
21
    : QApplication(argc, argv), _isRunning(false),
1 ✔
22
      _uniqueKey(std::move(uniqueKey)) {
1 ✔
23
  sharedMemory.setKey(_uniqueKey);
1 ✔
24
  if (sharedMemory.attach()) {
1 ✔
25
    if (peerIsListening()) {
1 ✔
26
      _isRunning = true;
1 ✔
27
      return;
1 ✔
28
    }
29
    // The segment outlived the instance that created it (a crash leaves
30
    // System V shared memory behind on Unix). Nobody answers on the socket,
31
    // so take over instead of forwarding into the void forever.
NEW
32
    qCDebug(lcQtPass) << "Stale single instance segment, taking over.";
×
UNCOV
33
    sharedMemory.detach();
×
34
  }
35
  becomePrimary();
×
36
}
×
37

38
// public slots.
39

40
/**
41
 * @brief SingleApplication::receiveMessage we have received (a command line)
42
 * message.
43
 *
44
 * Each accepted socket is read asynchronously so a peer that connects without
45
 * sending anything cannot stall the GUI thread; the message is delivered once
46
 * the peer disconnects and the socket is then deleted.
47
 */
48
void SingleApplication::receiveMessage() {
6 ✔
49
  while (QLocalSocket *localSocket = localServer->nextPendingConnection()) {
12 ✔
50
    auto buffer = QSharedPointer<QByteArray>::create();
6 ✔
51
    connect(localSocket, &QLocalSocket::readyRead, this,
6 ✔
52
            [localSocket, buffer]() {
13 ✔
53
              buffer->append(localSocket->readAll());
14 ✔
54
              if (buffer->size() > maxMessageBytes) {
7 ✔
55
                // Not a command line any more; drop the peer and its data.
56
                buffer->clear();
1 ✔
57
                localSocket->abort();
1 ✔
58
              }
59
            });
7 ✔
60
    connect(localSocket, &QLocalSocket::disconnected, this,
6 ✔
61
            [this, localSocket, buffer]() {
18 ✔
62
              buffer->append(localSocket->readAll());
12 ✔
63
              if (!buffer->isEmpty()) {
6 ✔
64
                emit messageAvailable(QString::fromUtf8(buffer->constData()));
6 ✔
65
              }
66
              localSocket->deleteLater();
6 ✔
67
            });
6 ✔
68
  }
6 ✔
69
}
6 ✔
70

71
// public functions.
72
/**
73
 * @brief SingleApplication::isRunning is there already a QtPass instance
74
 * running, to check whether to be server or client.
75
 * @return
76
 */
77
auto SingleApplication::isRunning() -> bool { return _isRunning; }
10 ✔
78

79
/**
80
 * @brief SingleApplication::sendMessage send a message (from commandline) to an
81
 * already running QtPass instance.
82
 * @param message
83
 * @return
84
 */
85
auto SingleApplication::sendMessage(const QString &message) -> bool {
2 ✔
86
  if (!_isRunning) {
2 ✔
87
    return false;
88
  }
89
  if (forwardMessage(message)) {
1 ✔
90
    return true;
91
  }
92
  // The peer vanished between the probe in the constructor and now. Let go of
93
  // its segment and take its place, so the window main() opens next is the
94
  // reachable instance. Staying attached without listening would make every
95
  // later launch fail create() and open yet another window without IPC.
96
  _isRunning = false;
1 ✔
97
  sharedMemory.detach();
1 ✔
98
  becomePrimary();
1 ✔
99
  return false;
1 ✔
100
}
101

102
// private functions.
103
/**
104
 * @brief SingleApplication::becomePrimary claim the shared-memory segment and
105
 * start listening for messages from later launches.
106
 *
107
 * Does nothing (and leaves isRunning() false) when another instance holds the
108
 * segment; the caller then runs without IPC rather than not at all.
109
 */
110
void SingleApplication::becomePrimary() {
1 ✔
111
  // create shared memory.
112
  if (!sharedMemory.create(1)) {
1 ✔
113
    // Another launch claimed the segment first (two launchers recovering
114
    // from the same crash). Give it a moment to start listening and become
115
    // its secondary, so main() forwards instead of opening a second window.
116
    if (sharedMemory.attach()) {
×
117
      for (int attempt = 0; attempt < takeoverProbes; ++attempt) {
×
118
        if (peerIsListening()) {
×
119
          _isRunning = true;
×
120
          return;
×
121
        }
122
        QThread::msleep(takeoverProbeIntervalMs);
×
123
      }
124
      sharedMemory.detach();
×
125
    }
NEW
126
    qCDebug(lcQtPass) << "Unable to create single instance.";
×
UNCOV
127
    return;
×
128
  }
129
  // create local server and listen to incoming messages from other
130
  // instances. A socket file left behind by a crashed instance would make
131
  // listen() fail (address in use) and silently disable IPC for good, so
132
  // clear it first: no live peer answered on it, or create() would have
133
  // failed.
134
  QLocalServer::removeServer(_uniqueKey);
1 ✔
135
  localServer.reset(new QLocalServer(this));
1 ✔
136
  localServer->setSocketOptions(QLocalServer::UserAccessOption);
1 ✔
137
  connect(localServer.data(), &QLocalServer::newConnection, this,
1 ✔
138
          &SingleApplication::receiveMessage);
2 ✔
139
  if (!localServer->listen(_uniqueKey)) {
1 ✔
NEW
140
    qCWarning(lcQtPass) << "SingleApplication: cannot listen on" << _uniqueKey
×
NEW
141
                        << ":" << localServer->errorString();
×
142
    // Holding the segment without a server would make every later launch
143
    // attach, fail the probe and then fail create(): no instance could ever
144
    // take over while this one lives. Release it and run without IPC.
145
    localServer.reset();
×
146
    sharedMemory.detach();
×
147
  }
148
}
149

150
/**
151
 * @brief SingleApplication::forwardMessage deliver a message to the instance
152
 * listening on the local socket.
153
 * @param message
154
 * @return true if the peer accepted the whole payload.
155
 */
156
auto SingleApplication::forwardMessage(const QString &message) -> bool {
1 ✔
157
  QLocalSocket localSocket(this);
1 ✔
158
  localSocket.connectToServer(_uniqueKey, QIODevice::WriteOnly);
1 ✔
159
  if (!localSocket.waitForConnected(timeout)) {
1 ✔
160
    qCDebug(lcQtPass) << localSocket.errorString().toLatin1();
1 ✔
161
    return false;
1 ✔
162
  }
163
  QByteArray payload = message.toUtf8();
164
  if (payload.isEmpty()) {
×
165
    payload = QByteArray(1, '\0');
×
166
  }
167
  localSocket.write(payload);
×
168
  if (!localSocket.waitForBytesWritten(timeout)) {
×
NEW
169
    qCDebug(lcQtPass) << localSocket.errorString().toLatin1();
×
UNCOV
170
    return false;
×
171
  }
172
  localSocket.disconnectFromServer();
×
173
  return true;
174
}
1 ✔
175

176
/**
177
 * @brief SingleApplication::peerIsListening probe whether an instance is
178
 * actually accepting connections on the local socket.
179
 * @return true if a connection to the unique key succeeded.
180
 */
181
auto SingleApplication::peerIsListening() -> bool {
1 ✔
182
  QLocalSocket probe;
1 ✔
183
  probe.connectToServer(_uniqueKey, QIODevice::WriteOnly);
1 ✔
184
  if (!probe.waitForConnected(timeout)) {
1 ✔
185
    return false;
186
  }
187
  probe.disconnectFromServer();
1 ✔
188
  return true;
189
}
1 ✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc