blob: f3ed3d3da9e02751c358c46e06f8f02a55c3601a [file] [log] [blame]
#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