mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-03-26 17:03:08 +00:00
Add a new QtTest that spawns marker processes and verifies ProcessScanner::scan() matches by env/cmdline, preferEnvironmentMatches behavior, and cancellation. Register the test with ctest.
202 lines
5.3 KiB
C++
202 lines
5.3 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// SPDX-FileCopyrightText: 2026 A-La-Karte Contributors
|
|
|
|
#include "processscanner.h"
|
|
|
|
#include <QElapsedTimer>
|
|
#include <QFileInfo>
|
|
#include <QProcess>
|
|
#include <QProcessEnvironment>
|
|
#include <QStandardPaths>
|
|
#include <QString>
|
|
#include <QUuid>
|
|
#include <QtTest>
|
|
|
|
#include <atomic>
|
|
#include <utility>
|
|
|
|
class ProcessScannerTest : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private Q_SLOTS:
|
|
void initTestCase();
|
|
void cleanupTestCase();
|
|
|
|
void scanFindsEnvAndCmdline();
|
|
void preferEnvironmentMatchesFilters();
|
|
void cancelledScanReturnsEmpty();
|
|
|
|
private:
|
|
QString m_shell;
|
|
QString m_marker;
|
|
QProcess m_envProc;
|
|
QProcess m_cmdProc;
|
|
|
|
static bool containsPid(const QList<ProcessScanner::Match> &matches, qint64 pid)
|
|
{
|
|
for (const auto &m : matches) {
|
|
if (m.pid == static_cast<uint>(pid)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static const ProcessScanner::Match *findByPid(const QList<ProcessScanner::Match> &matches, qint64 pid)
|
|
{
|
|
for (const auto &m : matches) {
|
|
if (m.pid == static_cast<uint>(pid)) {
|
|
return &m;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static bool waitForScan(std::function<bool()> predicate, int timeoutMs)
|
|
{
|
|
QElapsedTimer t;
|
|
t.start();
|
|
while (t.elapsed() < timeoutMs) {
|
|
if (predicate()) {
|
|
return true;
|
|
}
|
|
QTest::qWait(50);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void startProcesses();
|
|
void stopProcess(QProcess &p);
|
|
};
|
|
|
|
void ProcessScannerTest::initTestCase()
|
|
{
|
|
if (!QFileInfo(QStringLiteral("/proc")).isDir()) {
|
|
QSKIP("/proc not available");
|
|
}
|
|
|
|
m_shell = QStandardPaths::findExecutable(QStringLiteral("sh"));
|
|
if (m_shell.isEmpty()) {
|
|
QSKIP("sh not found");
|
|
}
|
|
|
|
m_marker = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
|
startProcesses();
|
|
}
|
|
|
|
void ProcessScannerTest::cleanupTestCase()
|
|
{
|
|
stopProcess(m_envProc);
|
|
stopProcess(m_cmdProc);
|
|
}
|
|
|
|
void ProcessScannerTest::startProcesses()
|
|
{
|
|
const QString script = QStringLiteral("while true; do sleep 60; done # %1").arg(m_marker);
|
|
|
|
{
|
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
|
env.insert(QStringLiteral("ALAKARTE_PROCESSSCANNER_TEST_MARKER"), m_marker);
|
|
m_envProc.setProcessEnvironment(env);
|
|
m_envProc.start(m_shell, {QStringLiteral("-c"), script});
|
|
QVERIFY2(m_envProc.waitForStarted(5000), qPrintable(m_envProc.errorString()));
|
|
QVERIFY(m_envProc.processId() > 0);
|
|
}
|
|
|
|
{
|
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
|
m_cmdProc.setProcessEnvironment(env);
|
|
m_cmdProc.start(m_shell, {QStringLiteral("-c"), script});
|
|
QVERIFY2(m_cmdProc.waitForStarted(5000), qPrintable(m_cmdProc.errorString()));
|
|
QVERIFY(m_cmdProc.processId() > 0);
|
|
}
|
|
}
|
|
|
|
void ProcessScannerTest::stopProcess(QProcess &p)
|
|
{
|
|
if (p.state() == QProcess::NotRunning) {
|
|
return;
|
|
}
|
|
|
|
p.terminate();
|
|
if (!p.waitForFinished(3000)) {
|
|
p.kill();
|
|
p.waitForFinished(3000);
|
|
}
|
|
}
|
|
|
|
void ProcessScannerTest::scanFindsEnvAndCmdline()
|
|
{
|
|
QVERIFY(m_envProc.processId() > 0);
|
|
QVERIFY(m_cmdProc.processId() > 0);
|
|
|
|
ProcessScanner::ScanOptions opts;
|
|
opts.envKeys = {QStringLiteral("ALAKARTE_PROCESSSCANNER_TEST_MARKER")};
|
|
opts.envValue = m_marker;
|
|
opts.cmdlineSubstring = m_marker;
|
|
opts.preferEnvironmentMatches = false;
|
|
|
|
QList<ProcessScanner::Match> results;
|
|
const bool ok = waitForScan(
|
|
[&]() {
|
|
results = ProcessScanner::scan(opts);
|
|
return containsPid(results, m_envProc.processId()) && containsPid(results, m_cmdProc.processId());
|
|
},
|
|
5000);
|
|
|
|
QVERIFY(ok);
|
|
|
|
const auto *envMatch = findByPid(results, m_envProc.processId());
|
|
QVERIFY(envMatch);
|
|
QVERIFY(envMatch->envMatched);
|
|
|
|
const auto *cmdMatch = findByPid(results, m_cmdProc.processId());
|
|
QVERIFY(cmdMatch);
|
|
QVERIFY(!cmdMatch->envMatched);
|
|
}
|
|
|
|
void ProcessScannerTest::preferEnvironmentMatchesFilters()
|
|
{
|
|
QVERIFY(m_envProc.processId() > 0);
|
|
QVERIFY(m_cmdProc.processId() > 0);
|
|
|
|
ProcessScanner::ScanOptions opts;
|
|
opts.envKeys = {QStringLiteral("ALAKARTE_PROCESSSCANNER_TEST_MARKER")};
|
|
opts.envValue = m_marker;
|
|
opts.cmdlineSubstring = m_marker;
|
|
opts.preferEnvironmentMatches = true;
|
|
|
|
QList<ProcessScanner::Match> results;
|
|
const bool ok = waitForScan(
|
|
[&]() {
|
|
results = ProcessScanner::scan(opts);
|
|
return containsPid(results, m_envProc.processId());
|
|
},
|
|
5000);
|
|
|
|
QVERIFY(ok);
|
|
QVERIFY(containsPid(results, m_envProc.processId()));
|
|
QVERIFY(!containsPid(results, m_cmdProc.processId()));
|
|
|
|
for (const auto &m : std::as_const(results)) {
|
|
QVERIFY(m.envMatched);
|
|
}
|
|
}
|
|
|
|
void ProcessScannerTest::cancelledScanReturnsEmpty()
|
|
{
|
|
ProcessScanner::ScanOptions opts;
|
|
opts.envKeys = {QStringLiteral("ALAKARTE_PROCESSSCANNER_TEST_MARKER")};
|
|
opts.envValue = m_marker;
|
|
opts.cmdlineSubstring = m_marker;
|
|
opts.preferEnvironmentMatches = false;
|
|
|
|
const std::atomic_bool cancelled(true);
|
|
const QList<ProcessScanner::Match> results = ProcessScanner::scan(opts, cancelled);
|
|
QVERIFY(results.isEmpty());
|
|
}
|
|
|
|
QTEST_MAIN(ProcessScannerTest)
|
|
|
|
#include "processscanner_test.moc"
|