| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613 |
- /*
- This file is part of the KDE project
- SPDX-FileCopyrightText: 2006 Kevin Ottens <ervin@kde.org>
- SPDX-License-Identifier: LGPL-2.0-only
- */
- #include "kjobtest.h"
- #include <QMetaEnum>
- #include <QSignalSpy>
- #include <QTest>
- #include <QTimer>
- #include <QVector>
- #include <string>
- QTEST_MAIN(KJobTest)
- KJobTest::KJobTest()
- : loop(this)
- {
- }
- void KJobTest::testEmitResult_data()
- {
- QTest::addColumn<int>("errorCode");
- QTest::addColumn<QString>("errorText");
- QTest::newRow("no error") << int(KJob::NoError) << QString();
- QTest::newRow("error no text") << 2 << QString();
- QTest::newRow("error with text") << 6 << "oops! an error? naaah, really?";
- }
- void KJobTest::testEmitResult()
- {
- TestJob *job = new TestJob;
- connect(job, &KJob::result, this, [this](KJob *job) {
- slotResult(job);
- loop.quit();
- });
- QFETCH(int, errorCode);
- QFETCH(QString, errorText);
- job->setError(errorCode);
- job->setErrorText(errorText);
- QSignalSpy destroyed_spy(job, &QObject::destroyed);
- job->start();
- QVERIFY(!job->isFinished());
- loop.exec();
- QVERIFY(job->isFinished());
- QCOMPARE(m_lastError, errorCode);
- QCOMPARE(m_lastErrorText, errorText);
- // Verify that the job is not deleted immediately...
- QCOMPARE(destroyed_spy.size(), 0);
- QTimer::singleShot(0, &loop, &QEventLoop::quit);
- // ... but when we enter the event loop again.
- loop.exec();
- QCOMPARE(destroyed_spy.size(), 1);
- }
- void KJobTest::testProgressTracking()
- {
- TestJob *testJob = new TestJob;
- KJob *job = testJob;
- qRegisterMetaType<KJob *>("KJob*");
- qRegisterMetaType<qulonglong>("qulonglong");
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- QSignalSpy processed_spy(job, qOverload<KJob *, KJob::Unit, qulonglong>(&KJob::processedAmount));
- QSignalSpy total_spy(job, qOverload<KJob *, KJob::Unit, qulonglong>(&KJob::totalAmount));
- QSignalSpy percent_spy(job, qOverload<KJob *, ulong>(&KJob::percent));
- #endif
- QSignalSpy processedChanged_spy(job, &KJob::processedAmountChanged);
- QSignalSpy totalChanged_spy(job, &KJob::totalAmountChanged);
- QSignalSpy percentChanged_spy(job, &KJob::percentChanged);
- /* Process a first item. Corresponding signal should be emitted.
- * Total size didn't change.
- * Since the total size is unknown, no percent signal is emitted.
- */
- testJob->setProcessedSize(1);
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- QCOMPARE(processed_spy.size(), 1);
- QCOMPARE(processed_spy.at(0).at(0).value<KJob *>(), static_cast<KJob *>(job));
- QCOMPARE(processed_spy.at(0).at(2).value<qulonglong>(), qulonglong(1));
- QCOMPARE(total_spy.size(), 0);
- QCOMPARE(percent_spy.size(), 0);
- #endif
- QCOMPARE(processedChanged_spy.size(), 1);
- QCOMPARE(processedChanged_spy.at(0).at(0).value<KJob *>(), static_cast<KJob *>(job));
- QCOMPARE(processedChanged_spy.at(0).at(2).value<qulonglong>(), qulonglong(1));
- QCOMPARE(totalChanged_spy.size(), 0);
- QCOMPARE(percentChanged_spy.size(), 0);
- /* Now, we know the total size. It's signaled.
- * The new percentage is signaled too.
- */
- testJob->setTotalSize(10);
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- QCOMPARE(processed_spy.size(), 1);
- QCOMPARE(total_spy.size(), 1);
- QCOMPARE(total_spy.at(0).at(0).value<KJob *>(), job);
- QCOMPARE(total_spy.at(0).at(2).value<qulonglong>(), qulonglong(10));
- QCOMPARE(percent_spy.size(), 1);
- QCOMPARE(percent_spy.at(0).at(0).value<KJob *>(), job);
- QCOMPARE(percent_spy.at(0).at(1).value<unsigned long>(), static_cast<unsigned long>(10));
- #endif
- QCOMPARE(processedChanged_spy.size(), 1);
- QCOMPARE(totalChanged_spy.size(), 1);
- QCOMPARE(totalChanged_spy.at(0).at(0).value<KJob *>(), job);
- QCOMPARE(totalChanged_spy.at(0).at(2).value<qulonglong>(), qulonglong(10));
- QCOMPARE(percentChanged_spy.size(), 1);
- QCOMPARE(percentChanged_spy.at(0).at(0).value<KJob *>(), job);
- QCOMPARE(percentChanged_spy.at(0).at(1).value<unsigned long>(), static_cast<unsigned long>(10));
- /* We announce a new percentage by hand.
- * Total, and processed didn't change, so no signal is emitted for them.
- */
- testJob->setPercent(15);
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- QCOMPARE(processed_spy.size(), 1);
- QCOMPARE(total_spy.size(), 1);
- QCOMPARE(percent_spy.size(), 2);
- QCOMPARE(percent_spy.at(1).at(0).value<KJob *>(), job);
- QCOMPARE(percent_spy.at(1).at(1).value<unsigned long>(), static_cast<unsigned long>(15));
- #endif
- QCOMPARE(processedChanged_spy.size(), 1);
- QCOMPARE(totalChanged_spy.size(), 1);
- QCOMPARE(percentChanged_spy.size(), 2);
- QCOMPARE(percentChanged_spy.at(1).at(0).value<KJob *>(), job);
- QCOMPARE(percentChanged_spy.at(1).at(1).value<unsigned long>(), static_cast<unsigned long>(15));
- /* We make some progress.
- * Processed size and percent are signaled.
- */
- testJob->setProcessedSize(3);
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- QCOMPARE(processed_spy.size(), 2);
- QCOMPARE(processed_spy.at(1).at(0).value<KJob *>(), job);
- QCOMPARE(processed_spy.at(1).at(2).value<qulonglong>(), qulonglong(3));
- QCOMPARE(total_spy.size(), 1);
- QCOMPARE(percent_spy.size(), 3);
- QCOMPARE(percent_spy.at(2).at(0).value<KJob *>(), job);
- QCOMPARE(percent_spy.at(2).at(1).value<unsigned long>(), static_cast<unsigned long>(30));
- #endif
- QCOMPARE(processedChanged_spy.size(), 2);
- QCOMPARE(processedChanged_spy.at(1).at(0).value<KJob *>(), job);
- QCOMPARE(processedChanged_spy.at(1).at(2).value<qulonglong>(), qulonglong(3));
- QCOMPARE(totalChanged_spy.size(), 1);
- QCOMPARE(percentChanged_spy.size(), 3);
- QCOMPARE(percentChanged_spy.at(2).at(0).value<KJob *>(), job);
- QCOMPARE(percentChanged_spy.at(2).at(1).value<unsigned long>(), static_cast<unsigned long>(30));
- /* We set a new total size, but equals to the previous one.
- * No signal is emitted.
- */
- testJob->setTotalSize(10);
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- QCOMPARE(processed_spy.size(), 2);
- QCOMPARE(total_spy.size(), 1);
- QCOMPARE(percent_spy.size(), 3);
- #endif
- QCOMPARE(processedChanged_spy.size(), 2);
- QCOMPARE(totalChanged_spy.size(), 1);
- QCOMPARE(percentChanged_spy.size(), 3);
- /* We 'lost' the previous work done.
- * Signals both percentage and new processed size.
- */
- testJob->setProcessedSize(0);
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- QCOMPARE(processed_spy.size(), 3);
- QCOMPARE(processed_spy.at(2).at(0).value<KJob *>(), job);
- QCOMPARE(processed_spy.at(2).at(2).value<qulonglong>(), qulonglong(0));
- QCOMPARE(total_spy.size(), 1);
- QCOMPARE(percent_spy.size(), 4);
- QCOMPARE(percent_spy.at(3).at(0).value<KJob *>(), job);
- QCOMPARE(percent_spy.at(3).at(1).value<unsigned long>(), static_cast<unsigned long>(0));
- #endif
- QCOMPARE(processedChanged_spy.size(), 3);
- QCOMPARE(processedChanged_spy.at(2).at(0).value<KJob *>(), job);
- QCOMPARE(processedChanged_spy.at(2).at(2).value<qulonglong>(), qulonglong(0));
- QCOMPARE(totalChanged_spy.size(), 1);
- QCOMPARE(percentChanged_spy.size(), 4);
- QCOMPARE(percentChanged_spy.at(3).at(0).value<KJob *>(), job);
- QCOMPARE(percentChanged_spy.at(3).at(1).value<unsigned long>(), static_cast<unsigned long>(0));
- /* We process more than the total size!?
- * Signals both percentage and new processed size.
- * Percentage is 150%
- *
- * Might sounds weird, but verify that this case is handled gracefully.
- */
- testJob->setProcessedSize(15);
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- QCOMPARE(processed_spy.size(), 4);
- QCOMPARE(processed_spy.at(3).at(0).value<KJob *>(), job);
- QCOMPARE(processed_spy.at(3).at(2).value<qulonglong>(), qulonglong(15));
- QCOMPARE(total_spy.size(), 1);
- QCOMPARE(percent_spy.size(), 5);
- QCOMPARE(percent_spy.at(4).at(0).value<KJob *>(), job);
- QCOMPARE(percent_spy.at(4).at(1).value<unsigned long>(), static_cast<unsigned long>(150));
- #endif
- QCOMPARE(processedChanged_spy.size(), 4);
- QCOMPARE(processedChanged_spy.at(3).at(0).value<KJob *>(), job);
- QCOMPARE(processedChanged_spy.at(3).at(2).value<qulonglong>(), qulonglong(15));
- QCOMPARE(totalChanged_spy.size(), 1);
- QCOMPARE(percentChanged_spy.size(), 5);
- QCOMPARE(percentChanged_spy.at(4).at(0).value<KJob *>(), job);
- QCOMPARE(percentChanged_spy.at(4).at(1).value<unsigned long>(), static_cast<unsigned long>(150));
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- processed_spy.clear();
- total_spy.clear();
- percent_spy.clear();
- #endif
- processedChanged_spy.clear();
- totalChanged_spy.clear();
- percentChanged_spy.clear();
- /**
- * Try again with Files as the progress unit
- */
- testJob->setProgressUnit(KJob::Files);
- testJob->setProcessedSize(16);
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- QCOMPARE(percent_spy.size(), 0); // no impact on percent
- #endif
- QCOMPARE(percentChanged_spy.size(), 0);
- testJob->setTotalFiles(5);
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- QCOMPARE(percent_spy.size(), 1);
- QCOMPARE(percent_spy.at(0).at(1).value<unsigned long>(), static_cast<unsigned long>(0));
- #endif
- QCOMPARE(percentChanged_spy.size(), 1);
- QCOMPARE(percentChanged_spy.at(0).at(1).value<unsigned long>(), static_cast<unsigned long>(0));
- testJob->setProcessedFiles(2);
- #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 80)
- QCOMPARE(percent_spy.size(), 2);
- QCOMPARE(percent_spy.at(1).at(1).value<unsigned long>(), static_cast<unsigned long>(40));
- #endif
- QCOMPARE(percentChanged_spy.size(), 2);
- QCOMPARE(percentChanged_spy.at(1).at(1).value<unsigned long>(), static_cast<unsigned long>(40));
- delete job;
- }
- void KJobTest::testExec_data()
- {
- QTest::addColumn<int>("errorCode");
- QTest::addColumn<QString>("errorText");
- QTest::newRow("no error") << int(KJob::NoError) << QString();
- QTest::newRow("error no text") << 2 << QString();
- QTest::newRow("error with text") << 6 << "oops! an error? naaah, really?";
- }
- void KJobTest::testExec()
- {
- TestJob *job = new TestJob;
- QFETCH(int, errorCode);
- QFETCH(QString, errorText);
- job->setError(errorCode);
- job->setErrorText(errorText);
- int resultEmitted = 0;
- // Prove to Kai Uwe that one can connect a job to a lambdas, despite the "private" signal
- connect(job, &KJob::result, this, [&resultEmitted](KJob *) {
- ++resultEmitted;
- });
- QSignalSpy destroyed_spy(job, &QObject::destroyed);
- QVERIFY(!job->isFinished());
- bool status = job->exec();
- QVERIFY(job->isFinished());
- QCOMPARE(resultEmitted, 1);
- QCOMPARE(status, (errorCode == KJob::NoError));
- QCOMPARE(job->error(), errorCode);
- QCOMPARE(job->errorText(), errorText);
- // Verify that the job is not deleted immediately...
- QCOMPARE(destroyed_spy.size(), 0);
- QTimer::singleShot(0, &loop, &QEventLoop::quit);
- // ... but when we enter the event loop again.
- loop.exec();
- QCOMPARE(destroyed_spy.size(), 1);
- }
- void KJobTest::testKill_data()
- {
- QTest::addColumn<int>("killVerbosity");
- QTest::addColumn<int>("errorCode");
- QTest::addColumn<QString>("errorText");
- QTest::addColumn<int>("resultEmitCount");
- QTest::addColumn<int>("finishedEmitCount");
- QTest::newRow("killed with result") << int(KJob::EmitResult) << int(KJob::KilledJobError) << QString() << 1 << 1;
- QTest::newRow("killed quietly") << int(KJob::Quietly) << int(KJob::KilledJobError) << QString() << 0 << 1;
- }
- void KJobTest::testKill()
- {
- auto *const job = setupErrorResultFinished();
- QSignalSpy destroyed_spy(job, &QObject::destroyed);
- QFETCH(int, killVerbosity);
- QFETCH(int, errorCode);
- QFETCH(QString, errorText);
- QFETCH(int, resultEmitCount);
- QFETCH(int, finishedEmitCount);
- QVERIFY(!job->isFinished());
- job->kill(static_cast<KJob::KillVerbosity>(killVerbosity));
- QVERIFY(job->isFinished());
- loop.processEvents(QEventLoop::AllEvents, 2000);
- QCOMPARE(m_lastError, errorCode);
- QCOMPARE(m_lastErrorText, errorText);
- QCOMPARE(job->error(), errorCode);
- QCOMPARE(job->errorText(), errorText);
- QCOMPARE(m_resultCount, resultEmitCount);
- QCOMPARE(m_finishedCount, finishedEmitCount);
- // Verify that the job is not deleted immediately...
- QCOMPARE(destroyed_spy.size(), 0);
- QTimer::singleShot(0, &loop, &QEventLoop::quit);
- // ... but when we enter the event loop again.
- loop.exec();
- QCOMPARE(destroyed_spy.size(), 1);
- QVERIFY(m_jobFinishCount.size() == (finishedEmitCount - resultEmitCount));
- m_jobFinishCount.clear();
- }
- void KJobTest::testDestroy()
- {
- auto *const job = setupErrorResultFinished();
- QVERIFY(!job->isFinished());
- delete job;
- QCOMPARE(m_lastError, static_cast<int>(KJob::NoError));
- QCOMPARE(m_lastErrorText, QString{});
- QCOMPARE(m_resultCount, 0);
- QCOMPARE(m_finishedCount, 1);
- QVERIFY(m_jobFinishCount.size() == 1);
- m_jobFinishCount.clear();
- }
- void KJobTest::testEmitAtMostOnce_data()
- {
- QTest::addColumn<bool>("autoDelete");
- QTest::addColumn<QVector<Action>>("actions");
- const auto actionName = [](Action action) {
- return QMetaEnum::fromType<Action>().valueToKey(static_cast<int>(action));
- };
- for (bool autoDelete : {true, false}) {
- for (Action a : {Action::Start, Action::KillQuietly, Action::KillVerbosely}) {
- for (Action b : {Action::Start, Action::KillQuietly, Action::KillVerbosely}) {
- const auto dataTag = std::string{actionName(a)} + '-' + actionName(b) + (autoDelete ? "-autoDelete" : "");
- QTest::newRow(dataTag.c_str()) << autoDelete << QVector<Action>{a, b};
- }
- }
- }
- }
- void KJobTest::testEmitAtMostOnce()
- {
- auto *const job = setupErrorResultFinished();
- QSignalSpy destroyed_spy(job, &QObject::destroyed);
- QFETCH(bool, autoDelete);
- job->setAutoDelete(autoDelete);
- QFETCH(QVector<Action>, actions);
- for (auto action : actions) {
- switch (action) {
- case Action::Start:
- job->start(); // in effect calls QTimer::singleShot(0, ... emitResult)
- break;
- case Action::KillQuietly:
- QTimer::singleShot(0, job, [=] {
- job->kill(KJob::Quietly);
- });
- break;
- case Action::KillVerbosely:
- QTimer::singleShot(0, job, [=] {
- job->kill(KJob::EmitResult);
- });
- break;
- }
- }
- QVERIFY(!job->isFinished());
- loop.processEvents(QEventLoop::AllEvents, 2000);
- QCOMPARE(destroyed_spy.size(), autoDelete);
- if (!autoDelete) {
- QVERIFY(job->isFinished());
- }
- QVERIFY(!actions.empty());
- // The first action alone should determine the job's error and result.
- const auto firstAction = actions.front();
- const int errorCode = firstAction == Action::Start ? KJob::NoError : KJob::KilledJobError;
- QCOMPARE(m_lastError, errorCode);
- QCOMPARE(m_lastErrorText, QString{});
- if (!autoDelete) {
- QCOMPARE(job->error(), m_lastError);
- QCOMPARE(job->errorText(), m_lastErrorText);
- }
- QCOMPARE(m_resultCount, firstAction == Action::KillQuietly ? 0 : 1);
- QCOMPARE(m_finishedCount, 1);
- if (!autoDelete) {
- delete job;
- }
- }
- void KJobTest::testDelegateUsage()
- {
- TestJob *job1 = new TestJob;
- TestJob *job2 = new TestJob;
- TestJobUiDelegate *delegate = new TestJobUiDelegate;
- QPointer<TestJobUiDelegate> guard(delegate);
- QVERIFY(job1->uiDelegate() == nullptr);
- job1->setUiDelegate(delegate);
- QVERIFY(job1->uiDelegate() == delegate);
- QVERIFY(job2->uiDelegate() == nullptr);
- job2->setUiDelegate(delegate);
- QVERIFY(job2->uiDelegate() == nullptr);
- delete job1;
- delete job2;
- QVERIFY(guard.isNull()); // deleted by job1
- }
- void KJobTest::testNestedExec()
- {
- m_innerJob = nullptr;
- QTimer::singleShot(100, this, &KJobTest::slotStartInnerJob);
- m_outerJob = new WaitJob();
- m_outerJob->exec();
- }
- void KJobTest::slotStartInnerJob()
- {
- QTimer::singleShot(100, this, &KJobTest::slotFinishOuterJob);
- m_innerJob = new WaitJob();
- m_innerJob->exec();
- }
- void KJobTest::slotFinishOuterJob()
- {
- QTimer::singleShot(100, this, &KJobTest::slotFinishInnerJob);
- m_outerJob->makeItFinish();
- }
- void KJobTest::slotFinishInnerJob()
- {
- m_innerJob->makeItFinish();
- }
- void KJobTest::slotResult(KJob *job)
- {
- const auto testJob = qobject_cast<const TestJob *>(job);
- QVERIFY(testJob);
- QVERIFY(testJob->isFinished());
- // Ensure the job has already emitted finished() if we are tracking from
- // setupErrorResultFinished
- if (m_jobFinishCount.contains(job)) {
- QVERIFY(m_jobFinishCount.value(job) == 1);
- QVERIFY(m_jobFinishCount.remove(job) == 1 /* num items removed */);
- }
- if (job->error()) {
- m_lastError = job->error();
- m_lastErrorText = job->errorText();
- } else {
- m_lastError = KJob::NoError;
- m_lastErrorText.clear();
- }
- m_resultCount++;
- }
- void KJobTest::slotFinished(KJob *job)
- {
- QVERIFY2(m_jobFinishCount.value(job) == 0, "Ensure we have not double-emitted KJob::finished()");
- m_jobFinishCount[job]++;
- if (job->error()) {
- m_lastError = job->error();
- m_lastErrorText = job->errorText();
- } else {
- m_lastError = KJob::NoError;
- m_lastErrorText.clear();
- }
- m_finishedCount++;
- }
- TestJob *KJobTest::setupErrorResultFinished()
- {
- m_lastError = KJob::UserDefinedError;
- m_lastErrorText.clear();
- m_resultCount = 0;
- m_finishedCount = 0;
- auto *job = new TestJob;
- m_jobFinishCount[job] = 0;
- connect(job, &KJob::result, this, &KJobTest::slotResult);
- connect(job, &KJob::finished, this, &KJobTest::slotFinished);
- return job;
- }
- TestJob::TestJob()
- : KJob()
- {
- }
- TestJob::~TestJob()
- {
- }
- void TestJob::start()
- {
- QTimer::singleShot(0, this, [this] {
- emitResult();
- });
- }
- bool TestJob::doKill()
- {
- return true;
- }
- void TestJob::setError(int errorCode)
- {
- KJob::setError(errorCode);
- }
- void TestJob::setErrorText(const QString &errorText)
- {
- KJob::setErrorText(errorText);
- }
- void TestJob::setProcessedSize(qulonglong size)
- {
- KJob::setProcessedAmount(KJob::Bytes, size);
- }
- void TestJob::setTotalSize(qulonglong size)
- {
- KJob::setTotalAmount(KJob::Bytes, size);
- }
- void TestJob::setProcessedFiles(qulonglong files)
- {
- KJob::setProcessedAmount(KJob::Files, files);
- }
- void TestJob::setTotalFiles(qulonglong files)
- {
- KJob::setTotalAmount(KJob::Files, files);
- }
- void TestJob::setPercent(unsigned long percentage)
- {
- KJob::setPercent(percentage);
- }
- void WaitJob::start()
- {
- }
- void WaitJob::makeItFinish()
- {
- emitResult();
- }
- void TestJobUiDelegate::connectJob(KJob *job)
- {
- QVERIFY(job->uiDelegate() != nullptr);
- }
- #include "moc_kjobtest.cpp"
|