// No copyright #pragma once #include "core/luajit/context.hh" #include #include #include #include #include #include #include #include "iface/common/exception.hh" #include "iface/common/task.hh" #include "iface/subsys/concurrency.hh" #include "iface/subsys/parallelism.hh" #include "iface/env.hh" namespace nf7::core::luajit::test { class ContextFixture : public ::testing::TestWithParam { private: class AsyncDriver final { public: explicit AsyncDriver(ContextFixture& parent) noexcept : parent_(parent) { } void BeginBusy() noexcept { } void EndBusy() noexcept { } void Drive(AsyncTask&& task) noexcept { try { task(param_); } catch (const Exception& e) { std::cerr << "unexpected exception while async task execution: " << e.what() << std::endl; std::abort(); } } AsyncTask::Time tick() const noexcept { const auto now = std::chrono::system_clock::now(); return std::chrono::time_point_cast(now); } bool nextIdleInterruption() const noexcept { return !parent_.alive_; } bool nextTaskInterruption() const noexcept { return false; } private: ContextFixture& parent_; AsyncTaskContext param_; }; class SyncDriver final { public: void BeginBusy() noexcept { } void EndBusy() noexcept { interrupt_ = true; } void Drive(SyncTask&& task) noexcept { try { task(param_); } catch (const Exception& e) { std::cerr << "unexpected exception while sync task execution: " << e.what() << std::endl; std::abort(); } } SyncTask::Time tick() const noexcept { const auto now = std::chrono::system_clock::now(); return std::chrono::time_point_cast(now); } bool nextIdleInterruption() const noexcept { return interrupt_; } bool nextTaskInterruption() const noexcept { return false; } private: bool interrupt_ = false; SyncTaskContext param_; }; public: ContextFixture() noexcept : async_driver_(*this) { } protected: void SetUp() override { syncq_ = std::make_shared>(); asyncq_ = std::make_shared>(); env_.emplace(SimpleEnv::FactoryMap { { typeid(subsys::Concurrency), [this](auto&) { return std::make_shared< WrappedTaskQueue>(syncq_); }, }, { typeid(subsys::Parallelism), [this](auto&) { return std::make_shared< WrappedTaskQueue>(asyncq_); }, }, }); thread_ = std::thread {[this]() { asyncq_->Drive(async_driver_); }}; } void TearDown() override { ConsumeTasks(); env_ = std::nullopt; WaitAsyncTasks(std::chrono::seconds(3)); alive_ = false; asyncq_->Wake(); thread_.join(); asyncq_ = nullptr; syncq_ = nullptr; } void ConsumeTasks() noexcept { SyncDriver sync_driver; syncq_->Drive(sync_driver); WaitAsyncTasks(std::chrono::seconds(1)); } void WaitAsyncTasks(auto dur) noexcept { if (!asyncq_->WaitForEmpty(dur)) { std::cerr << "timeout while waiting for task execution" << std::endl; std::abort(); } } protected: std::shared_ptr> syncq_; std::shared_ptr> asyncq_; std::optional env_; private: std::atomic alive_ = true; uint32_t async_cycle_ = 0; std::thread thread_; AsyncDriver async_driver_; }; } // namespace nf7::core::luajit::test