File indexing completed on 2024-05-12 05:11:09

0001 /*
0002     SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "racetest.h"
0008 
0009 #include <KProcess>
0010 #include <QDebug>
0011 
0012 #include <Akonadi/AgentInstance>
0013 #include <Akonadi/AgentInstanceCreateJob>
0014 #include <Akonadi/AgentManager>
0015 #include <Akonadi/AgentType>
0016 #include <akonadi/qtest_akonadi.h>
0017 #include <control.h>
0018 //#include <localfolders.h>
0019 
0020 #define TIMEOUT_SECONDS 20
0021 #define MAXCOUNT 10
0022 // NOTE: REQUESTER_EXE is defined by cmake.
0023 
0024 Q_DECLARE_METATYPE(QProcess::ProcessError)
0025 Q_DECLARE_METATYPE(QProcess::ExitStatus)
0026 
0027 using namespace Akonadi;
0028 
0029 void RaceTest::initTestCase()
0030 {
0031     QVERIFY(Control::start());
0032     QTest::qWait(1000); // give the MDA time to start so that we can kill it in peace
0033     qRegisterMetaType<QProcess::ProcessError>();
0034     qRegisterMetaType<QProcess::ExitStatus>();
0035 }
0036 
0037 void RaceTest::testMultipleProcesses_data()
0038 {
0039     QTest::addColumn<int>("count"); // how many processes to create
0040     QTest::addColumn<int>("delay"); // number of ms to wait before starting next process
0041 
0042     QTest::newRow("1-nodelay") << 1 << 0;
0043     QTest::newRow("2-nodelay") << 2 << 0;
0044     QTest::newRow("5-nodelay") << 5 << 0;
0045     QTest::newRow("10-nodelay") << 10 << 0;
0046     QTest::newRow("2-shortdelay") << 2 << 100;
0047     QTest::newRow("5-shortdelay") << 5 << 100;
0048     QTest::newRow("10-shortdelay") << 10 << 100;
0049     QTest::newRow("2-longdelay") << 2 << 1000;
0050     QTest::newRow("5-longdelay") << 5 << 1000;
0051     QTest::newRow("5-verylongdelay") << 5 << 4000;
0052     Q_ASSERT(10 <= MAXCOUNT);
0053 }
0054 
0055 void RaceTest::testMultipleProcesses()
0056 {
0057     QFETCH(int, count);
0058     QFETCH(int, delay);
0059 
0060     killZombies();
0061 
0062     // Remove all maildir instances (at most 1 really) and MDAs (which use LocalFolders).
0063     // (This is to ensure that one of *our* instances is the main instance.)
0064     AgentType::List types;
0065     types.append(AgentManager::self()->type(QLatin1StringView("akonadi_maildir_resource")));
0066     types.append(AgentManager::self()->type(QLatin1StringView("akonadi_maildispatcher_agent")));
0067     AgentInstance::List instances = AgentManager::self()->instances();
0068     for (const AgentInstance &instance : std::as_const(instances)) {
0069         if (types.contains(instance.type())) {
0070             qDebug() << "Removing instance of type" << instance.type().identifier();
0071             AgentManager::self()->removeInstance(instance);
0072             QSignalSpy removedSpy(AgentManager::self(), SIGNAL(instanceRemoved(Akonadi::AgentInstance)));
0073             QVERIFY(removedSpy.wait());
0074         }
0075     }
0076     instances = AgentManager::self()->instances();
0077     for (const AgentInstance &instance : std::as_const(instances)) {
0078         QVERIFY(!types.contains(instance.type()));
0079     }
0080 
0081     QSignalSpy *errorSpy[MAXCOUNT];
0082     QSignalSpy *finishedSpy[MAXCOUNT];
0083     for (int i = 0; i < count; i++) {
0084         qDebug() << "Starting process" << i + 1 << "of" << count;
0085         KProcess *proc = new KProcess;
0086         procs.append(proc);
0087         proc->setProgram(QStringLiteral(REQUESTER_EXE));
0088         errorSpy[i] = new QSignalSpy(proc, SIGNAL(error(QProcess::ProcessError)));
0089         finishedSpy[i] = new QSignalSpy(proc, SIGNAL(finished(int, QProcess::ExitStatus)));
0090         proc->start();
0091         QTest::qWait(delay);
0092     }
0093     qDebug() << "Launched" << count << "processes.";
0094 
0095     int seconds = 0;
0096     int error, finished;
0097     while (true) {
0098         seconds++;
0099         QTest::qWait(1000);
0100 
0101         error = 0;
0102         finished = 0;
0103         for (int i = 0; i < count; i++) {
0104             if (errorSpy[i]->count() > 0) {
0105                 error++;
0106             }
0107             if (finishedSpy[i]->count() > 0) {
0108                 finished++;
0109             }
0110         }
0111         qDebug() << seconds << "seconds elapsed." << error << "processes error'd," << finished << "processes finished.";
0112 
0113         if (error + finished >= count) {
0114             break;
0115         }
0116 
0117 #if 0
0118         if (seconds >= TIMEOUT_SECONDS) {
0119             qDebug() << "Timeout, gdb master!";
0120             QTest::qWait(1000 * 1000);
0121         }
0122 #endif
0123         QVERIFY2(seconds < TIMEOUT_SECONDS, "Timeout");
0124     }
0125 
0126     QCOMPARE(error, 0);
0127     QCOMPARE(finished, count);
0128     for (int i = 0; i < count; i++) {
0129         qDebug() << "Checking exit status of process" << i + 1 << "of" << count;
0130         QCOMPARE(finishedSpy[i]->count(), 1);
0131         QList<QVariant> args = finishedSpy[i]->takeFirst();
0132         if (args[0].toInt() != 2) {
0133             qDebug() << "Exit status" << args[0].toInt() << ", expected 2. Timeout, gdb master!";
0134             QTest::qWait(1000 * 1000);
0135         }
0136         QCOMPARE(args[0].toInt(), 2);
0137     }
0138 
0139     while (!procs.isEmpty()) {
0140         KProcess *proc = procs.takeFirst();
0141         QCOMPARE(proc->exitStatus(), QProcess::NormalExit);
0142         QCOMPARE(proc->exitCode(), 2);
0143         delete proc;
0144     }
0145     QVERIFY(procs.isEmpty());
0146 }
0147 
0148 void RaceTest::killZombies()
0149 {
0150     while (!procs.isEmpty()) {
0151         // These processes probably hung, and will never recover, so we need to kill them.
0152         // (This happens if the last test failed.)
0153         qDebug() << "Killing zombies from the past.";
0154         KProcess *proc = procs.takeFirst();
0155         proc->kill();
0156         proc->deleteLater();
0157     }
0158 }
0159 
0160 QTEST_AKONADIMAIN(RaceTest)
0161 
0162 #include "moc_racetest.cpp"