From 0db05ac71b6d4d1f67e3eeaf0275eb9b55f2c8d6 Mon Sep 17 00:00:00 2001 From: Per Malmberg Date: Sat, 10 Mar 2018 23:42:00 +0100 Subject: [PATCH] Functionality in main Cron class. --- libcron/CMakeLists.txt | 2 +- libcron/Cron.cpp | 29 ++++++--- libcron/Cron.h | 16 +++-- libcron/CronSchedule.h | 5 +- libcron/Task.cpp | 17 ++++++ libcron/Task.h | 15 ++++- test/CMakeLists.txt | 4 +- test/{test.cpp => CronDataTest.cpp} | 0 test/CronScheduleTest.cpp | 2 +- test/CronTest.cpp | 92 +++++++++++++++++++++++++++++ 10 files changed, 161 insertions(+), 21 deletions(-) create mode 100644 libcron/Task.cpp rename test/{test.cpp => CronDataTest.cpp} (100%) create mode 100644 test/CronTest.cpp diff --git a/libcron/CMakeLists.txt b/libcron/CMakeLists.txt index 66ac343..2e1882d 100644 --- a/libcron/CMakeLists.txt +++ b/libcron/CMakeLists.txt @@ -15,4 +15,4 @@ add_library(${PROJECT_NAME} CronData.cpp CronSchedule.cpp CronSchedule.h - externals/date/date.h DateTime.h) + externals/date/date.h DateTime.h Task.cpp) diff --git a/libcron/Cron.cpp b/libcron/Cron.cpp index 06cf09e..7ea7ca6 100644 --- a/libcron/Cron.cpp +++ b/libcron/Cron.cpp @@ -1,14 +1,29 @@ #include #include "Cron.h" -bool libcron::Cron::add_schedule(const std::string &schedule, std::function work) +namespace libcron { - auto cron = CronData::create(schedule); - bool res = cron.is_valid(); - if (res) + + bool libcron::Cron::add_schedule(const std::string& schedule, std::function work) { - items.emplace(Task(CronSchedule(cron), std::move(work))); + auto cron = CronData::create(schedule); + bool res = cron.is_valid(); + if (res) + { + CronSchedule s(cron); + Task t(std::move(s), std::move(work)); + if (t.calculate_next()) + { + items.emplace(t); + } + } + + return res; } - return res; -} + bool libcron::Cron::has_expired_task(const std::chrono::system_clock::time_point now) const + { + return !items.empty() && items.top().is_expired(now); + } + +} \ No newline at end of file diff --git a/libcron/Cron.h b/libcron/Cron.h index 17ee640..52699c6 100644 --- a/libcron/Cron.h +++ b/libcron/Cron.h @@ -10,9 +10,17 @@ namespace libcron class Cron { - public: - bool add_schedule(const std::string& schedule, std::function work); - private: - std::priority_queue items{}; + public: + bool add_schedule(const std::string& schedule, std::function work); + + size_t count() const + { + return items.size(); + } + + bool has_expired_task(std::chrono::system_clock::time_point now = std::chrono::system_clock::now()) const; + + private: + std::priority_queue items{}; }; } \ No newline at end of file diff --git a/libcron/CronSchedule.h b/libcron/CronSchedule.h index 3d38bc2..629e3ac 100644 --- a/libcron/CronSchedule.h +++ b/libcron/CronSchedule.h @@ -10,8 +10,8 @@ namespace libcron class CronSchedule { public: - explicit CronSchedule(CronData data) - : data(std::move(data)) + explicit CronSchedule(CronData& data) + : data(data) { } @@ -37,7 +37,6 @@ namespace libcron return dt; } - private: CronData data; }; diff --git a/libcron/Task.cpp b/libcron/Task.cpp new file mode 100644 index 0000000..51d124e --- /dev/null +++ b/libcron/Task.cpp @@ -0,0 +1,17 @@ +#include "Task.h" + +namespace libcron +{ + + bool Task::calculate_next(std::chrono::system_clock::time_point from) + { + auto result = schedule.calculate_from(from); + auto res = std::get<0>(result); + if(res) + { + next_schedule = std::get<1>(result); + } + + return res; + } +} \ No newline at end of file diff --git a/libcron/Task.h b/libcron/Task.h index b44a5f6..00bf981 100644 --- a/libcron/Task.h +++ b/libcron/Task.h @@ -3,6 +3,7 @@ #include #include "CronData.h" #include "CronSchedule.h" +#include namespace libcron { @@ -11,7 +12,7 @@ namespace libcron public: Task(CronSchedule schedule, std::function task) - : schedule(std::move(schedule))//, task(std::move(task)) + : schedule(std::move(schedule)), task(std::move(task)) { } @@ -19,13 +20,21 @@ namespace libcron Task& operator=(const Task&) = default; + bool calculate_next(std::chrono::system_clock::time_point from = std::chrono::system_clock::now()); + bool operator<(const Task& other) const { - return false; + return next_schedule < other.next_schedule; + } + + bool is_expired(std::chrono::system_clock::time_point now = std::chrono::system_clock::now()) const + { + return now >= next_schedule; } private: CronSchedule schedule; - //std::function task; + std::chrono::system_clock::time_point next_schedule; + std::function task; }; } \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 65387b1..9470888 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,7 +12,7 @@ include_directories( add_executable( ${PROJECT_NAME} - test.cpp - CronScheduleTest.cpp) + CronDataTest.cpp + CronScheduleTest.cpp CronTest.cpp) target_link_libraries(${PROJECT_NAME} libcron) \ No newline at end of file diff --git a/test/test.cpp b/test/CronDataTest.cpp similarity index 100% rename from test/test.cpp rename to test/CronDataTest.cpp diff --git a/test/CronScheduleTest.cpp b/test/CronScheduleTest.cpp index 99d44f9..a40f236 100644 --- a/test/CronScheduleTest.cpp +++ b/test/CronScheduleTest.cpp @@ -178,6 +178,6 @@ SCENARIO("Multiple calculations") SCENARIO("Unable to calculate time point") { - // TODO: Find a + // TODO: Find a schedule that is unsolvable. //REQUIRE_FALSE(test("0 0 0 1 1 0", DT(2021_y / 12 / 15), DT(2022_y / 1 / 1))); } \ No newline at end of file diff --git a/test/CronTest.cpp b/test/CronTest.cpp new file mode 100644 index 0000000..cb04ad9 --- /dev/null +++ b/test/CronTest.cpp @@ -0,0 +1,92 @@ +#include +#include +#include + +using namespace libcron; +using namespace std::chrono; + +std::string create_schedule_expiring_in(hours h, minutes m, seconds s) +{ + auto now = system_clock::now() + h + m + s; + auto dt = CronSchedule::to_calendar_time(now); + + std::string res = ""; + res += std::to_string(dt.sec) + " "; + res += std::to_string(dt.min) + " "; + res += std::to_string(dt.hour) + " * * *"; + + return res; +} + + +SCENARIO("Adding a task") +{ + GIVEN("A Cron instance with no task") + { + Cron c; + + THEN("Starts with no task") + { + REQUIRE(c.count() == 0); + } + + WHEN("Adding a task that runs every second") + { + REQUIRE(c.add_schedule("* * * * * *", + []() + { + return; + }) + ); + + THEN("Count is 1 and task was not expired two seconds ago") + { + REQUIRE(c.count() == 1); + REQUIRE_FALSE(c.has_expired_task(system_clock::now() - 2s)); + } + AND_THEN("Task is expired when calculating based on current time") + { + THEN("Task is expired") + { + REQUIRE(c.has_expired_task()); + } + } + } + } +} + +SCENARIO("Adding a task that expires in the future") +{ + GIVEN("A Cron instance with task expiring in 3 seconds") + { + Cron c; + REQUIRE(c.add_schedule(create_schedule_expiring_in(hours{0}, minutes{0}, seconds{3}), + []() + { + return; + }) + ); + + THEN("Not yet expired") + { + REQUIRE_FALSE(c.has_expired_task()); + } + AND_WHEN("When waiting one second") + { + std::this_thread::sleep_for(1s); + THEN("Task has not yet expired") + { + REQUIRE_FALSE(c.has_expired_task()); + } + } + AND_WHEN("When waiting three seconds") + { + std::this_thread::sleep_for(3s); + THEN("Task has expired") + { + REQUIRE(c.has_expired_task()); + } + } + } +} +