File indexing completed on 2024-05-19 04:39:58

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2014 Milian Wolff <mail@milianw.de>
0004     SPDX-FileCopyrightText: 2023 Igor Kushnir <igorkuo@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "ksequentialcompoundjobtest.h"
0010 
0011 #include "ksimplesequentialcompoundjob.h"
0012 
0013 #include <QSignalSpy>
0014 #include <QStandardPaths>
0015 #include <QTest>
0016 
0017 #include <memory>
0018 #include <vector>
0019 
0020 using namespace KDevCoreAddons;
0021 
0022 class PublicSequentialCompoundJob : public KSequentialCompoundJob
0023 {
0024 public:
0025     using KSequentialCompoundJob::addSubjob;
0026     using KSequentialCompoundJob::clearSubjobs;
0027     using KSequentialCompoundJob::removeSubjob;
0028     using KSequentialCompoundJob::subjobs;
0029 };
0030 
0031 struct JobSpy {
0032     explicit JobSpy(const KJob *job)
0033         : infoMessage(job, &KJob::infoMessage)
0034         , percentChanged(job, &KJob::percentChanged)
0035         , finished(job, &KJob::finished)
0036         , result(job, &KJob::result)
0037     {
0038     }
0039 
0040     QSignalSpy infoMessage;
0041     QSignalSpy percentChanged;
0042     QSignalSpy finished;
0043     QSignalSpy result;
0044 };
0045 
0046 struct KillableTestJobSpy : public JobSpy {
0047     explicit KillableTestJobSpy(KillableTestJob *job)
0048         : JobSpy(job)
0049         , started(job, &KillableTestJob::started)
0050         , killed(job, &KillableTestJob::killed)
0051         , job(job)
0052     {
0053     }
0054 
0055     QSignalSpy started;
0056     QSignalSpy killed;
0057     QPointer<KillableTestJob> job;
0058 };
0059 
0060 std::vector<std::unique_ptr<KillableTestJobSpy>> generateKillableTestJobSpies(int subjobCount)
0061 {
0062     std::vector<std::unique_ptr<KillableTestJobSpy>> jobSpies;
0063     for (int j = 0; j < subjobCount; ++j) {
0064         jobSpies.push_back(std::make_unique<KillableTestJobSpy>(new KillableTestJob));
0065     }
0066     return jobSpies;
0067 }
0068 
0069 void KSequentialCompoundJobTest::initTestCase()
0070 {
0071     QStandardPaths::setTestModeEnabled(true);
0072 }
0073 
0074 void KSequentialCompoundJobTest::runZeroJobs()
0075 {
0076     QPointer master(new KSimpleSequentialCompoundJob);
0077     JobSpy masterSpy(master);
0078 
0079     QCOMPARE(masterSpy.finished.count(), 0);
0080     QCOMPARE(masterSpy.result.count(), 0);
0081     master->start();
0082     QCOMPARE(masterSpy.finished.count(), 1);
0083     QCOMPARE(masterSpy.result.count(), 1);
0084 
0085     QTest::qWait(1);
0086     QVERIFY(!master);
0087 }
0088 
0089 void KSequentialCompoundJobTest::runOneJob()
0090 {
0091     QPointer slave(new TestJob);
0092 
0093     QPointer master(new KSimpleSequentialCompoundJob);
0094     QVERIFY(master->addSubjob(slave));
0095     JobSpy masterSpy(master);
0096 
0097     QSignalSpy startedSpy(slave, &TestJob::started);
0098     JobSpy slaveSpy(slave);
0099 
0100     master->start();
0101     QCOMPARE(startedSpy.count(), 1);
0102     QCOMPARE(slaveSpy.finished.count(), 0);
0103     QCOMPARE(masterSpy.finished.count(), 0);
0104 
0105     QCOMPARE(masterSpy.percentChanged.count(), 0);
0106     QCOMPARE(master->percent(), 0);
0107 
0108     slave->emitResult();
0109 
0110     QCOMPARE(masterSpy.percentChanged.count(), 1);
0111     QCOMPARE(master->percent(), 100);
0112 
0113     QCOMPARE(masterSpy.finished.count(), 1);
0114     QCOMPARE(masterSpy.result.count(), 1);
0115 
0116     QCOMPARE(startedSpy.count(), 1);
0117     QCOMPARE(slaveSpy.finished.count(), 1);
0118     QCOMPARE(slaveSpy.result.count(), 1);
0119 
0120     QTest::qWait(1);
0121 
0122     QVERIFY(!slave);
0123     QVERIFY(!master);
0124 
0125     QCOMPARE(slaveSpy.percentChanged.count(), 0);
0126     QCOMPARE(masterSpy.percentChanged.count(), 1);
0127 }
0128 
0129 void KSequentialCompoundJobTest::runTwoJobs()
0130 {
0131     QPointer slave1(new TestJob);
0132     QPointer slave2(new TestJob);
0133 
0134     QPointer master(new KSimpleSequentialCompoundJob);
0135     QVERIFY(master->addSubjob(slave1));
0136     QVERIFY(master->addSubjob(slave2));
0137     JobSpy masterSpy(master);
0138 
0139     QSignalSpy started1Spy(slave1, &TestJob::started);
0140     QSignalSpy started2Spy(slave2, &TestJob::started);
0141     JobSpy slave1Spy(slave1);
0142     JobSpy slave2Spy(slave2);
0143 
0144     master->start();
0145     QCOMPARE(started1Spy.count(), 1);
0146     QCOMPARE(slave1Spy.finished.count(), 0);
0147     QCOMPARE(started2Spy.count(), 0);
0148     QCOMPARE(slave2Spy.finished.count(), 0);
0149     QCOMPARE(masterSpy.finished.count(), 0);
0150 
0151     QCOMPARE(masterSpy.percentChanged.count(), 0);
0152     QCOMPARE(master->percent(), 0);
0153 
0154     slave1->emitResult();
0155 
0156     QCOMPARE(masterSpy.percentChanged.count(), 1);
0157     QCOMPARE(master->percent(), 50);
0158 
0159     QCOMPARE(started1Spy.count(), 1);
0160     QCOMPARE(slave1Spy.finished.count(), 1);
0161     QCOMPARE(started2Spy.count(), 1);
0162     QCOMPARE(slave2Spy.finished.count(), 0);
0163     QCOMPARE(masterSpy.finished.count(), 0);
0164 
0165     slave2->emitResult();
0166 
0167     QCOMPARE(masterSpy.percentChanged.count(), 2);
0168     QCOMPARE(master->percent(), 100);
0169 
0170     QCOMPARE(masterSpy.finished.count(), 1);
0171     QCOMPARE(masterSpy.result.count(), 1);
0172 
0173     QCOMPARE(started1Spy.count(), 1);
0174     QCOMPARE(slave1Spy.finished.count(), 1);
0175     QCOMPARE(slave1Spy.result.count(), 1);
0176 
0177     QCOMPARE(started2Spy.count(), 1);
0178     QCOMPARE(slave2Spy.finished.count(), 1);
0179     QCOMPARE(slave2Spy.result.count(), 1);
0180 
0181     QTest::qWait(1);
0182 
0183     QVERIFY(!slave1);
0184     QVERIFY(!slave2);
0185     QVERIFY(!master);
0186 
0187     QCOMPARE(slave1Spy.percentChanged.count(), 0);
0188     QCOMPARE(slave2Spy.percentChanged.count(), 0);
0189     QCOMPARE(masterSpy.percentChanged.count(), 2);
0190 }
0191 
0192 void KSequentialCompoundJobTest::addRemoveClearSubjob_data()
0193 {
0194     QTest::addColumn<bool>("shouldClearSubjobs");
0195     QTest::newRow("removeSubjob") << false;
0196     QTest::newRow("clearSubjobs") << true;
0197 }
0198 
0199 void KSequentialCompoundJobTest::addRemoveClearSubjob()
0200 {
0201     QPointer compoundJob(new PublicSequentialCompoundJob);
0202     JobSpy compoundSpy(compoundJob);
0203     QVERIFY(!compoundJob->addSubjob(nullptr));
0204     QVERIFY(!compoundJob->removeSubjob(nullptr));
0205 
0206     QPointer subjob(new TestJob);
0207     JobSpy subjobSpy(subjob);
0208     QCOMPARE(subjob->parent(), nullptr);
0209     QVERIFY(!compoundJob->removeSubjob(subjob));
0210 
0211     subjob->emitInfoMessage("hi");
0212     QCOMPARE(subjobSpy.infoMessage.count(), 1);
0213     subjob->setPercent(10);
0214     QCOMPARE(subjobSpy.percentChanged.count(), 1);
0215 
0216     QCOMPARE(compoundJob->subjobs(), {});
0217     QVERIFY(compoundJob->addSubjob(subjob));
0218     QCOMPARE(compoundJob->subjobs(), {subjob});
0219 
0220     QCOMPARE(subjob->parent(), compoundJob);
0221     QVERIFY(!compoundJob->addSubjob(subjob));
0222     QCOMPARE(subjob->parent(), compoundJob);
0223 
0224     QCOMPARE(compoundSpy.infoMessage.count(), 0);
0225     QCOMPARE(compoundSpy.percentChanged.count(), 0);
0226 
0227     subjob->emitInfoMessage("there");
0228     QCOMPARE(subjobSpy.infoMessage.count(), 2);
0229     QCOMPARE(compoundSpy.infoMessage.count(), 1);
0230     QCOMPARE(compoundSpy.infoMessage.constLast().at(0).value<KJob *>(), subjob);
0231     QCOMPARE(compoundSpy.infoMessage.constLast().at(1), "there");
0232 
0233     subjob->setPercent(25);
0234     QCOMPARE(subjobSpy.percentChanged.count(), 2);
0235     QCOMPARE(compoundSpy.percentChanged.count(), 0); // the subjob is not started, its percent changes are ignored
0236 
0237     compoundJob->start();
0238     subjob->setPercent(45);
0239     QCOMPARE(subjobSpy.percentChanged.count(), 3);
0240     QCOMPARE(compoundSpy.percentChanged.count(), 1);
0241     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0242     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), 45);
0243     QCOMPARE(compoundJob->percent(), 45);
0244 
0245     QVERIFY(!compoundJob->removeSubjob(nullptr));
0246     QCOMPARE(compoundJob->subjobs(), {subjob});
0247     QCOMPARE(subjob->parent(), compoundJob);
0248 
0249     QFETCH(const bool, shouldClearSubjobs);
0250     if (shouldClearSubjobs) {
0251         compoundJob->clearSubjobs();
0252     } else {
0253         QVERIFY(compoundJob->removeSubjob(subjob));
0254     }
0255     QCOMPARE(compoundJob->subjobs(), {});
0256     QCOMPARE(subjob->parent(), nullptr);
0257     QVERIFY(!compoundJob->removeSubjob(subjob));
0258 
0259     subjob->emitInfoMessage("!");
0260     QCOMPARE(subjobSpy.infoMessage.count(), 3);
0261     subjob->setPercent(91);
0262     QCOMPARE(subjobSpy.percentChanged.count(), 4);
0263 
0264     QCOMPARE(subjobSpy.finished.count(), 0);
0265     QCOMPARE(subjobSpy.result.count(), 0);
0266     subjob->emitResult();
0267     QCOMPARE(subjobSpy.finished.count(), 1);
0268     QCOMPARE(subjobSpy.result.count(), 1);
0269 
0270     QVERIFY(subjob);
0271     QTest::qWait(1);
0272     QVERIFY(!subjob);
0273 
0274     QVERIFY(compoundJob);
0275     QCOMPARE(compoundSpy.finished.count(), 0);
0276     delete compoundJob;
0277     QCOMPARE(compoundSpy.finished.count(), 1);
0278 
0279     QCOMPARE(compoundSpy.infoMessage.count(), 1);
0280     QCOMPARE(compoundSpy.percentChanged.count(), 1);
0281     QCOMPARE(compoundSpy.result.count(), 0);
0282 }
0283 
0284 void KSequentialCompoundJobTest::addClearSubjobs()
0285 {
0286     QPointer compoundJob(new PublicSequentialCompoundJob);
0287     JobSpy compoundSpy(compoundJob);
0288 
0289     QPointer subjob1(new TestJob);
0290     JobSpy subjob1Spy(subjob1);
0291     QSignalSpy started1Spy(subjob1, &TestJob::started);
0292     QPointer subjob2(new TestJob);
0293     JobSpy subjob2Spy(subjob2);
0294     QSignalSpy started2Spy(subjob2, &TestJob::started);
0295 
0296     QVERIFY(compoundJob->addSubjob(subjob1));
0297     QVERIFY(!compoundJob->removeSubjob(subjob2));
0298     QVERIFY(compoundJob->addSubjob(subjob2));
0299     const QList<KJob *> expectedSubjobList{subjob1, subjob2};
0300     QCOMPARE(compoundJob->subjobs(), expectedSubjobList);
0301     QCOMPARE(subjob1->parent(), compoundJob);
0302     QCOMPARE(subjob2->parent(), compoundJob);
0303 
0304     QVERIFY(!compoundJob->addSubjob(subjob2));
0305     QVERIFY(!compoundJob->addSubjob(subjob1));
0306     QCOMPARE(compoundJob->subjobs(), expectedSubjobList);
0307 
0308     subjob1->emitInfoMessage("take");
0309     QCOMPARE(subjob1Spy.infoMessage.count(), 1);
0310     QCOMPARE(compoundSpy.infoMessage.count(), 1);
0311     QCOMPARE(compoundSpy.infoMessage.constLast().at(0).value<KJob *>(), subjob1);
0312     QCOMPARE(compoundSpy.infoMessage.constLast().at(1), "take");
0313 
0314     subjob2->emitInfoMessage("that");
0315     QCOMPARE(subjob2Spy.infoMessage.count(), 1);
0316     QCOMPARE(compoundSpy.infoMessage.count(), 2);
0317     QCOMPARE(compoundSpy.infoMessage.constLast().at(0).value<KJob *>(), subjob2);
0318     QCOMPARE(compoundSpy.infoMessage.constLast().at(1), "that");
0319 
0320     subjob1->setPercent(31);
0321     QCOMPARE(subjob1Spy.percentChanged.count(), 1);
0322     subjob2->setPercent(12);
0323     QCOMPARE(subjob2Spy.percentChanged.count(), 1);
0324     QCOMPARE(compoundSpy.percentChanged.count(), 0); // the subjobs are not started, their percent changes are ignored
0325 
0326     QCOMPARE(started1Spy.count(), 0);
0327     compoundJob->start();
0328     QCOMPARE(started1Spy.count(), 1);
0329     QCOMPARE(started2Spy.count(), 0);
0330 
0331     subjob1->setPercent(52);
0332     QCOMPARE(subjob1Spy.percentChanged.count(), 2);
0333     QCOMPARE(compoundSpy.percentChanged.count(), 1);
0334     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0335     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), 26);
0336     QCOMPARE(compoundJob->percent(), 26);
0337 
0338     subjob2->setPercent(95);
0339     QCOMPARE(subjob2Spy.percentChanged.count(), 2);
0340     QCOMPARE(compoundSpy.percentChanged.count(), 1); // the 2nd subjob is not started, its percent changes are ignored
0341     QCOMPARE(compoundJob->percent(), 26);
0342 
0343     subjob1->setPercent(83);
0344     QCOMPARE(subjob1Spy.percentChanged.count(), 3);
0345     QCOMPARE(compoundSpy.percentChanged.count(), 2);
0346     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0347     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), 41);
0348     QCOMPARE(compoundJob->percent(), 41);
0349 
0350     QVERIFY(!compoundJob->removeSubjob(nullptr));
0351     QCOMPARE(compoundJob->subjobs(), expectedSubjobList);
0352     compoundJob->clearSubjobs();
0353     QCOMPARE(compoundJob->subjobs(), {});
0354     QCOMPARE(subjob1->parent(), nullptr);
0355     QCOMPARE(subjob2->parent(), nullptr);
0356     QVERIFY(!compoundJob->removeSubjob(nullptr));
0357     QVERIFY(!compoundJob->removeSubjob(subjob2));
0358     QVERIFY(!compoundJob->removeSubjob(subjob1));
0359     QCOMPARE(compoundJob->subjobs(), {});
0360 
0361     subjob1->emitInfoMessage("lost");
0362     subjob2->emitInfoMessage("too");
0363     subjob1->setPercent(99);
0364     subjob2->setPercent(100);
0365     QCOMPARE(compoundSpy.infoMessage.count(), 2);
0366     QCOMPARE(compoundSpy.percentChanged.count(), 2);
0367     QCOMPARE(compoundJob->percent(), 41);
0368 
0369     delete subjob1;
0370     delete subjob2;
0371     QTest::qWait(1);
0372     QVERIFY(compoundJob);
0373 
0374     QCOMPARE(compoundSpy.finished.count(), 0);
0375     QVERIFY(compoundJob->kill());
0376     QCOMPARE(compoundSpy.finished.count(), 1);
0377 
0378     QVERIFY(compoundJob);
0379     QTest::qWait(1);
0380     QVERIFY(!compoundJob);
0381 
0382     QCOMPARE(compoundSpy.infoMessage.count(), 2);
0383     QCOMPARE(compoundSpy.percentChanged.count(), 2);
0384     QCOMPARE(compoundSpy.finished.count(), 1);
0385     QCOMPARE(compoundSpy.result.count(), 0);
0386 }
0387 
0388 void KSequentialCompoundJobTest::subjobPercentChanged()
0389 {
0390     QPointer compoundJob(new KSimpleSequentialCompoundJob);
0391     JobSpy compoundSpy(compoundJob);
0392 
0393     QPointer subjob1(new TestJob);
0394     JobSpy subjob1Spy(subjob1);
0395     QSignalSpy started1Spy(subjob1, &TestJob::started);
0396     QPointer subjob2(new TestJob);
0397     JobSpy subjob2Spy(subjob2);
0398     QSignalSpy started2Spy(subjob2, &TestJob::started);
0399     QPointer subjob3(new TestJob);
0400     JobSpy subjob3Spy(subjob3);
0401     QSignalSpy started3Spy(subjob3, &TestJob::started);
0402 
0403     QVERIFY(compoundJob->addSubjob(subjob1));
0404     QVERIFY(compoundJob->addSubjob(subjob2));
0405     QVERIFY(compoundJob->addSubjob(subjob3));
0406 
0407     QCOMPARE(started1Spy.count(), 0);
0408     compoundJob->start();
0409     QCOMPARE(started1Spy.count(), 1);
0410     QCOMPARE(started2Spy.count(), 0);
0411 
0412     subjob1->setPercent(33);
0413     QCOMPARE(subjob1Spy.percentChanged.count(), 1);
0414     QCOMPARE(compoundSpy.percentChanged.count(), 1);
0415     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0416     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), 33 / 3);
0417     QCOMPARE(compoundJob->percent(), 11);
0418 
0419     subjob1->setPercent(88);
0420     QCOMPARE(subjob1Spy.percentChanged.count(), 2);
0421     QCOMPARE(compoundSpy.percentChanged.count(), 2);
0422     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0423     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), 88 / 3);
0424     QCOMPARE(compoundJob->percent(), 29);
0425 
0426     subjob1->emitResult();
0427     QCOMPARE(subjob1Spy.percentChanged.count(), 2);
0428     QCOMPARE(compoundSpy.percentChanged.count(), 3);
0429     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0430     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), 100 / 3);
0431     QCOMPARE(compoundJob->percent(), 33);
0432 
0433     QCOMPARE(started1Spy.count(), 1);
0434     QCOMPARE(subjob1Spy.finished.count(), 1);
0435     QCOMPARE(started2Spy.count(), 1);
0436     QCOMPARE(started3Spy.count(), 0);
0437 
0438     subjob2->setPercent(4);
0439     QCOMPARE(subjob2Spy.percentChanged.count(), 1);
0440     QCOMPARE(compoundSpy.percentChanged.count(), 4);
0441     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0442     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), (100 + 4) / 3);
0443     QCOMPARE(compoundJob->percent(), 34);
0444 
0445     subjob2->setPercent(5);
0446     QCOMPARE(subjob2Spy.percentChanged.count(), 2);
0447     QCOMPARE(compoundSpy.percentChanged.count(), 5);
0448     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0449     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), (100 + 5) / 3);
0450     QCOMPARE(compoundJob->percent(), 35);
0451 
0452     subjob2->setPercent(7);
0453     QCOMPARE(subjob2Spy.percentChanged.count(), 3);
0454     QCOMPARE(compoundSpy.percentChanged.count(), 5); // the total percent value hasn't changed
0455     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0456     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), (100 + 7) / 3);
0457     QCOMPARE(compoundJob->percent(), 35);
0458 
0459     subjob2->setPercent(99);
0460     QCOMPARE(subjob2Spy.percentChanged.count(), 4);
0461     QCOMPARE(compoundSpy.percentChanged.count(), 6);
0462     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0463     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), (100 + 99) / 3);
0464     QCOMPARE(compoundJob->percent(), 66);
0465 
0466     subjob2->emitResult();
0467     QCOMPARE(subjob2Spy.percentChanged.count(), 4);
0468     QCOMPARE(compoundSpy.percentChanged.count(), 6); // the total percent value hasn't changed
0469     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0470     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), 2 * 100 / 3);
0471     QCOMPARE(compoundJob->percent(), 66);
0472 
0473     QCOMPARE(started2Spy.count(), 1);
0474     QCOMPARE(subjob2Spy.finished.count(), 1);
0475     QCOMPARE(started3Spy.count(), 1);
0476 
0477     subjob3->setPercent(50);
0478     QCOMPARE(subjob3Spy.percentChanged.count(), 1);
0479     QCOMPARE(compoundSpy.percentChanged.count(), 7);
0480     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0481     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), (200 + 50) / 3);
0482     QCOMPARE(compoundJob->percent(), 83);
0483 
0484     delete subjob3;
0485     QCOMPARE(subjob3Spy.percentChanged.count(), 1);
0486     QCOMPARE(compoundSpy.percentChanged.count(), 8);
0487     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0488     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), 3 * 100 / 3);
0489     QCOMPARE(compoundJob->percent(), 100);
0490 
0491     QCOMPARE(started3Spy.count(), 1);
0492     QCOMPARE(subjob3Spy.finished.count(), 1);
0493 
0494     QTest::qWait(1);
0495     QVERIFY(!subjob1);
0496     QVERIFY(!subjob2);
0497     QVERIFY(!subjob3);
0498     QVERIFY(!compoundJob);
0499 
0500     QCOMPARE(compoundSpy.percentChanged.count(), 8);
0501     QCOMPARE(compoundSpy.finished.count(), 1);
0502     QCOMPARE(compoundSpy.result.count(), 1);
0503 }
0504 
0505 void KSequentialCompoundJobTest::abortOnSubjobError()
0506 {
0507     QPointer compoundJob(new PublicSequentialCompoundJob);
0508     JobSpy compoundSpy(compoundJob);
0509 
0510     QPointer subjob1(new TestJob);
0511     QPointer subjob2(new TestJob);
0512     JobSpy subjob2Spy(subjob2);
0513     QSignalSpy started2Spy(subjob2, &TestJob::started);
0514 
0515     QVERIFY(compoundJob->addSubjob(subjob1));
0516     QVERIFY(compoundJob->addSubjob(subjob2));
0517     const QList<KJob *> expectedSubjobList{subjob1, subjob2};
0518     QCOMPARE(compoundJob->subjobs(), expectedSubjobList);
0519 
0520     compoundJob->start();
0521 
0522     QCOMPARE(started2Spy.count(), 0);
0523     subjob1->setError(244);
0524     subjob1->setErrorText("Crashed");
0525     subjob1->emitResult();
0526     QCOMPARE(compoundJob->error(), 244);
0527     QCOMPARE(compoundJob->errorText(), "Crashed");
0528     QCOMPARE(compoundSpy.finished.count(), 1);
0529     QCOMPARE(compoundSpy.result.count(), 1);
0530 
0531     QTest::qWait(1);
0532     QVERIFY(!subjob1);
0533     QVERIFY(!subjob2);
0534     QVERIFY(!compoundJob);
0535 
0536     QCOMPARE(started2Spy.count(), 0);
0537     QCOMPARE(subjob2Spy.finished.count(), 1);
0538     QCOMPARE(subjob2Spy.result.count(), 0);
0539 }
0540 
0541 void KSequentialCompoundJobTest::disableAbortOnSubjobError_data()
0542 {
0543     QTest::addColumn<bool>("lastSubjobError");
0544     QTest::newRow("last-subjob-no-error") << false;
0545     QTest::newRow("last-subjob-error") << true;
0546 }
0547 
0548 void KSequentialCompoundJobTest::disableAbortOnSubjobError()
0549 {
0550     QPointer compoundJob(new PublicSequentialCompoundJob);
0551     JobSpy compoundSpy(compoundJob);
0552     compoundJob->setAbortOnSubjobError(false);
0553 
0554     QPointer subjob1(new TestJob);
0555     QPointer subjob2(new TestJob);
0556     JobSpy subjob2Spy(subjob2);
0557     QSignalSpy started2Spy(subjob2, &TestJob::started);
0558 
0559     QVERIFY(compoundJob->addSubjob(subjob1));
0560     QVERIFY(compoundJob->addSubjob(subjob2));
0561     const QList<KJob *> expectedSubjobList{subjob1, subjob2};
0562     QCOMPARE(compoundJob->subjobs(), expectedSubjobList);
0563 
0564     compoundJob->start();
0565 
0566     QCOMPARE(started2Spy.count(), 0);
0567     subjob1->setError(105);
0568     subjob1->setErrorText("Failed");
0569     subjob1->emitResult();
0570     QCOMPARE(started2Spy.count(), 1);
0571     QCOMPARE(subjob1->parent(), nullptr);
0572     QCOMPARE(subjob2->parent(), compoundJob);
0573     QCOMPARE(compoundJob->subjobs(), {subjob2});
0574 
0575     QTest::qWait(1);
0576     QVERIFY(!subjob1);
0577 
0578     QCOMPARE(subjob2Spy.finished.count(), 0);
0579     QCOMPARE(subjob2Spy.result.count(), 0);
0580     QCOMPARE(compoundSpy.percentChanged.count(), 1);
0581     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0582     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), 50);
0583     QCOMPARE(compoundJob->percent(), 50);
0584     QCOMPARE(compoundJob->error(), 0);
0585     QCOMPARE(compoundJob->errorText(), "");
0586     QCOMPARE(compoundSpy.finished.count(), 0);
0587     QCOMPARE(compoundSpy.result.count(), 0);
0588 
0589     QFETCH(const bool, lastSubjobError);
0590     const int errorCode = lastSubjobError ? 902 : 0;
0591     const QString errorText = lastSubjobError ? "broken pipe" : "";
0592     if (lastSubjobError) {
0593         subjob2->setError(errorCode);
0594         subjob2->setErrorText(errorText);
0595     }
0596     subjob2->emitResult();
0597     QCOMPARE(compoundSpy.percentChanged.count(), 2);
0598     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0599     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), 100);
0600     QCOMPARE(compoundJob->percent(), 100);
0601     QCOMPARE(compoundJob->error(), errorCode);
0602     QCOMPARE(compoundJob->errorText(), errorText);
0603     QCOMPARE(compoundSpy.finished.count(), 1);
0604     QCOMPARE(compoundSpy.result.count(), 1);
0605 
0606     QTest::qWait(1);
0607     QVERIFY(!subjob2);
0608     QVERIFY(!compoundJob);
0609 }
0610 
0611 void KSequentialCompoundJobTest::finishWrongSubjob_data()
0612 {
0613     QTest::addColumn<bool>("abortOnSubjobError");
0614     QTest::newRow("abort-on-subjob-error") << true;
0615     QTest::newRow("no-abort-on-subjob-error") << false;
0616 }
0617 
0618 void KSequentialCompoundJobTest::finishWrongSubjob()
0619 {
0620     QPointer compoundJob(new PublicSequentialCompoundJob);
0621     JobSpy compoundSpy(compoundJob);
0622 
0623     QFETCH(const bool, abortOnSubjobError);
0624     compoundJob->setAbortOnSubjobError(abortOnSubjobError);
0625 
0626     QPointer subjob1(new TestJob);
0627     QPointer subjob2(new TestJob);
0628     JobSpy subjob2Spy(subjob2);
0629     QSignalSpy started2Spy(subjob2, &TestJob::started);
0630 
0631     QVERIFY(compoundJob->addSubjob(subjob1));
0632     QVERIFY(compoundJob->addSubjob(subjob2));
0633     const QList<KJob *> expectedSubjobList{subjob1, subjob2};
0634     QCOMPARE(compoundJob->subjobs(), expectedSubjobList);
0635 
0636     compoundJob->start();
0637 
0638     subjob2->setError(543);
0639     subjob2->setErrorText("Canceled");
0640     subjob2->emitResult();
0641     QCOMPARE(subjob2->error(), 543);
0642     QCOMPARE(subjob2->errorText(), "Canceled");
0643     QCOMPARE(started2Spy.count(), 0);
0644     QCOMPARE(subjob2Spy.finished.count(), 1);
0645     QCOMPARE(subjob2Spy.result.count(), 1);
0646     // when an unstarted job finishes, sequential compound job simply removes it
0647     QCOMPARE(subjob2->parent(), nullptr);
0648     QCOMPARE(compoundJob->subjobs(), {subjob1});
0649 
0650     QTest::qWait(1);
0651     QVERIFY(!subjob2);
0652 
0653     QCOMPARE(compoundJob->error(), 0);
0654     QCOMPARE(compoundJob->errorText(), "");
0655     QCOMPARE(compoundSpy.finished.count(), 0);
0656     QCOMPARE(compoundSpy.result.count(), 0);
0657 
0658     subjob1->emitResult();
0659     QCOMPARE(subjob1->parent(), nullptr);
0660     QCOMPARE(compoundJob->subjobs(), {});
0661     QCOMPARE(compoundJob->error(), 0);
0662     QCOMPARE(compoundJob->errorText(), "");
0663     QCOMPARE(compoundSpy.finished.count(), 1);
0664     QCOMPARE(compoundSpy.result.count(), 1);
0665 
0666     // These percent checks do not test expected behavior, but demonstrate what the current behavior is.
0667     // compoundSpy.percentChanged.count() == 2 and compoundJob->percent() == 100 may be more correct/useful.
0668     QCOMPARE(compoundSpy.percentChanged.count(), 1);
0669     QCOMPARE(compoundSpy.percentChanged.constLast().at(0).value<KJob *>(), compoundJob);
0670     QCOMPARE(compoundSpy.percentChanged.constLast().at(1), 50);
0671     QCOMPARE(compoundJob->percent(), 50);
0672 
0673     QTest::qWait(1);
0674     QVERIFY(!subjob1);
0675     QVERIFY(!compoundJob);
0676 }
0677 
0678 void KSequentialCompoundJobTest::killUnstartedCompoundJob_data()
0679 {
0680     QTest::addColumn<int>("subjobCount");
0681     QTest::addColumn<KJob::KillVerbosity>("killVerbosity");
0682     for (int subjobCount : {0, 1, 4}) {
0683         QTest::addRow("kill-quietly-%d-subjobs", subjobCount) << subjobCount << KJob::Quietly;
0684         QTest::addRow("emit-result-%d-subjobs", subjobCount) << subjobCount << KJob::EmitResult;
0685     }
0686 }
0687 
0688 void KSequentialCompoundJobTest::killUnstartedCompoundJob()
0689 {
0690     QPointer compoundJob(new KSimpleSequentialCompoundJob);
0691     JobSpy compoundSpy(compoundJob);
0692     QVERIFY(compoundJob->capabilities() & KJob::Killable);
0693 
0694     QFETCH(const int, subjobCount);
0695     auto subjobSpies = generateKillableTestJobSpies(subjobCount);
0696     for (const auto &spy : subjobSpies) {
0697         QVERIFY(compoundJob->addSubjob(spy->job));
0698     }
0699 
0700     QCOMPARE(compoundSpy.finished.count(), 0);
0701     QFETCH(const KJob::KillVerbosity, killVerbosity);
0702     QVERIFY(compoundJob->kill(killVerbosity));
0703     QCOMPARE(compoundSpy.finished.count(), 1);
0704     QCOMPARE(compoundSpy.result.count(), killVerbosity == KJob::EmitResult);
0705     QCOMPARE(compoundJob->error(), KJob::KilledJobError);
0706 
0707     QVERIFY(compoundJob);
0708     for (const auto &spy : subjobSpies) {
0709         QVERIFY(spy->job);
0710         // no need to kill unstarted subjobs, just destroy them
0711         QCOMPARE(spy->job->error(), 0);
0712         QCOMPARE(spy->job->errorText(), "");
0713     }
0714     QTest::qWait(1);
0715     QVERIFY(!compoundJob);
0716     for (const auto &spy : subjobSpies) {
0717         QVERIFY(!spy->job);
0718         QCOMPARE(spy->started.count(), 0);
0719         QCOMPARE(spy->killed.count(), 0); // no need to kill unstarted subjobs, just destroy them
0720         QCOMPARE(spy->finished.count(), 1);
0721         QCOMPARE(spy->result.count(), 0);
0722     }
0723 }
0724 
0725 void KSequentialCompoundJobTest::killFinishedCompoundJob_data()
0726 {
0727     killUnstartedCompoundJob_data();
0728 }
0729 
0730 void KSequentialCompoundJobTest::killFinishedCompoundJob()
0731 {
0732     QPointer compoundJob(new KSimpleSequentialCompoundJob);
0733     JobSpy compoundSpy(compoundJob);
0734     QVERIFY(compoundJob->capabilities() & KJob::Killable);
0735 
0736     QFETCH(const int, subjobCount);
0737     auto subjobSpies = generateKillableTestJobSpies(subjobCount);
0738     for (const auto &spy : subjobSpies) {
0739         QVERIFY(compoundJob->addSubjob(spy->job));
0740     }
0741 
0742     compoundJob->start();
0743     for (std::size_t i = 0; i < subjobSpies.size(); ++i) {
0744         QCOMPARE(compoundSpy.finished.count(), 0);
0745         auto &spy = subjobSpies[i];
0746         QCOMPARE(spy->started.count(), 1);
0747         QCOMPARE(spy->finished.count(), 0);
0748         QCOMPARE(spy->result.count(), 0);
0749         spy->job->emitResult();
0750         QCOMPARE(spy->started.count(), 1);
0751         QCOMPARE(spy->finished.count(), 1);
0752         QCOMPARE(spy->result.count(), 1);
0753     }
0754     QCOMPARE(compoundSpy.finished.count(), 1);
0755     QCOMPARE(compoundSpy.result.count(), 1);
0756 
0757     QFETCH(const KJob::KillVerbosity, killVerbosity);
0758     QVERIFY(compoundJob->kill(killVerbosity));
0759     QCOMPARE(compoundSpy.finished.count(), 1);
0760     QCOMPARE(compoundSpy.result.count(), 1);
0761     QCOMPARE(compoundJob->error(), 0);
0762     QCOMPARE(compoundJob->errorText(), "");
0763 
0764     QVERIFY(compoundJob);
0765     for (const auto &spy : subjobSpies) {
0766         QVERIFY(spy->job);
0767         QCOMPARE(spy->job->error(), 0);
0768         QCOMPARE(spy->job->errorText(), "");
0769     }
0770     QTest::qWait(1);
0771     QVERIFY(!compoundJob);
0772     for (const auto &spy : subjobSpies) {
0773         QVERIFY(!spy->job);
0774         QCOMPARE(spy->started.count(), 1);
0775         QCOMPARE(spy->killed.count(), 0);
0776         QCOMPARE(spy->finished.count(), 1);
0777         QCOMPARE(spy->result.count(), 1);
0778     }
0779 }
0780 
0781 void KSequentialCompoundJobTest::killRunningCompoundJob()
0782 {
0783     QPointer compoundJob(new KSimpleSequentialCompoundJob);
0784     JobSpy compoundSpy(compoundJob);
0785     QVERIFY(compoundJob->capabilities() & KJob::Killable);
0786 
0787     auto subjobSpies = generateKillableTestJobSpies(3);
0788     QCOMPARE(subjobSpies.size(), 3);
0789     for (const auto &spy : subjobSpies) {
0790         QVERIFY(compoundJob->addSubjob(spy->job));
0791     }
0792     const auto &firstSubjobSpy = *subjobSpies.front();
0793 
0794     compoundJob->start();
0795     QCOMPARE(firstSubjobSpy.started.count(), 1);
0796     QCOMPARE(firstSubjobSpy.finished.count(), 0);
0797     QCOMPARE(compoundSpy.finished.count(), 0);
0798 
0799     QVERIFY(compoundJob->kill());
0800     QCOMPARE(firstSubjobSpy.started.count(), 1);
0801     QCOMPARE(firstSubjobSpy.killed.count(), 1);
0802     QCOMPARE(firstSubjobSpy.killed.constLast().constFirst(), true);
0803     QCOMPARE(firstSubjobSpy.finished.count(), 1);
0804     QCOMPARE(firstSubjobSpy.result.count(), 0); // kill the running subjob quietly
0805     QCOMPARE(compoundSpy.finished.count(), 1);
0806     QCOMPARE(compoundSpy.result.count(), 0);
0807 
0808     QCOMPARE(firstSubjobSpy.job->error(), KJob::KilledJobError);
0809     QCOMPARE(compoundJob->error(), KJob::KilledJobError);
0810     QCOMPARE(compoundJob->errorText(), firstSubjobSpy.job->errorText());
0811 
0812     QVERIFY(compoundJob);
0813     for (const auto &spy : subjobSpies) {
0814         QVERIFY(spy->job);
0815         if (spy.get() != &firstSubjobSpy) {
0816             QCOMPARE(spy->job->error(), 0);
0817             QCOMPARE(spy->job->errorText(), "");
0818         }
0819     }
0820     QTest::qWait(1);
0821     QVERIFY(!compoundJob);
0822     for (const auto &spy : subjobSpies) {
0823         QVERIFY(!spy->job);
0824         const bool isFirstSubjob = spy.get() == &firstSubjobSpy;
0825         QCOMPARE(spy->started.count(), isFirstSubjob); // don't start subjobs once killed
0826         QCOMPARE(spy->killed.count(), isFirstSubjob); // no need to kill unstarted subjobs, just destroy them
0827         QCOMPARE(spy->finished.count(), 1);
0828         QCOMPARE(spy->result.count(), 0);
0829     }
0830 }
0831 
0832 void KSequentialCompoundJobTest::killingSubjobFails()
0833 {
0834     QPointer compoundJob(new PublicSequentialCompoundJob);
0835     JobSpy compoundSpy(compoundJob);
0836     QVERIFY(compoundJob->capabilities() & KJob::Killable);
0837 
0838     auto subjobSpies = generateKillableTestJobSpies(7);
0839     QCOMPARE(subjobSpies.size(), 7);
0840     for (const auto &spy : subjobSpies) {
0841         QVERIFY(compoundJob->addSubjob(spy->job));
0842     }
0843     const auto &firstSubjobSpy = *subjobSpies.front();
0844     firstSubjobSpy.job->setKillingSucceeds(false);
0845 
0846     compoundJob->start();
0847     QCOMPARE(firstSubjobSpy.started.count(), 1);
0848     QCOMPARE(firstSubjobSpy.finished.count(), 0);
0849     QCOMPARE(compoundSpy.finished.count(), 0);
0850 
0851     QVERIFY(!compoundJob->kill());
0852     QCOMPARE(firstSubjobSpy.started.count(), 1);
0853     QCOMPARE(firstSubjobSpy.killed.count(), 1);
0854     QCOMPARE(firstSubjobSpy.killed.constLast().constFirst(), false);
0855     QCOMPARE(firstSubjobSpy.finished.count(), 0);
0856     QCOMPARE(firstSubjobSpy.result.count(), 0);
0857     QCOMPARE(compoundSpy.finished.count(), 0);
0858     QCOMPARE(compoundJob->subjobs().count(), subjobSpies.size());
0859 
0860     QCOMPARE(firstSubjobSpy.job->error(), 0);
0861     QCOMPARE(firstSubjobSpy.job->errorText(), "");
0862     QCOMPARE(compoundJob->error(), 0);
0863     QCOMPARE(compoundJob->errorText(), "");
0864 
0865     // unstarted subjobs are simply removed when finished, their errors are ignored
0866 
0867     const auto &finishedSubjobSpy = *subjobSpies[3];
0868     finishedSubjobSpy.job->emitResult();
0869     QCOMPARE(finishedSubjobSpy.started.count(), 0);
0870     QCOMPARE(finishedSubjobSpy.finished.count(), 1);
0871     QCOMPARE(compoundSpy.finished.count(), 0);
0872     QCOMPARE(compoundJob->subjobs().count(), subjobSpies.size() - 1);
0873     QVERIFY(!compoundJob->subjobs().contains(finishedSubjobSpy.job));
0874 
0875     const auto &errorSubjobSpy = *subjobSpies[1];
0876     errorSubjobSpy.job->setError(142);
0877     errorSubjobSpy.job->emitResult();
0878     QCOMPARE(errorSubjobSpy.started.count(), 0);
0879     QCOMPARE(errorSubjobSpy.finished.count(), 1);
0880     QCOMPARE(compoundSpy.finished.count(), 0);
0881     QCOMPARE(compoundJob->subjobs().count(), subjobSpies.size() - 2);
0882     QVERIFY(!compoundJob->subjobs().contains(errorSubjobSpy.job));
0883 
0884     const auto &killedSubjobSpy = *subjobSpies[4];
0885     QVERIFY(killedSubjobSpy.job->kill());
0886     QCOMPARE(killedSubjobSpy.started.count(), 0);
0887     QCOMPARE(killedSubjobSpy.finished.count(), 1);
0888     QCOMPARE(killedSubjobSpy.result.count(), 0);
0889     QCOMPARE(compoundSpy.finished.count(), 0);
0890     QCOMPARE(compoundJob->subjobs().count(), subjobSpies.size() - 3);
0891     QVERIFY(!compoundJob->subjobs().contains(killedSubjobSpy.job));
0892 
0893     QCOMPARE(compoundJob->error(), 0);
0894     QCOMPARE(compoundJob->errorText(), "");
0895 
0896     firstSubjobSpy.job->emitResult();
0897     QCOMPARE(firstSubjobSpy.started.count(), 1);
0898     QCOMPARE(firstSubjobSpy.killed.count(), 1);
0899     QCOMPARE(firstSubjobSpy.finished.count(), 1);
0900     QCOMPARE(firstSubjobSpy.result.count(), 1);
0901     QCOMPARE(compoundSpy.finished.count(), 1);
0902     QCOMPARE(compoundSpy.result.count(), 1);
0903 
0904     QCOMPARE(firstSubjobSpy.job->error(), 0);
0905     // the compound job was killed, so it should report KilledJobError, even if the killed subjob does not
0906     QCOMPARE(compoundJob->error(), KJob::KilledJobError);
0907     QCOMPARE(compoundJob->errorText(), firstSubjobSpy.job->errorText());
0908 
0909     QVERIFY(compoundJob);
0910     for (const auto &spy : subjobSpies) {
0911         QVERIFY(spy->job);
0912     }
0913     QTest::qWait(1);
0914     QVERIFY(!compoundJob);
0915     for (const auto &spy : subjobSpies) {
0916         QVERIFY(!spy->job);
0917         const bool isFirstSubjob = spy.get() == &firstSubjobSpy;
0918         QCOMPARE(spy->started.count(), isFirstSubjob); // don't start subjobs once killed
0919         QCOMPARE(spy->killed.count(), isFirstSubjob || spy.get() == &killedSubjobSpy);
0920         QCOMPARE(spy->finished.count(), 1);
0921         QCOMPARE(spy->result.count(), isFirstSubjob || spy.get() == &finishedSubjobSpy || spy.get() == &errorSubjobSpy);
0922     }
0923 }
0924 
0925 void KSequentialCompoundJobTest::killRunningCompoundJobRepeatedly_data()
0926 {
0927     QTest::addColumn<int>("killAttempts");
0928     QTest::addColumn<int>("successfulKillAttempts");
0929     QTest::addColumn<bool>("destroyKilledSubjob");
0930     for (int killAttempts = 1; killAttempts <= 3; ++killAttempts) {
0931         for (int successfulKillAttempts = 0; successfulKillAttempts <= killAttempts; ++successfulKillAttempts) {
0932             QTest::addRow("attempts-%d-successful-%d", killAttempts, successfulKillAttempts) << killAttempts << successfulKillAttempts << false;
0933             if (successfulKillAttempts == 0) {
0934                 QTest::addRow("attempts-%d-successful-%d-destroy-killed-subjob", killAttempts, successfulKillAttempts)
0935                     << killAttempts << successfulKillAttempts << true;
0936             }
0937         }
0938     }
0939 }
0940 
0941 void KSequentialCompoundJobTest::killRunningCompoundJobRepeatedly()
0942 {
0943     QPointer compoundJob(new KSimpleSequentialCompoundJob);
0944     JobSpy compoundSpy(compoundJob);
0945     QVERIFY(compoundJob->capabilities() & KJob::Killable);
0946 
0947     // The number of subjobs is independent from and unrelated to the number of kill attempts.
0948     auto subjobSpies = generateKillableTestJobSpies(3);
0949     QCOMPARE(subjobSpies.size(), 3);
0950     for (const auto &spy : subjobSpies) {
0951         QVERIFY(compoundJob->addSubjob(spy->job));
0952     }
0953     const auto &firstSubjobSpy = *subjobSpies.front();
0954 
0955     compoundJob->start();
0956     QCOMPARE(firstSubjobSpy.started.count(), 1);
0957     QCOMPARE(firstSubjobSpy.finished.count(), 0);
0958     QCOMPARE(compoundSpy.finished.count(), 0);
0959 
0960     QFETCH(const int, killAttempts);
0961     QFETCH(const int, successfulKillAttempts);
0962     for (int attempt = 0; attempt < killAttempts; ++attempt) {
0963         const bool killingSucceeds = attempt >= killAttempts - successfulKillAttempts;
0964         firstSubjobSpy.job->setKillingSucceeds(killingSucceeds);
0965         QCOMPARE(compoundJob->kill(), killingSucceeds);
0966         // KJob::kill() calls KSequentialCompoundJob::doKill() only if the job has not finished,
0967         // i.e. if the previous killing failed.
0968         const int subjobKillCount = std::min(attempt, killAttempts - successfulKillAttempts) + 1;
0969 
0970         QCOMPARE(firstSubjobSpy.started.count(), 1);
0971         QCOMPARE(firstSubjobSpy.killed.count(), subjobKillCount);
0972         if (attempt < subjobKillCount) {
0973             QCOMPARE(firstSubjobSpy.killed.at(attempt).constFirst(), killingSucceeds);
0974         }
0975         QCOMPARE(firstSubjobSpy.finished.count(), killingSucceeds);
0976         QCOMPARE(firstSubjobSpy.result.count(), 0); // kill the running subjob quietly
0977         QCOMPARE(compoundSpy.finished.count(), killingSucceeds);
0978         QCOMPARE(compoundSpy.result.count(), 0);
0979 
0980         const auto error = killingSucceeds ? KJob::KilledJobError : KJob::NoError;
0981         QCOMPARE(firstSubjobSpy.job->error(), error);
0982         QCOMPARE(compoundJob->error(), error);
0983     }
0984 
0985     QFETCH(const bool, destroyKilledSubjob);
0986     if (destroyKilledSubjob) {
0987         delete firstSubjobSpy.job;
0988         QVERIFY(compoundJob);
0989         QCOMPARE(compoundSpy.result.count(), 1);
0990     } else if (successfulKillAttempts == 0) {
0991         delete compoundJob;
0992         QVERIFY(!compoundJob);
0993         QCOMPARE(compoundSpy.result.count(), 0);
0994     } else {
0995         QVERIFY(compoundJob);
0996         QCOMPARE(compoundSpy.result.count(), 0); // killed quietly in the loop above
0997     }
0998 
0999     QCOMPARE(compoundSpy.finished.count(), 1);
1000     QTest::qWait(1);
1001     QVERIFY(!compoundJob);
1002     for (const auto &spy : subjobSpies) {
1003         QVERIFY(!spy->job);
1004         const bool isFirstSubjob = spy.get() == &firstSubjobSpy;
1005         QCOMPARE(spy->started.count(), isFirstSubjob); // don't start subjobs once killed
1006         if (isFirstSubjob) {
1007             QVERIFY(spy->killed.count() <= killAttempts);
1008         } else {
1009             QCOMPARE(spy->killed.count(), 0);
1010         }
1011         QCOMPARE(spy->finished.count(), 1);
1012         QCOMPARE(spy->result.count(), 0);
1013     }
1014 }
1015 
1016 QTEST_GUILESS_MAIN(KSequentialCompoundJobTest)