| |
| #include "bmc/scheduler_bmc.h" |
| |
| #include <chrono> // NOLINT(build/c++11) |
| #include <iostream> |
| #include <memory> |
| #include <string_view> |
| |
| #include "scheduler_interface.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/status/status.h" |
| #include "absl/time/time.h" |
| #include "boost/asio/io_context.hpp" // NOLINT(readability/boost) |
| |
| namespace safepower_agent { |
| namespace { |
| |
| TEST(Scheduler_fixtures, periodicCall) { |
| testing::MockFunction<void()> MockedCallBack; |
| auto test_io = std::make_shared<boost::asio::io_context>(); |
| std::shared_ptr<SchedulerInterface> scheduler = |
| std::make_shared<SchedulerBMC>(*test_io); |
| constexpr std::string_view task_name = "test_task"; |
| |
| EXPECT_EQ(scheduler->PeriodicCall(MockedCallBack.AsStdFunction(), |
| absl::Milliseconds(100), task_name), |
| absl::OkStatus()); |
| // wait .5 seconds |
| EXPECT_CALL(MockedCallBack, Call()).Times(testing::Between(4, 5)); |
| test_io->run_for(std::chrono::milliseconds(500)); |
| } |
| |
| TEST(Scheduler_fixtures, delayCall) { |
| testing::MockFunction<void()> MockedCallBack; |
| auto test_io = std::make_shared<boost::asio::io_context>(); |
| std::shared_ptr<SchedulerInterface> scheduler = |
| std::make_shared<SchedulerBMC>(*test_io); |
| |
| constexpr std::string_view task_name = "test_task_delayCall"; |
| EXPECT_EQ(scheduler->DelayCall(MockedCallBack.AsStdFunction(), |
| absl::Milliseconds(100), task_name), |
| absl::OkStatus()); |
| EXPECT_CALL(MockedCallBack, Call()).Times(1); |
| test_io->run_for(std::chrono::milliseconds(250)); |
| } |
| |
| TEST(Scheduler_fixtures, cancelCall) { |
| testing::MockFunction<void()> MockedCallBack_1; |
| testing::MockFunction<void()> MockedCallBack_2; |
| |
| EXPECT_CALL(MockedCallBack_1, Call()).Times(2); |
| EXPECT_CALL(MockedCallBack_2, Call()).Times(3); |
| |
| auto test_io = std::make_shared<boost::asio::io_context>(); |
| std::shared_ptr<SchedulerInterface> scheduler = |
| std::make_shared<SchedulerBMC>(*test_io); |
| |
| constexpr std::string_view task_name_1 = "test_task_1"; |
| constexpr std::string_view task_name_2 = "test_task_2"; |
| ASSERT_EQ(scheduler->PeriodicCall(MockedCallBack_1.AsStdFunction(), |
| absl::Milliseconds(100), task_name_1), |
| absl::OkStatus()); |
| EXPECT_EQ(scheduler->PeriodicCall(MockedCallBack_2.AsStdFunction(), |
| absl::Milliseconds(150), task_name_2), |
| absl::OkStatus()); |
| |
| test_io->run_for(std::chrono::milliseconds(250)); |
| ASSERT_EQ(scheduler->CancelCall(task_name_1), absl::OkStatus()); |
| test_io->run_for(std::chrono::milliseconds(250)); |
| ASSERT_EQ(scheduler->CancelCall(task_name_2), absl::OkStatus()); |
| } |
| |
| TEST(Scheduler_fixtures, cancelAll) { |
| testing::MockFunction<void()> MockedCallBack; |
| auto test_io = std::make_shared<boost::asio::io_context>(); |
| std::shared_ptr<SchedulerInterface> scheduler = |
| std::make_shared<SchedulerBMC>(*test_io); |
| |
| constexpr std::string_view task_name_1 = "test_task_1"; |
| constexpr std::string_view task_name_2 = "test_task_2"; |
| EXPECT_CALL(MockedCallBack, Call()).Times(0); |
| ASSERT_EQ(scheduler->PeriodicCall(MockedCallBack.AsStdFunction(), |
| absl::Milliseconds(100), task_name_1), |
| absl::OkStatus()); |
| ASSERT_EQ(scheduler->PeriodicCall(MockedCallBack.AsStdFunction(), |
| absl::Milliseconds(100), task_name_2), |
| absl::OkStatus()); |
| ASSERT_EQ(scheduler->CancelAll(), absl::OkStatus()); |
| test_io->run_for(std::chrono::milliseconds(1000)); |
| } |
| |
| TEST(Scheduler_fixtures, shutdown) { |
| testing::MockFunction<void()> MockedCallBack; |
| auto test_io = std::make_shared<boost::asio::io_context>(); |
| std::shared_ptr<SchedulerInterface> scheduler = |
| std::make_shared<SchedulerBMC>(*test_io); |
| boost::asio::steady_timer testTimer(*test_io); |
| |
| constexpr std::string_view task_name_1 = "test_task_1"; |
| constexpr std::string_view task_name_2 = "test_task_2"; |
| EXPECT_CALL(MockedCallBack, Call()).Times(4); |
| ASSERT_EQ(scheduler->PeriodicCall(MockedCallBack.AsStdFunction(), |
| absl::Milliseconds(100), task_name_1), |
| absl::OkStatus()); |
| ASSERT_EQ(scheduler->PeriodicCall(MockedCallBack.AsStdFunction(), |
| absl::Milliseconds(100), task_name_2), |
| absl::OkStatus()); |
| |
| testTimer.expires_from_now(std::chrono::milliseconds(250)); |
| testTimer.async_wait([scheduler](const boost::system::error_code&) mutable { |
| ASSERT_EQ(scheduler->Shutdown(), absl::OkStatus()); |
| }); |
| // run the event loop until shutdown is called, and run() stops blocking |
| test_io->run(); |
| // continue running the event loop to ensure the timer is not called again |
| test_io->run_for(std::chrono::milliseconds(1000)); |
| } |
| |
| TEST(Scheduler_fixtures, SelfCancel) { |
| auto test_io = std::make_shared<boost::asio::io_context>(); |
| std::shared_ptr<SchedulerInterface> scheduler = |
| std::make_shared<SchedulerBMC>(*test_io); |
| int count = 0; |
| ASSERT_EQ(scheduler->PeriodicCall( |
| [&count, &scheduler]() { |
| count++; |
| if (count == 5) { |
| ASSERT_EQ(scheduler->CancelCall("self_cancel_task"), |
| absl::OkStatus()); |
| } |
| }, |
| absl::Milliseconds(100), "self_cancel_task"), |
| absl::OkStatus()); |
| test_io->run_for(std::chrono::milliseconds(1000)); |
| ASSERT_EQ(count, 5); |
| } |
| |
| TEST(Scheduler_fixtures, RescheduleSelf) { |
| auto test_io = std::make_shared<boost::asio::io_context>(); |
| std::shared_ptr<SchedulerInterface> scheduler = |
| std::make_shared<SchedulerBMC>(*test_io); |
| int count = 0; |
| // task A is allowed to reschedule itself |
| ASSERT_EQ(scheduler->DelayCall([&count, &scheduler](){ |
| count++; |
| ASSERT_EQ(scheduler->DelayCall([&count, &scheduler](){ |
| count++; |
| ASSERT_EQ(scheduler->DelayCall([&count, &scheduler](){ |
| count++; |
| }, absl::Milliseconds(100), "self_reschedule_task"), absl::OkStatus()); |
| }, |
| absl::Milliseconds(100), |
| "self_reschedule_task"), absl::OkStatus()); |
| }, |
| absl::Milliseconds(100), |
| "self_reschedule_task"), absl::OkStatus()); |
| |
| test_io->run_for(std::chrono::milliseconds(500)); |
| ASSERT_EQ(count, 3); |
| } |
| |
| TEST(Scheduler_fixtures, RescheduleOther) { |
| auto test_io = std::make_shared<boost::asio::io_context>(); |
| std::shared_ptr<SchedulerInterface> scheduler = |
| std::make_shared<SchedulerBMC>(*test_io); |
| int task_a_ct = 0; |
| ASSERT_EQ(scheduler->DelayCall([&task_a_ct, &scheduler](){ |
| task_a_ct++; |
| std::cerr << "task a ran"; |
| }, absl::Milliseconds(200), |
| "task_a"), absl::OkStatus()); |
| |
| // task_a is already running, so it should fail from caller |
| ASSERT_EQ(scheduler->DelayCall([](){ |
| }, absl::Milliseconds(50), |
| "task_a"), absl::AlreadyExistsError("Task already exists")); |
| test_io->run_for(std::chrono::milliseconds(500)); |
| ASSERT_EQ(task_a_ct, 1); |
| } |
| // TODO error conditions |
| // - write a test that cancels a non-existing task |
| // - write a test that cancels a task twice |
| // - write a test that schedules two test with the same name |
| // - both callback, and periodic |
| // - write a test that cancels all task, when there are not tasks (maybe) |
| |
| } // namespace |
| } // namespace safepower_agent |