mirror of
https://github.com/PerMalmberg/libcron.git
synced 2024-11-27 03:09:51 +08:00
Work on DST handling.
This commit is contained in:
parent
5d60fa7133
commit
e99b049d2b
@ -1,6 +1,9 @@
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
|
||||
set(OUTPUT_LOCATION ${CMAKE_CURRENT_LIST_DIR}/out/)
|
||||
|
||||
add_subdirectory(libcron)
|
||||
add_subdirectory(test)
|
||||
|
||||
add_dependencies(cron_test libcron)
|
||||
add_dependencies(cron_test libcron)
|
||||
|
||||
|
@ -8,7 +8,6 @@ include_directories(${CMAKE_CURRENT_LIST_DIR}/externals/date/include)
|
||||
|
||||
add_library(${PROJECT_NAME}
|
||||
Cron.h
|
||||
Cron.cpp
|
||||
Task.h
|
||||
CronData.h
|
||||
TimeTypes.h
|
||||
@ -18,3 +17,8 @@ add_library(${PROJECT_NAME}
|
||||
DateTime.h
|
||||
Task.cpp
|
||||
CronClock.h)
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${OUTPUT_LOCATION}"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_LOCATION}"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_LOCATION}")
|
@ -1,87 +0,0 @@
|
||||
#include <functional>
|
||||
#include "Cron.h"
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
namespace libcron
|
||||
{
|
||||
bool libcron::Cron::add_schedule( std::string name, const std::string& schedule, std::function<void()> work)
|
||||
{
|
||||
auto cron = CronData::create(schedule);
|
||||
bool res = cron.is_valid();
|
||||
if (res)
|
||||
{
|
||||
|
||||
Task t{std::move(name), CronSchedule{cron}, std::move(work)};
|
||||
if (t.calculate_next(clock->now()))
|
||||
{
|
||||
tasks.push(t);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::chrono::system_clock::duration Cron::time_until_next() const
|
||||
{
|
||||
system_clock::duration d{};
|
||||
if (tasks.empty())
|
||||
{
|
||||
d = std::numeric_limits<minutes>::max();
|
||||
}
|
||||
else
|
||||
{
|
||||
d = tasks.top().time_until_expiry(clock->now());
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
size_t Cron::execute_expired_tasks(system_clock::time_point now)
|
||||
{
|
||||
std::vector<Task> executed{};
|
||||
|
||||
while(!tasks.empty()
|
||||
&& tasks.top().is_expired(now))
|
||||
{
|
||||
executed.push_back(tasks.top());
|
||||
tasks.pop();
|
||||
auto& t = executed[executed.size()-1];
|
||||
t.execute();
|
||||
}
|
||||
|
||||
auto res = executed.size();
|
||||
|
||||
// Place executed tasks back onto the priority queue.
|
||||
std::for_each(executed.begin(), executed.end(), [this, &now](Task& task)
|
||||
{
|
||||
// Must calculate new schedules using second after 'now', otherwise
|
||||
// we'll run the same task over and over if it takes less than 1s to execute.
|
||||
if(task.calculate_next(now + 1s))
|
||||
{
|
||||
tasks.push(task);
|
||||
}
|
||||
});
|
||||
|
||||
print_queue(tasks);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void Cron::print_queue(std::priority_queue<Task, std::vector<Task>, std::greater<>> queue)
|
||||
{
|
||||
std::vector<Task> v{};
|
||||
|
||||
while( !queue.empty())
|
||||
{
|
||||
auto t = queue.top();
|
||||
queue.pop();
|
||||
v.push_back(t);
|
||||
}
|
||||
|
||||
std::for_each(v.begin(), v.end(), [&queue](auto& task){
|
||||
queue.push(task);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
152
libcron/Cron.h
152
libcron/Cron.h
@ -9,15 +9,17 @@
|
||||
|
||||
namespace libcron
|
||||
{
|
||||
template<typename ClockType>
|
||||
class Cron;
|
||||
|
||||
template<typename ClockType>
|
||||
std::ostream& operator<<(std::ostream& stream, const Cron<ClockType>& c);
|
||||
|
||||
template<typename ClockType = libcron::LocalClock>
|
||||
class Cron
|
||||
{
|
||||
public:
|
||||
|
||||
explicit Cron(std::unique_ptr<ICronClock> clock = std::make_unique<LocalClock>())
|
||||
: clock(std::move(clock))
|
||||
{
|
||||
}
|
||||
|
||||
bool add_schedule(std::string name, const std::string& schedule, std::function<void()> work);
|
||||
|
||||
size_t count() const
|
||||
@ -25,24 +27,148 @@ namespace libcron
|
||||
return tasks.size();
|
||||
}
|
||||
|
||||
// Tick is expected to be called at least once a second to prevent missing schedules.
|
||||
size_t
|
||||
execute_expired_tasks()
|
||||
tick()
|
||||
{
|
||||
return execute_expired_tasks(clock->now());
|
||||
return tick(clock.now());
|
||||
}
|
||||
|
||||
size_t
|
||||
execute_expired_tasks(std::chrono::system_clock::time_point now);
|
||||
tick(std::chrono::system_clock::time_point now);
|
||||
|
||||
std::chrono::system_clock::duration
|
||||
time_until_next() const;
|
||||
|
||||
std::shared_ptr<ICronClock> get_clock() const { return clock; }
|
||||
ClockType& get_clock()
|
||||
{
|
||||
return clock;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<<>(std::ostream& stream, const Cron<ClockType>& c);
|
||||
|
||||
private:
|
||||
// Priority queue placing smallest (i.e. nearest in time) items on top.
|
||||
std::priority_queue<Task, std::vector<Task>, std::greater<>> tasks{};
|
||||
void print_queue(std::priority_queue<Task, std::vector<Task>, std::greater<>> queue);
|
||||
std::shared_ptr<ICronClock> clock{};
|
||||
class Queue
|
||||
// Priority queue placing smallest (i.e. nearest in time) items on top.
|
||||
: public std::priority_queue<Task, std::vector<Task>, std::greater<>>
|
||||
{
|
||||
public:
|
||||
// Inherit to allow access to the container.
|
||||
const std::vector<Task>& get_tasks() const
|
||||
{
|
||||
return c;
|
||||
}
|
||||
|
||||
std::vector<Task>& get_tasks()
|
||||
{
|
||||
return c;
|
||||
}
|
||||
};
|
||||
|
||||
Queue tasks{};
|
||||
ClockType clock{};
|
||||
bool first_tick = true;
|
||||
std::chrono::system_clock::time_point last_tick{};
|
||||
};
|
||||
|
||||
template<typename ClockType>
|
||||
bool Cron<ClockType>::add_schedule(std::string name, const std::string& schedule, std::function<void()> work)
|
||||
{
|
||||
auto cron = CronData::create(schedule);
|
||||
bool res = cron.is_valid();
|
||||
if (res)
|
||||
{
|
||||
|
||||
Task t{std::move(name), CronSchedule{cron}, std::move(work)};
|
||||
if (t.calculate_next(clock.now()))
|
||||
{
|
||||
tasks.push(t);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename ClockType>
|
||||
std::chrono::system_clock::duration Cron<ClockType>::time_until_next() const
|
||||
{
|
||||
system_clock::duration d{};
|
||||
if (tasks.empty())
|
||||
{
|
||||
d = std::numeric_limits<minutes>::max();
|
||||
}
|
||||
else
|
||||
{
|
||||
d = tasks.top().time_until_expiry(clock.now());
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
template<typename ClockType>
|
||||
size_t Cron<ClockType>::tick(system_clock::time_point now)
|
||||
{
|
||||
size_t res = 0;
|
||||
|
||||
if (first_tick)
|
||||
{
|
||||
first_tick = false;
|
||||
}
|
||||
else if (now - last_tick < hours{3})
|
||||
{
|
||||
// Reschedule all tasks.
|
||||
for (auto& t : tasks.get_tasks())
|
||||
{
|
||||
t.calculate_next(now);
|
||||
}
|
||||
}
|
||||
else if(now < last_tick && now >= last_tick - hours{3})
|
||||
{
|
||||
// Prevent tasks from running until the clock has reached current 'last_tick'.
|
||||
for (auto& t : tasks.get_tasks())
|
||||
{
|
||||
//t.set_back_limit(last_tick);
|
||||
}
|
||||
}
|
||||
|
||||
last_tick = now;
|
||||
|
||||
std::vector<Task> executed{};
|
||||
|
||||
while (!tasks.empty()
|
||||
&& tasks.top().is_expired(now))
|
||||
{
|
||||
executed.push_back(tasks.top());
|
||||
tasks.pop();
|
||||
auto& t = executed[executed.size() - 1];
|
||||
t.execute();
|
||||
}
|
||||
|
||||
res = executed.size();
|
||||
|
||||
// Place executed tasks back onto the priority queue.
|
||||
std::for_each(executed.begin(), executed.end(), [this, &now](Task& task)
|
||||
{
|
||||
// Must calculate new schedules using second after 'now', otherwise
|
||||
// we'll run the same task over and over if it takes less than 1s to execute.
|
||||
if (task.calculate_next(now + 1s))
|
||||
{
|
||||
tasks.push(task);
|
||||
}
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename ClockType>
|
||||
std::ostream& operator<<(std::ostream& stream, const Cron<ClockType>& c)
|
||||
{
|
||||
std::for_each(c.tasks.get_tasks().cbegin(), c.tasks.get_tasks().cend(),
|
||||
[&stream, &c](const Task& t)
|
||||
{
|
||||
stream << t.get_status(c.clock.now()) << '\n';
|
||||
});
|
||||
|
||||
return stream;
|
||||
}
|
||||
}
|
@ -10,20 +10,20 @@ namespace libcron
|
||||
class ICronClock
|
||||
{
|
||||
public:
|
||||
virtual std::chrono::system_clock::time_point now() = 0;
|
||||
virtual std::chrono::seconds utc_offset(std::chrono::system_clock::time_point now) = 0;
|
||||
virtual std::chrono::system_clock::time_point now() const = 0;
|
||||
virtual std::chrono::seconds utc_offset(std::chrono::system_clock::time_point now) const = 0;
|
||||
};
|
||||
|
||||
class UTCClock
|
||||
: public ICronClock
|
||||
{
|
||||
public:
|
||||
std::chrono::system_clock::time_point now() override
|
||||
std::chrono::system_clock::time_point now() const override
|
||||
{
|
||||
return std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
std::chrono::seconds utc_offset(std::chrono::system_clock::time_point) override
|
||||
std::chrono::seconds utc_offset(std::chrono::system_clock::time_point) const override
|
||||
{
|
||||
return 0s;
|
||||
}
|
||||
@ -33,13 +33,13 @@ namespace libcron
|
||||
: public ICronClock
|
||||
{
|
||||
public:
|
||||
std::chrono::system_clock::time_point now() override
|
||||
std::chrono::system_clock::time_point now() const override
|
||||
{
|
||||
auto now = system_clock::now();
|
||||
return now + utc_offset(now);
|
||||
}
|
||||
|
||||
std::chrono::seconds utc_offset(std::chrono::system_clock::time_point now) override
|
||||
std::chrono::seconds utc_offset(std::chrono::system_clock::time_point now) const override
|
||||
{
|
||||
auto t = system_clock::to_time_t(now);
|
||||
tm tm{};
|
||||
|
@ -56,7 +56,7 @@ namespace libcron
|
||||
s+= std::to_string(dt.day) + " ";
|
||||
s+= std::to_string(dt.hour) + ":";
|
||||
s+= std::to_string(dt.min) + ":";
|
||||
s+= std::to_string(dt.sec) + " UTC";
|
||||
s+= std::to_string(dt.sec);
|
||||
return s;
|
||||
}
|
||||
}
|
@ -15,4 +15,9 @@ add_executable(
|
||||
CronDataTest.cpp
|
||||
CronScheduleTest.cpp CronTest.cpp)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} libcron)
|
||||
target_link_libraries(${PROJECT_NAME} libcron)
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${OUTPUT_LOCATION}"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_LOCATION}"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_LOCATION}")
|
@ -19,190 +19,260 @@ std::string create_schedule_expiring_in(std::chrono::system_clock::time_point no
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
SCENARIO("Adding a task")
|
||||
{
|
||||
GIVEN("A Cron instance with no task")
|
||||
{
|
||||
Cron c;
|
||||
auto expired = false;
|
||||
|
||||
THEN("Starts with no task")
|
||||
{
|
||||
REQUIRE(c.count() == 0);
|
||||
}
|
||||
|
||||
WHEN("Adding a task that runs every second")
|
||||
{
|
||||
REQUIRE(c.add_schedule("A task", "* * * * * ?",
|
||||
[&expired]()
|
||||
{
|
||||
expired = true;
|
||||
})
|
||||
);
|
||||
|
||||
THEN("Count is 1 and task was not expired two seconds ago")
|
||||
{
|
||||
REQUIRE(c.count() == 1);
|
||||
c.execute_expired_tasks(c.get_clock()->now() - 2s);
|
||||
REQUIRE_FALSE(expired);
|
||||
}
|
||||
AND_THEN("Task is expired when calculating based on current time")
|
||||
{
|
||||
c.execute_expired_tasks();
|
||||
THEN("Task is expired")
|
||||
{
|
||||
REQUIRE(expired);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("Adding a task that expires in the future")
|
||||
{
|
||||
GIVEN("A Cron instance with task expiring in 3 seconds")
|
||||
{
|
||||
auto expired = false;
|
||||
|
||||
Cron c;
|
||||
REQUIRE(c.add_schedule("A task", create_schedule_expiring_in(c.get_clock()->now(), hours{0}, minutes{0}, seconds{3}),
|
||||
[&expired]()
|
||||
{
|
||||
expired = true;
|
||||
})
|
||||
);
|
||||
|
||||
THEN("Not yet expired")
|
||||
{
|
||||
REQUIRE_FALSE(expired);
|
||||
}
|
||||
AND_WHEN("When waiting one second")
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
c.execute_expired_tasks();
|
||||
THEN("Task has not yet expired")
|
||||
{
|
||||
REQUIRE_FALSE(expired);
|
||||
}
|
||||
}
|
||||
AND_WHEN("When waiting three seconds")
|
||||
{
|
||||
std::this_thread::sleep_for(3s);
|
||||
c.execute_expired_tasks();
|
||||
THEN("Task has expired")
|
||||
{
|
||||
REQUIRE(expired);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("Task priority")
|
||||
{
|
||||
GIVEN("A Cron instance with two tasks expiring in 3 and 5 seconds, added in 'reverse' order")
|
||||
{
|
||||
auto _3_second_expired = 0;
|
||||
auto _5_second_expired = 0;
|
||||
|
||||
|
||||
Cron c;
|
||||
REQUIRE(c.add_schedule("Five", create_schedule_expiring_in(c.get_clock()->now(), hours{0}, minutes{0}, seconds{5}),
|
||||
[&_5_second_expired]()
|
||||
{
|
||||
_5_second_expired++;
|
||||
})
|
||||
);
|
||||
|
||||
REQUIRE(c.add_schedule("Three", create_schedule_expiring_in(c.get_clock()->now(), hours{0}, minutes{0}, seconds{3}),
|
||||
[&_3_second_expired]()
|
||||
{
|
||||
_3_second_expired++;
|
||||
})
|
||||
);
|
||||
|
||||
THEN("Not yet expired")
|
||||
{
|
||||
REQUIRE_FALSE(_3_second_expired);
|
||||
REQUIRE_FALSE(_5_second_expired);
|
||||
}
|
||||
|
||||
WHEN("Waiting 1 seconds")
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
c.execute_expired_tasks();
|
||||
|
||||
THEN("Task has not yet expired")
|
||||
{
|
||||
REQUIRE(_3_second_expired == 0);
|
||||
REQUIRE(_5_second_expired == 0);
|
||||
}
|
||||
}
|
||||
AND_WHEN("Waiting 3 seconds")
|
||||
{
|
||||
std::this_thread::sleep_for(3s);
|
||||
c.execute_expired_tasks();
|
||||
|
||||
THEN("3 second task has expired")
|
||||
{
|
||||
REQUIRE(_3_second_expired == 1);
|
||||
REQUIRE(_5_second_expired == 0);
|
||||
}
|
||||
}
|
||||
AND_WHEN("Waiting 5 seconds")
|
||||
{
|
||||
std::this_thread::sleep_for(5s);
|
||||
c.execute_expired_tasks();
|
||||
|
||||
THEN("3 and 5 second task has expired")
|
||||
{
|
||||
REQUIRE(_3_second_expired == 1);
|
||||
REQUIRE(_5_second_expired == 1);
|
||||
}
|
||||
}
|
||||
AND_WHEN("Waiting based on the time given by the Cron instance")
|
||||
{
|
||||
std::this_thread::sleep_for(c.time_until_next());
|
||||
c.execute_expired_tasks();
|
||||
|
||||
THEN("3 second task has expired")
|
||||
{
|
||||
REQUIRE(_3_second_expired == 1);
|
||||
REQUIRE(_5_second_expired == 0);
|
||||
}
|
||||
}
|
||||
AND_WHEN("Waiting based on the time given by the Cron instance")
|
||||
{
|
||||
std::this_thread::sleep_for(c.time_until_next());
|
||||
REQUIRE(c.execute_expired_tasks() == 1);
|
||||
|
||||
std::this_thread::sleep_for(c.time_until_next());
|
||||
REQUIRE(c.execute_expired_tasks() == 1);
|
||||
|
||||
THEN("3 and 5 second task has each expired once")
|
||||
{
|
||||
REQUIRE(_3_second_expired == 1);
|
||||
REQUIRE(_5_second_expired == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//SCENARIO("Clock changes")
|
||||
//SCENARIO("Adding a task")
|
||||
//{
|
||||
// GIVEN("A Cron instance with a single task expiring in 4h")
|
||||
// GIVEN("A Cron instance with no task")
|
||||
// {
|
||||
// Cron c;
|
||||
// auto clock = c.get_clock();
|
||||
// system_clock::time_point time_from_task;
|
||||
// auto now = clock->now();
|
||||
// REQUIRE(c.add_schedule("Task", create_schedule_expiring_in(now, hours{4}, minutes{0}, seconds{0}),
|
||||
// [clock, &time_from_task]()
|
||||
// Cron<> c;
|
||||
// auto expired = false;
|
||||
//
|
||||
// THEN("Starts with no task")
|
||||
// {
|
||||
// REQUIRE(c.count() == 0);
|
||||
// }
|
||||
//
|
||||
// WHEN("Adding a task that runs every second")
|
||||
// {
|
||||
// REQUIRE(c.add_schedule("A task", "* * * * * ?",
|
||||
// [&expired]()
|
||||
// {
|
||||
// expired = true;
|
||||
// })
|
||||
// );
|
||||
//
|
||||
// THEN("Count is 1 and task was not expired two seconds ago")
|
||||
// {
|
||||
// REQUIRE(c.count() == 1);
|
||||
// c.tick(c.get_clock().now() - 2s);
|
||||
// REQUIRE_FALSE(expired);
|
||||
// }
|
||||
// AND_THEN("Task is expired when calculating based on current time")
|
||||
// {
|
||||
// c.tick();
|
||||
// THEN("Task is expired")
|
||||
// {
|
||||
// REQUIRE(expired);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//SCENARIO("Adding a task that expires in the future")
|
||||
//{
|
||||
// GIVEN("A Cron instance with task expiring in 3 seconds")
|
||||
// {
|
||||
// auto expired = false;
|
||||
//
|
||||
// Cron<> c;
|
||||
// REQUIRE(c.add_schedule("A task",
|
||||
// create_schedule_expiring_in(c.get_clock().now(), hours{0}, minutes{0}, seconds{3}),
|
||||
// [&expired]()
|
||||
// {
|
||||
// time_from_task = clock->now();
|
||||
// expired = true;
|
||||
// })
|
||||
// );
|
||||
//
|
||||
//
|
||||
// THEN("Not yet expired")
|
||||
// {
|
||||
// REQUIRE_FALSE(expired);
|
||||
// }
|
||||
// AND_WHEN("When waiting one second")
|
||||
// {
|
||||
// std::this_thread::sleep_for(1s);
|
||||
// c.tick();
|
||||
// THEN("Task has not yet expired")
|
||||
// {
|
||||
// REQUIRE_FALSE(expired);
|
||||
// }
|
||||
// }
|
||||
// AND_WHEN("When waiting three seconds")
|
||||
// {
|
||||
// std::this_thread::sleep_for(3s);
|
||||
// c.tick();
|
||||
// THEN("Task has expired")
|
||||
// {
|
||||
// REQUIRE(expired);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//}
|
||||
//
|
||||
//SCENARIO("Task priority")
|
||||
//{
|
||||
// GIVEN("A Cron instance with two tasks expiring in 3 and 5 seconds, added in 'reverse' order")
|
||||
// {
|
||||
// auto _3_second_expired = 0;
|
||||
// auto _5_second_expired = 0;
|
||||
//
|
||||
//
|
||||
// Cron<> c;
|
||||
// REQUIRE(c.add_schedule("Five",
|
||||
// create_schedule_expiring_in(c.get_clock().now(), hours{0}, minutes{0}, seconds{5}),
|
||||
// [&_5_second_expired]()
|
||||
// {
|
||||
// _5_second_expired++;
|
||||
// })
|
||||
// );
|
||||
//
|
||||
// REQUIRE(c.add_schedule("Three",
|
||||
// create_schedule_expiring_in(c.get_clock().now(), hours{0}, minutes{0}, seconds{3}),
|
||||
// [&_3_second_expired]()
|
||||
// {
|
||||
// _3_second_expired++;
|
||||
// })
|
||||
// );
|
||||
//
|
||||
// THEN("Not yet expired")
|
||||
// {
|
||||
// REQUIRE_FALSE(_3_second_expired);
|
||||
// REQUIRE_FALSE(_5_second_expired);
|
||||
// }
|
||||
//
|
||||
// WHEN("Waiting 1 seconds")
|
||||
// {
|
||||
// std::this_thread::sleep_for(1s);
|
||||
// c.tick();
|
||||
//
|
||||
// THEN("Task has not yet expired")
|
||||
// {
|
||||
// REQUIRE(_3_second_expired == 0);
|
||||
// REQUIRE(_5_second_expired == 0);
|
||||
// }
|
||||
// }
|
||||
// AND_WHEN("Waiting 3 seconds")
|
||||
// {
|
||||
// std::this_thread::sleep_for(3s);
|
||||
// c.tick();
|
||||
//
|
||||
// THEN("3 second task has expired")
|
||||
// {
|
||||
// REQUIRE(_3_second_expired == 1);
|
||||
// REQUIRE(_5_second_expired == 0);
|
||||
// }
|
||||
// }
|
||||
// AND_WHEN("Waiting 5 seconds")
|
||||
// {
|
||||
// std::this_thread::sleep_for(5s);
|
||||
// c.tick();
|
||||
//
|
||||
// THEN("3 and 5 second task has expired")
|
||||
// {
|
||||
// REQUIRE(_3_second_expired == 1);
|
||||
// REQUIRE(_5_second_expired == 1);
|
||||
// }
|
||||
// }
|
||||
// AND_WHEN("Waiting based on the time given by the Cron instance")
|
||||
// {
|
||||
// std::this_thread::sleep_for(c.time_until_next());
|
||||
// c.tick();
|
||||
//
|
||||
// THEN("3 second task has expired")
|
||||
// {
|
||||
// REQUIRE(_3_second_expired == 1);
|
||||
// REQUIRE(_5_second_expired == 0);
|
||||
// }
|
||||
// }
|
||||
// AND_WHEN("Waiting based on the time given by the Cron instance")
|
||||
// {
|
||||
// std::this_thread::sleep_for(c.time_until_next());
|
||||
// REQUIRE(c.tick() == 1);
|
||||
//
|
||||
// std::this_thread::sleep_for(c.time_until_next());
|
||||
// REQUIRE(c.tick() == 1);
|
||||
//
|
||||
// THEN("3 and 5 second task has each expired once")
|
||||
// {
|
||||
// REQUIRE(_3_second_expired == 1);
|
||||
// REQUIRE(_5_second_expired == 1);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
class TestClock
|
||||
: public ICronClock
|
||||
{
|
||||
public:
|
||||
std::chrono::system_clock::time_point now() const override
|
||||
{
|
||||
return current_time;
|
||||
}
|
||||
|
||||
std::chrono::seconds utc_offset(std::chrono::system_clock::time_point) const override
|
||||
{
|
||||
return 0s;
|
||||
}
|
||||
|
||||
void add(system_clock::duration time)
|
||||
{
|
||||
current_time += time;
|
||||
}
|
||||
|
||||
void set(system_clock::time_point new_time)
|
||||
{
|
||||
current_time = new_time;
|
||||
}
|
||||
|
||||
private:
|
||||
system_clock::time_point current_time = system_clock::now();
|
||||
|
||||
};
|
||||
|
||||
SCENARIO("Clock changes")
|
||||
{
|
||||
GIVEN("A Cron instance with a single task expiring in 4h")
|
||||
{
|
||||
Cron<TestClock> c{};
|
||||
auto& clock = c.get_clock();
|
||||
|
||||
// Midnight
|
||||
clock.set(sys_days{2018_y / 05 / 05});
|
||||
|
||||
// Every hour
|
||||
REQUIRE(c.add_schedule("Clock change task", "0 0 * * * ?", []()
|
||||
{
|
||||
})
|
||||
);
|
||||
|
||||
// https://linux.die.net/man/8/cron
|
||||
|
||||
WHEN("Clock changes <3h forward")
|
||||
{
|
||||
THEN("Task expires accordingly")
|
||||
{
|
||||
REQUIRE(c.tick() == 1);
|
||||
clock.add(minutes{30}); // 00:30
|
||||
REQUIRE(c.tick() == 0);
|
||||
clock.add(minutes{30}); // 01:00
|
||||
REQUIRE(c.tick() == 1);
|
||||
REQUIRE(c.tick() == 0);
|
||||
REQUIRE(c.tick() == 0);
|
||||
clock.add(minutes{30}); // 01:30
|
||||
REQUIRE(c.tick() == 0);
|
||||
clock.add(minutes{15}); // 01:45
|
||||
REQUIRE(c.tick() == 0);
|
||||
clock.add(minutes{15}); // 02:00
|
||||
REQUIRE(c.tick() == 1);
|
||||
}
|
||||
}
|
||||
AND_WHEN("Clock is moved forward >= 3h")
|
||||
{
|
||||
THEN("Task are rescheduled, not run")
|
||||
{
|
||||
REQUIRE(c.tick() == 1);
|
||||
std::cout << c << std::endl;
|
||||
clock.add(hours{3}); // 03:00
|
||||
REQUIRE(c.tick() == 1); // Rescheduled
|
||||
std::cout << c << std::endl;
|
||||
clock.add(minutes{15}); // 03:15
|
||||
REQUIRE(c.tick() == 1);
|
||||
std::cout << c << std::endl;
|
||||
clock.add(minutes{45}); // 04:00
|
||||
REQUIRE(c.tick() == 1);
|
||||
std::cout << c << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user