diff options
Diffstat (limited to '')
-rw-r--r-- | xbmc/utils/test/TestJobManager.cpp | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/xbmc/utils/test/TestJobManager.cpp b/xbmc/utils/test/TestJobManager.cpp new file mode 100644 index 0000000..86f0af9 --- /dev/null +++ b/xbmc/utils/test/TestJobManager.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "ServiceBroker.h" +#include "test/MtTestUtils.h" +#include "utils/Job.h" +#include "utils/JobManager.h" +#include "utils/XTimeUtils.h" + +#include <atomic> +#include <mutex> + +#include <gtest/gtest.h> + +using namespace ConditionPoll; + +struct Flags +{ + std::atomic<bool> lingerAtWork{true}; + std::atomic<bool> started{false}; + std::atomic<bool> finished{false}; + std::atomic<bool> wasCanceled{false}; +}; + +class DummyJob : public CJob +{ + Flags* m_flags; +public: + inline DummyJob(Flags* flags) : m_flags(flags) + { + } + + bool DoWork() override + { + m_flags->started = true; + while (m_flags->lingerAtWork) + std::this_thread::yield(); + + if (ShouldCancel(0,0)) + m_flags->wasCanceled = true; + + m_flags->finished = true; + return true; + } +}; + +class ReallyDumbJob : public CJob +{ + Flags* m_flags; +public: + inline ReallyDumbJob(Flags* flags) : m_flags(flags) {} + + bool DoWork() override + { + m_flags->finished = true; + return true; + } +}; + +class TestJobManager : public testing::Test +{ +protected: + TestJobManager() { CServiceBroker::RegisterJobManager(std::make_shared<CJobManager>()); } + + ~TestJobManager() override + { + /* Always cancel jobs test completion */ + CServiceBroker::GetJobManager()->CancelJobs(); + CServiceBroker::GetJobManager()->Restart(); + CServiceBroker::UnregisterJobManager(); + } +}; + +TEST_F(TestJobManager, AddJob) +{ + Flags* flags = new Flags(); + ReallyDumbJob* job = new ReallyDumbJob(flags); + CServiceBroker::GetJobManager()->AddJob(job, nullptr); + ASSERT_TRUE(poll([flags]() -> bool { return flags->finished; })); + delete flags; +} + +TEST_F(TestJobManager, CancelJob) +{ + unsigned int id; + Flags* flags = new Flags(); + DummyJob* job = new DummyJob(flags); + id = CServiceBroker::GetJobManager()->AddJob(job, nullptr); + + // wait for the worker thread to be entered + ASSERT_TRUE(poll([flags]() -> bool { return flags->started; })); + + // cancel the job + CServiceBroker::GetJobManager()->CancelJob(id); + + // let the worker thread continue + flags->lingerAtWork = false; + + // make sure the job finished. + ASSERT_TRUE(poll([flags]() -> bool { return flags->finished; })); + + // ... and that it was canceled. + EXPECT_TRUE(flags->wasCanceled); + delete flags; +} + +namespace +{ +struct JobControlPackage +{ + JobControlPackage() + { + // We're not ready to wait yet + jobCreatedMutex.lock(); + } + + ~JobControlPackage() + { + jobCreatedMutex.unlock(); + } + + bool ready = false; + XbmcThreads::ConditionVariable jobCreatedCond; + CCriticalSection jobCreatedMutex; +}; + +class BroadcastingJob : + public CJob +{ +public: + + BroadcastingJob(JobControlPackage &package) : + m_package(package), + m_finish(false) + { + } + + void FinishAndStopBlocking() + { + std::unique_lock<CCriticalSection> lock(m_blockMutex); + + m_finish = true; + m_block.notifyAll(); + } + + const char * GetType() const override + { + return "BroadcastingJob"; + } + + bool DoWork() override + { + { + std::unique_lock<CCriticalSection> lock(m_package.jobCreatedMutex); + + m_package.ready = true; + m_package.jobCreatedCond.notifyAll(); + } + + std::unique_lock<CCriticalSection> blockLock(m_blockMutex); + + // Block until we're told to go away + while (!m_finish) + m_block.wait(m_blockMutex); + return true; + } + +private: + + JobControlPackage &m_package; + + XbmcThreads::ConditionVariable m_block; + CCriticalSection m_blockMutex; + bool m_finish; +}; + +BroadcastingJob * +WaitForJobToStartProcessing(CJob::PRIORITY priority, JobControlPackage &package) +{ + BroadcastingJob* job = new BroadcastingJob(package); + CServiceBroker::GetJobManager()->AddJob(job, nullptr, priority); + + // We're now ready to wait, wait and then unblock once ready + while (!package.ready) + package.jobCreatedCond.wait(package.jobCreatedMutex); + + return job; +} +} + +TEST_F(TestJobManager, PauseLowPriorityJob) +{ + JobControlPackage package; + BroadcastingJob *job (WaitForJobToStartProcessing(CJob::PRIORITY_LOW_PAUSABLE, package)); + + EXPECT_TRUE(CServiceBroker::GetJobManager()->IsProcessing(CJob::PRIORITY_LOW_PAUSABLE)); + CServiceBroker::GetJobManager()->PauseJobs(); + EXPECT_FALSE(CServiceBroker::GetJobManager()->IsProcessing(CJob::PRIORITY_LOW_PAUSABLE)); + CServiceBroker::GetJobManager()->UnPauseJobs(); + EXPECT_TRUE(CServiceBroker::GetJobManager()->IsProcessing(CJob::PRIORITY_LOW_PAUSABLE)); + + job->FinishAndStopBlocking(); +} + +TEST_F(TestJobManager, IsProcessing) +{ + JobControlPackage package; + BroadcastingJob *job (WaitForJobToStartProcessing(CJob::PRIORITY_LOW_PAUSABLE, package)); + + EXPECT_EQ(0, CServiceBroker::GetJobManager()->IsProcessing("")); + + job->FinishAndStopBlocking(); +} |