File indexing completed on 2024-04-28 16:49:46

0001 /*
0002     SPDX-FileCopyrightText: 2007 John Tapsell <tapsell@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include <QDebug>
0008 #include <QTreeView>
0009 #include <QtCore>
0010 #include <QtTestGui>
0011 #include <QProcess>
0012 
0013 #include <limits>
0014 
0015 #include "processcore/process.h"
0016 #include "processcore/processes.h"
0017 #include "processcore/processes_base_p.h"
0018 #include "processcore_debug.h"
0019 
0020 #include "processui/ksysguardprocesslist.h"
0021 
0022 #include "processtest.h"
0023 
0024 Q_DECLARE_METATYPE(KSysGuard::Process::Scheduler);
0025 Q_DECLARE_METATYPE(KSysGuard::Process::IoPriorityClass);
0026 
0027 void testProcess::testSetScheduler_data()
0028 {
0029     QTest::addColumn<int>("priority");
0030     QTest::addColumn<KSysGuard::Process::Scheduler>("scheduler");
0031     QTest::addColumn<bool>("niceness");
0032 
0033     QTest::addRow("normal, 0") << 0 << KSysGuard::Process::Scheduler::Other << true;
0034     QTest::addRow("normal, 5") << 5 << KSysGuard::Process::Scheduler::Other << true;
0035 
0036     QTest::addRow("batch, 0") << 0 << KSysGuard::Process::Scheduler::Batch << true;
0037     QTest::addRow("batch, 5") << 5 << KSysGuard::Process::Scheduler::Batch << true;
0038 
0039     QTest::addRow("idle, 0") << 0 << KSysGuard::Process::Scheduler::SchedulerIdle << false;
0040 
0041     QTest::addRow("rr, 5") << 5 << KSysGuard::Process::Scheduler::RoundRobin << false;
0042 
0043     QTest::addRow("fifo, 5") << 5 << KSysGuard::Process::Scheduler::Fifo << false;
0044 }
0045 
0046 void testProcess::testSetScheduler()
0047 {
0048     QFETCH(int, priority);
0049     QFETCH(KSysGuard::Process::Scheduler, scheduler);
0050     QFETCH(bool, niceness);
0051 
0052     KSysGuard::Processes *processController = new KSysGuard::Processes();
0053 
0054     QProcess proc;
0055     proc.start(QStringLiteral("sleep"), {QStringLiteral("100")});
0056     QVERIFY(proc.waitForStarted());
0057 
0058     int pid=proc.processId();
0059     QVERIFY(pid);
0060 
0061     if (!processController->setScheduler(pid, scheduler, priority))
0062         QSKIP("skipping verfifcation because setScheduler failed");
0063 
0064     if (niceness && !processController->setNiceness(pid, priority))
0065         QSKIP("skipping verfifcation because setNiceness failed");
0066 
0067     processController->updateAllProcesses();
0068     KSysGuard::Process* process = processController->getProcess(pid);
0069 
0070     QVERIFY(process);
0071     QCOMPARE(process->scheduler(), scheduler);
0072     QCOMPARE(process->niceLevel(), priority);
0073 }
0074 
0075 void testProcess::testSetIoScheduler_data()
0076 {
0077     QTest::addColumn<int>("priority");
0078     QTest::addColumn<KSysGuard::Process::IoPriorityClass>("prioClass");
0079 
0080     QTest::addRow("idle, 0") << 0 << KSysGuard::Process::Idle;
0081     QTest::addRow("idle, 5") << 5 << KSysGuard::Process::Idle;
0082 
0083     QTest::addRow("best-effort, 0") << 0 << KSysGuard::Process::BestEffort;
0084     QTest::addRow("best-effort, 5") << 5 << KSysGuard::Process::BestEffort;
0085 
0086     QTest::addRow("rt, 0") << 0 << KSysGuard::Process::RealTime;
0087     QTest::addRow("rt, 5") << 5 << KSysGuard::Process::RealTime;
0088 }
0089 
0090 void testProcess::testSetIoScheduler()
0091 {
0092     QFETCH(int, priority);
0093     QFETCH(KSysGuard::Process::IoPriorityClass, prioClass);
0094 
0095     KSysGuard::Processes *processController = new KSysGuard::Processes();
0096 
0097     QProcess proc;
0098     proc.start(QStringLiteral("sleep"), {QStringLiteral("100")});
0099     QVERIFY(proc.waitForStarted());
0100 
0101     int pid=proc.processId();
0102     QVERIFY(pid);
0103 
0104     if (!processController->setIoNiceness(pid, prioClass, priority))
0105         QSKIP("skipping verfifcation because setNiceness failed");
0106 
0107     processController->updateAllProcesses();
0108     KSysGuard::Process* process = processController->getProcess(pid);
0109 
0110     QVERIFY(process);
0111     QCOMPARE(process->ioPriorityClass(), prioClass);
0112     QCOMPARE(process->ioniceLevel(), priority);
0113 }
0114 
0115 void testProcess::testProcesses()
0116 {
0117     KSysGuard::Processes *processController = new KSysGuard::Processes();
0118     processController->updateAllProcesses();
0119     const QList<KSysGuard::Process *> processes = processController->getAllProcesses();
0120     QSet<long> pids;
0121     for (KSysGuard::Process *process : processes) {
0122         if (process->pid() == 0)
0123             continue;
0124         QVERIFY(process->pid() > 0);
0125         QVERIFY(!process->name().isEmpty());
0126 
0127         // test all the pids are unique
0128         QVERIFY(!pids.contains(process->pid()));
0129         pids.insert(process->pid());
0130     }
0131     processController->updateAllProcesses();
0132     const QList<KSysGuard::Process *> processes2 = processController->getAllProcesses();
0133     for (KSysGuard::Process *process : processes2) {
0134         if (process->pid() == 0)
0135             continue;
0136         QVERIFY(process->pid() > 0);
0137         QVERIFY(!process->name().isEmpty());
0138 
0139         // test all the pids are unique
0140         if (!pids.contains(process->pid())) {
0141             qCDebug(LIBKSYSGUARD_PROCESSCORE) << process->pid() << " not found. " << process->name();
0142         }
0143         pids.remove(process->pid());
0144     }
0145 
0146     QVERIFY(processes2.size() == processes.size());
0147     QCOMPARE(processes,
0148              processes2); // Make sure calling it twice gives the same results.  The difference in time is so small that it really shouldn't have changed
0149     delete processController;
0150 }
0151 
0152 unsigned long testProcess::countNumChildren(KSysGuard::Process *p)
0153 {
0154     unsigned long total = p->children().size();
0155     for (int i = 0; i < p->children().size(); i++) {
0156         total += countNumChildren(p->children()[i]);
0157     }
0158     return total;
0159 }
0160 
0161 void testProcess::testProcessesTreeStructure()
0162 {
0163     KSysGuard::Processes *processController = new KSysGuard::Processes();
0164     processController->updateAllProcesses();
0165 
0166     auto verify_counts = [this](const auto processes) {
0167         for (KSysGuard::Process *process : processes) {
0168             QCOMPARE(countNumChildren(process), process->numChildren());
0169 
0170             for(int i = 0; i < process->children().size(); i++) {
0171                 QVERIFY(process->children()[i]->parent());
0172                 QCOMPARE(process->children()[i]->parent(), process);
0173             }
0174         }
0175     };
0176 
0177     verify_counts(processController->getAllProcesses());
0178 
0179     // this should test if the children accounting isn't off on updates
0180     QProcess proc;
0181     proc.start(QStringLiteral("/bin/sh"), {QStringLiteral("-c"), QStringLiteral("sleep 100& (sleep 50; sleep 50) & while true; do :; done")});
0182     QVERIFY(proc.waitForStarted());
0183     QTest::qSleep(2000);
0184 
0185     processController->updateAllProcesses();
0186     verify_counts(processController->getAllProcesses());
0187 
0188     proc.terminate();
0189 
0190     QVERIFY(proc.waitForFinished());
0191     processController->updateAllProcesses();
0192     verify_counts(processController->getAllProcesses());
0193 
0194     delete processController;
0195 }
0196 
0197 void testProcess::testProcessesModification()
0198 {
0199     // We will modify the tree, then re-call getProcesses and make sure that it fixed everything we modified
0200     KSysGuard::Processes *processController = new KSysGuard::Processes();
0201     processController->updateAllProcesses();
0202     KSysGuard::Process *initProcess = processController->getProcess(1);
0203 
0204     if (!initProcess || initProcess->numChildren() < 3) {
0205         delete processController;
0206         return;
0207     }
0208 
0209     QVERIFY(initProcess);
0210     QVERIFY(initProcess->children()[0]);
0211     QVERIFY(initProcess->children()[1]);
0212     qCDebug(LIBKSYSGUARD_PROCESSCORE) << initProcess->numChildren();
0213     initProcess->children()[0]->setParent(initProcess->children()[1]);
0214     initProcess->children()[1]->children().append(initProcess->children()[0]);
0215     initProcess->children()[1]->numChildren()++;
0216     initProcess->numChildren()--;
0217     initProcess->children().removeAt(0);
0218     delete processController;
0219 }
0220 
0221 void testProcess::testTimeToUpdateAllProcesses()
0222 {
0223     // See how long it takes to get process information
0224     KSysGuard::Processes *processController = new KSysGuard::Processes();
0225     QBENCHMARK {
0226         processController->updateAllProcesses();
0227     }
0228     delete processController;
0229 }
0230 void testProcess::testTimeToUpdateModel()
0231 {
0232     KSysGuardProcessList *processList = new KSysGuardProcessList;
0233     processList->treeView()->setColumnHidden(13, false);
0234     processList->show();
0235     QVERIFY(QTest::qWaitForWindowExposed(processList));
0236 
0237     QBENCHMARK {
0238         processList->updateList();
0239         QTest::qWait(0);
0240     }
0241     delete processList;
0242 }
0243 
0244 void testProcess::testHistories()
0245 {
0246     KSysGuard::Processes *processController = new KSysGuard::Processes();
0247     QBENCHMARK_ONCE {
0248         if (!processController->isHistoryAvailable()) {
0249             qWarning("History was not available");
0250             delete processController;
0251             return;
0252         }
0253     }
0254     QCOMPARE(processController->historyFileName(), QStringLiteral("/var/log/atop.log"));
0255     QList<QPair<QDateTime, uint>> history = processController->historiesAvailable();
0256     bool success = processController->setViewingTime(history[0].first);
0257     QVERIFY(success);
0258     QVERIFY(processController->viewingTime() == history[0].first);
0259     success = processController->setViewingTime(history[0].first.addSecs(-1));
0260     QVERIFY(success);
0261     QVERIFY(processController->viewingTime() == history[0].first);
0262     success = processController->setViewingTime(history[0].first.addSecs(-history[0].second - 1));
0263     QVERIFY(!success);
0264     QVERIFY(processController->viewingTime() == history[0].first);
0265     QCOMPARE(processController->historyFileName(), QStringLiteral("/var/log/atop.log"));
0266 
0267     // Test the tree structure
0268     processController->updateAllProcesses();
0269     const QList<KSysGuard::Process *> processes = processController->getAllProcesses();
0270 
0271     for (KSysGuard::Process *process : processes) {
0272         QCOMPARE(countNumChildren(process), process->numChildren());
0273 
0274         for (int i = 0; i < process->children().size(); i++) {
0275             QVERIFY(process->children()[i]->parent());
0276             QCOMPARE(process->children()[i]->parent(), process);
0277         }
0278     }
0279 
0280     // test all the pids are unique
0281     QSet<long> pids;
0282     for (KSysGuard::Process *process : processes) {
0283         if (process->pid() == 0)
0284             continue;
0285         QVERIFY(process->pid() > 0);
0286         QVERIFY(!process->name().isEmpty());
0287 
0288         QVERIFY(!pids.contains(process->pid()));
0289         pids.insert(process->pid());
0290     }
0291     delete processController;
0292 }
0293 
0294 void testProcess::testUpdateOrAddProcess()
0295 {
0296     KSysGuard::Processes *processController = new KSysGuard::Processes();
0297     processController->updateAllProcesses();
0298     KSysGuard::Process *process;
0299     // Make sure that this doesn't crash at least
0300     process = processController->getProcess(0);
0301     process = processController->getProcess(1);
0302     if (process)
0303         QCOMPARE(process->pid(), 1l);
0304 
0305     // Make sure that this doesn't crash at least
0306     processController->updateOrAddProcess(1);
0307     processController->updateOrAddProcess(0);
0308     processController->updateOrAddProcess(-1);
0309 
0310     processController->updateOrAddProcess(std::numeric_limits<long>::max()-1);
0311     QVERIFY(processController->getProcess(std::numeric_limits<long>::max()-1));
0312     processController->updateAllProcesses();
0313     QVERIFY(processController->getProcess(std::numeric_limits<long>::max()-1));
0314     QCOMPARE(processController->getProcess(std::numeric_limits<long>::max()-1)->status(), KSysGuard::Process::Ended);
0315     processController->updateAllProcesses();
0316     QVERIFY(!processController->getProcess(std::numeric_limits<long>::max()-1));
0317 }
0318 
0319 void testProcess::testHistoriesWithWidget()
0320 {
0321     KSysGuardProcessList *processList = new KSysGuardProcessList;
0322     processList->treeView()->setColumnHidden(13, false);
0323     processList->show();
0324     QVERIFY(QTest::qWaitForWindowExposed(processList));
0325     KSysGuard::Processes *processController = processList->processModel()->processController();
0326 
0327     QList<QPair<QDateTime, uint>> history = processController->historiesAvailable();
0328 
0329     for (int i = 0; i < history.size(); i++) {
0330         qCDebug(LIBKSYSGUARD_PROCESSCORE) << "Viewing time" << history[i].first;
0331         bool success = processController->setViewingTime(history[i].first);
0332         QVERIFY(success);
0333         QCOMPARE(processController->viewingTime(), history[i].first);
0334         processList->updateList();
0335         QTest::qWait(100);
0336     }
0337     delete processList;
0338 }
0339 
0340 void testProcess::testCPUGraphHistory()
0341 {
0342     KSysGuardProcessList processList;
0343     processList.show();
0344     QVERIFY(QTest::qWaitForWindowExposed(&processList));
0345     auto model = processList.processModel();
0346     // Access the PercentageHistoryRole to enable collection
0347     for (int i = 0; i < model->rowCount(); i++) {
0348         auto index = model->index(i, ProcessModel::HeadingCPUUsage, {});
0349         auto percentageHist = index.data(ProcessModel::PercentageHistoryRole).value<QVector<ProcessModel::PercentageHistoryEntry>>();
0350     }
0351 
0352     processList.updateList();
0353 
0354     // Verify that the current value is the newest history entry
0355     for (int i = 0; i < model->rowCount(); i++) {
0356         auto index = model->index(i, ProcessModel::HeadingCPUUsage, {});
0357         auto percentage = index.data(ProcessModel::PercentageRole).toFloat();
0358         auto percentageHist = index.data(ProcessModel::PercentageHistoryRole).value<QVector<ProcessModel::PercentageHistoryEntry>>();
0359         QVERIFY(percentageHist.size() > 0);
0360         QCOMPARE(percentage, percentageHist.constLast().value);
0361     }
0362 }
0363 
0364 QTEST_MAIN(testProcess)