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

IJHack / QtPass / 23862950363

01 Apr 2026 05:54PM UTC coverage: 20.086% (+0.004%) from 20.082%
23862950363

push

github

web-flow
Merge pull request #890 from IJHack/fix/ai-findings-10-more

fix: address 10 more AI findings across 4 files

9 of 45 new or added lines in 7 files covered. (20.0%)

4 existing lines in 4 files now uncovered.

1030 of 5128 relevant lines covered (20.09%)

7.89 hits per line

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

34.41
/src/executor.cpp
1
// SPDX-FileCopyrightText: 2016 Anne Jan Brouwer
2
// SPDX-License-Identifier: GPL-3.0-or-later
3
#include "executor.h"
4
#include <QCoreApplication>
5
#include <QDir>
6
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
7
#include <QTextCodec>
8
#endif
9
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
10
#include <QStringDecoder>
11
#endif
12
#include <utility>
13

14
#ifdef QT_DEBUG
15
#include "debughelper.h"
16
#endif
17

18
/**
19
 * @brief Executor::Executor executes external applications
20
 * @param parent
21
 */
22
Executor::Executor(QObject *parent) : QObject(parent), running(false) {
11✔
23
  connect(&m_process,
11✔
24
          static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(
25
              &QProcess::finished),
26
          this,
27
          static_cast<void (Executor::*)(int, QProcess::ExitStatus)>(
11✔
28
              &Executor::finished));
29
  connect(&m_process, &QProcess::started, this, &Executor::starting);
11✔
30
}
11✔
31

32
/**
33
 * @brief Executor::startProcess starts the internal process, handling WSL
34
 * prefixes.
35
 * @param app Executable path (may start with "wsl ").
36
 * @param args Arguments to pass to the executable.
37
 */
NEW
38
void Executor::startProcess(const QString &app, const QStringList &args) {
×
NEW
39
  if (app.startsWith("wsl ")) {
×
40
    QStringList wslArgs = args;
41
    QString actualApp = app;
NEW
42
    wslArgs.prepend(actualApp.remove(0, 4));
×
NEW
43
    m_process.start("wsl", wslArgs);
×
44
  } else {
NEW
45
    m_process.start(app, args);
×
46
  }
NEW
47
}
×
48

49
/**
50
 * @brief Executor::startProcessBlocking starts a given process, handling WSL
51
 * prefixes.
52
 * @param internal QProcess reference to start.
53
 * @param app Executable path (may start with "wsl ").
54
 * @param args Arguments to pass to the executable.
55
 */
56
void Executor::startProcessBlocking(QProcess &internal, const QString &app,
13✔
57
                                    const QStringList &args) {
58
  if (app.startsWith("wsl ")) {
26✔
59
    QStringList wslArgs = args;
60
    QString actualApp = app;
NEW
61
    wslArgs.prepend(actualApp.remove(0, 4));
×
NEW
62
    internal.start("wsl", wslArgs);
×
63
  } else {
64
    internal.start(app, args);
13✔
65
  }
66
}
13✔
67

68
/**
69
 * @brief Executor::executeNext consumes executable tasks from the queue
70
 */
71
void Executor::executeNext() {
×
72
  if (!running) {
×
73
    if (!m_execQueue.isEmpty()) {
×
74
      const execQueueItem &i = m_execQueue.head();
75
      running = true;
×
76
      if (!i.workingDir.isEmpty()) {
×
77
        m_process.setWorkingDirectory(i.workingDir);
×
78
      }
NEW
79
      startProcess(i.app, i.args);
×
UNCOV
80
      if (!i.input.isEmpty()) {
×
NEW
81
        if (!m_process.waitForStarted(-1)) {
×
82
#ifdef QT_DEBUG
83
          dbg() << "Process failed to start:" << i.id << " " << i.app;
84
#endif
NEW
85
          m_process.closeWriteChannel();
×
NEW
86
          running = false;
×
NEW
87
          m_execQueue.dequeue();
×
NEW
88
          executeNext();
×
NEW
89
          return;
×
90
        }
91
        QByteArray data = i.input.toUtf8();
×
92
        if (m_process.write(data) != data.length()) {
×
93
#ifdef QT_DEBUG
94
          dbg() << "Not all data written to process:" << i.id << " " << i.app;
95
#endif
96
        }
97
      }
98
      m_process.closeWriteChannel();
×
99
    }
100
  }
101
}
102

103
/**
104
 * @brief Executor::execute execute an app
105
 * @param id
106
 * @param app
107
 * @param args
108
 * @param readStdout
109
 * @param readStderr
110
 */
111
void Executor::execute(int id, const QString &app, const QStringList &args,
×
112
                       bool readStdout, bool readStderr) {
113
  execute(id, QString(), app, args, QString(), readStdout, readStderr);
×
114
}
×
115

116
/**
117
 * @brief Executor::execute executes an app from a workDir
118
 * @param id
119
 * @param workDir
120
 * @param app
121
 * @param args
122
 * @param readStdout
123
 * @param readStderr
124
 */
125
void Executor::execute(int id, const QString &workDir, const QString &app,
×
126
                       const QStringList &args, bool readStdout,
127
                       bool readStderr) {
128
  execute(id, workDir, app, args, QString(), readStdout, readStderr);
×
129
}
×
130

131
/**
132
 * @brief Executor::execute an app, takes input and presents it as stdin
133
 * @param id
134
 * @param app
135
 * @param args
136
 * @param input
137
 * @param readStdout
138
 * @param readStderr
139
 */
140
void Executor::execute(int id, const QString &app, const QStringList &args,
×
141
                       QString input, bool readStdout, bool readStderr) {
142
  execute(id, QString(), app, args, std::move(input), readStdout, readStderr);
×
143
}
×
144

145
/**
146
 * @brief Executor::execute  executes an app from a workDir, takes input and
147
 * presents it as stdin
148
 * @param id
149
 * @param workDir
150
 * @param app
151
 * @param args
152
 * @param input
153
 * @param readStdout
154
 * @param readStderr
155
 */
156
void Executor::execute(int id, const QString &workDir, const QString &app,
×
157
                       const QStringList &args, QString input, bool readStdout,
158
                       bool readStderr) {
159
  // Happens a lot if e.g. git binary is not set.
160
  // This will result in bogus "QProcess::FailedToStart" messages,
161
  // also hiding legitimate errors from the gpg commands.
162
  if (app.isEmpty()) {
×
163
#ifdef QT_DEBUG
164
    dbg() << "Trying to execute nothing...";
165
#endif
166
    return;
×
167
  }
168
  QString appPath = app;
169
  if (!appPath.startsWith("wsl ")) {
×
170
    appPath =
171
        QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(app);
×
172
  }
173
  m_execQueue.push_back(
×
174
      {id, appPath, args, std::move(input), readStdout, readStderr, workDir});
175
  executeNext();
×
176
}
×
177

178
/**
179
 * @brief decodes the input into a string assuming UTF-8 encoding.
180
 * If this fails (which is likely if it is not actually UTF-8)
181
 * it will then fall back to Qt's decoding function, which
182
 * will try based on BOM and if that fails fall back to local encoding.
183
 * This should not be needed in Qt6
184
 *
185
 * @param in input data
186
 * @return Input bytes decoded to string
187
 */
188
static auto decodeAssumingUtf8(const QByteArray &in) -> QString {
18✔
189
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
190
  QTextCodec *codec = QTextCodec::codecForName("UTF-8");
191
  QTextCodec::ConverterState state;
192
  QString out = codec->toUnicode(in.constData(), in.size(), &state);
193
  if (state.invalidChars == 0) {
194
    return out;
195
  }
196
  codec = QTextCodec::codecForUtfText(in);
197
  return codec->toUnicode(in);
198
#else
199
  auto converter = QStringDecoder(QStringDecoder::Utf8);
200
  QString out = converter(in);
18✔
201
  if (!converter.hasError()) {
18✔
202
    return out;
203
  }
204
  // Fallback if UTF-8 decoding failed - try system encoding
205
  auto fallback = QStringDecoder(QStringDecoder::System);
NEW
206
  return fallback(in);
×
207
#endif
208
}
209

210
/**
211
 * @brief Executor::executeBlocking blocking version of the executor,
212
 * takes input and presents it as stdin
213
 * @param app
214
 * @param args
215
 * @param input
216
 * @param process_out
217
 * @param process_err
218
 * @return
219
 *
220
 * Note: Returning error code instead of throwing to maintain compatibility
221
 * with the existing error handling pattern used throughout QtPass.
222
 */
223
auto Executor::executeBlocking(QString app, const QStringList &args,
13✔
224
                               const QString &input, QString *process_out,
225
                               QString *process_err) -> int {
226
  QProcess internal;
13✔
227
  startProcessBlocking(internal, app, args);
13✔
228
  if (!internal.waitForStarted(-1)) {
13✔
229
#ifdef QT_DEBUG
230
    dbg() << "Process failed to start:" << app;
231
#endif
232
    return -1;
233
  }
234
  if (!input.isEmpty()) {
9✔
235
    QByteArray data = input.toUtf8();
236
    if (internal.write(data) != data.length()) {
1✔
237
#ifdef QT_DEBUG
238
      dbg() << "Not all input written:" << app;
239
#endif
240
    }
241
    internal.closeWriteChannel();
1✔
242
  }
243
  internal.waitForFinished(-1);
9✔
244
  if (internal.exitStatus() == QProcess::NormalExit) {
9✔
245
    QString pout = decodeAssumingUtf8(internal.readAllStandardOutput());
9✔
246
    QString perr = decodeAssumingUtf8(internal.readAllStandardError());
9✔
247
    if (process_out != nullptr) {
9✔
248
      *process_out = pout;
9✔
249
    }
250
    if (process_err != nullptr) {
9✔
251
      *process_err = perr;
2✔
252
    }
253
    return internal.exitCode();
9✔
254
  }
255
  // Process failed to start or crashed; return -1 to indicate error.
256
  // The calling code checks for non-zero exit codes for error handling.
257
  return -1;
258
}
13✔
259

260
/**
261
 * @brief Executor::executeBlocking blocking version of the executor
262
 * @param app
263
 * @param args
264
 * @param process_out
265
 * @param process_err
266
 * @return
267
 */
268
auto Executor::executeBlocking(QString app, const QStringList &args,
3✔
269
                               QString *process_out, QString *process_err)
270
    -> int {
271
  return executeBlocking(std::move(app), args, QString(), process_out,
6✔
272
                         process_err);
3✔
273
}
274

275
/**
276
 * @brief Executor::setEnvironment set environment variables
277
 * for executor processes
278
 * @param env
279
 */
280
void Executor::setEnvironment(const QStringList &env) {
×
281
  m_process.setEnvironment(env);
×
282
}
×
283

284
/**
285
 * @brief Executor::cancelNext  cancels execution of first process in queue
286
 *                              if it's not already running
287
 *
288
 * @return  id of the cancelled process or -1 on error
289
 */
290
auto Executor::cancelNext() -> int {
×
291
  if (running || m_execQueue.isEmpty()) {
×
292
    return -1; // Return -1 to indicate no process was cancelled
293
               // (queue empty or currently executing).
294
  }
295
  return m_execQueue.dequeue().id;
×
296
}
297

298
/**
299
 * @brief Executor::finished called when an executed process finishes
300
 * @param exitCode
301
 * @param exitStatus
302
 */
303
void Executor::finished(int exitCode, QProcess::ExitStatus exitStatus) {
×
304
  execQueueItem i = m_execQueue.dequeue();
305
  running = false;
×
306
  if (exitStatus == QProcess::NormalExit) {
×
307
    QString output;
×
308
    QString err;
×
309
    if (i.readStdout) {
×
310
      output = decodeAssumingUtf8(m_process.readAllStandardOutput());
×
311
    }
312
    if (i.readStderr || exitCode != 0) {
×
313
      err = decodeAssumingUtf8(m_process.readAllStandardError());
×
314
      if (exitCode != 0) {
315
#ifdef QT_DEBUG
316
        dbg() << exitCode << err;
317
#endif
318
      }
319
    }
320
    emit finished(i.id, exitCode, output, err);
×
321
  }
322
  //  else: emit crashed with ID, which may give a chance to recover ?
323
  executeNext();
×
324
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc