Compare commits
7 Commits
0698081d7b
...
a7495db03f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7495db03f | ||
|
|
0271b0d0de | ||
|
|
ddb4931920 | ||
|
|
a8175db127 | ||
|
|
29ffd478ef | ||
|
|
80a4f39f7b | ||
|
|
72961f2750 |
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||
|
||||
set(SKULLC_VERSION 0.1.0)
|
||||
set(SKULLC_VERSION 0.2.0)
|
||||
|
||||
project(skullc
|
||||
VERSION ${SKULLC_VERSION}
|
||||
@ -19,6 +19,7 @@ include(skullc-install)
|
||||
|
||||
add_subdirectory(Peripherals)
|
||||
add_subdirectory(Utility)
|
||||
add_subdirectory(CppTick)
|
||||
add_subdirectory(Messaging)
|
||||
|
||||
if(SKULLC_WITH_TESTS)
|
||||
|
||||
26
CppTick/CMakeLists.txt
Normal file
26
CppTick/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||
|
||||
add_library(cpptick STATIC
|
||||
Src/timer.cpp
|
||||
)
|
||||
|
||||
add_library(skullc::cpptick ALIAS cpptick)
|
||||
|
||||
target_include_directories(cpptick
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
|
||||
set_target_properties(cpptick
|
||||
PROPERTIES
|
||||
CXX_STANDARD 20
|
||||
CXX_STANDARD_REQUIRED TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(cpptick
|
||||
PUBLIC
|
||||
skullc::utility
|
||||
)
|
||||
|
||||
skullc_install_packages(skullc cpptick ${SKULLC_VERSION})
|
||||
118
CppTick/Inc/cpptick/argstore.hpp
Normal file
118
CppTick/Inc/cpptick/argstore.hpp
Normal file
@ -0,0 +1,118 @@
|
||||
//
|
||||
// Created by erki on 24/10/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace cpptick
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
concept TrivialClass = std::is_trivial_v<T> && std::is_trivially_destructible_v<T>;
|
||||
|
||||
template<typename T>
|
||||
concept CopyableClass = !TrivialClass<T> && std::copy_constructible<T>;
|
||||
|
||||
template<typename T>
|
||||
concept SlotArgument = TrivialClass<T> || CopyableClass<T>;
|
||||
|
||||
struct ArgStorage
|
||||
{
|
||||
std::array<char, sizeof(int) * 12> buffer;
|
||||
std::array<char*, 12> pointer_buffer;
|
||||
std::array<char*, 12>::iterator pointer_buffer_head;
|
||||
std::size_t space_remaining;
|
||||
|
||||
ArgStorage() = default;
|
||||
ArgStorage(const ArgStorage&) = default;
|
||||
ArgStorage(ArgStorage&&) = default;
|
||||
|
||||
void reset()
|
||||
{
|
||||
space_remaining = buffer.size();
|
||||
pointer_buffer_head = pointer_buffer.begin();
|
||||
*pointer_buffer_head = buffer.data();
|
||||
}
|
||||
|
||||
template<TrivialClass T>
|
||||
void pushArg(T&& arg)
|
||||
{
|
||||
if (pointer_buffer_head == pointer_buffer.end() || space_remaining == 0)
|
||||
std::exit(1);
|
||||
|
||||
void* memory_location = *pointer_buffer_head;
|
||||
|
||||
if (std::align(alignof(T), sizeof(T), memory_location, space_remaining) == nullptr)
|
||||
std::exit(2);
|
||||
|
||||
std::memcpy(memory_location, &arg, sizeof(T));
|
||||
pointer_buffer_head++;
|
||||
if (pointer_buffer_head != pointer_buffer.end())
|
||||
{
|
||||
*pointer_buffer_head = (char*) memory_location + sizeof(T);
|
||||
space_remaining -= sizeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
template<CopyableClass T>
|
||||
void pushArg(T&& arg)
|
||||
{
|
||||
if (pointer_buffer_head == pointer_buffer.end() || space_remaining == 0)
|
||||
std::exit(1);
|
||||
|
||||
void* memory_location = *pointer_buffer_head;
|
||||
|
||||
if (std::align(alignof(T), sizeof(T), memory_location, space_remaining) == nullptr)
|
||||
std::exit(2);
|
||||
|
||||
new (memory_location) T{arg};
|
||||
pointer_buffer_head++;
|
||||
if (pointer_buffer_head != pointer_buffer.end())
|
||||
{
|
||||
*pointer_buffer_head = (char*) memory_location + sizeof(T);
|
||||
space_remaining -= sizeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
template<TrivialClass T>
|
||||
void cleanUp(const std::size_t)
|
||||
{}
|
||||
|
||||
template<CopyableClass T>
|
||||
void cleanUp(const std::size_t index)
|
||||
{
|
||||
T* item = reinterpret_cast<T*>(pointer_buffer[index]);
|
||||
item->~T();
|
||||
}
|
||||
|
||||
template<typename... Args, size_t... Is>
|
||||
void cleanUp(std::index_sequence<Is...>)
|
||||
{
|
||||
(cleanUp<Args>(Is), ...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void cleanUp()
|
||||
{
|
||||
cleanUp<Args...>(std::make_index_sequence<sizeof...(Args)>{});
|
||||
}
|
||||
|
||||
template<SlotArgument T>
|
||||
T& at(const std::size_t idx)
|
||||
{
|
||||
return *((T*) pointer_buffer[idx]);
|
||||
}
|
||||
|
||||
template<SlotArgument T>
|
||||
const T& at(const std::size_t idx) const
|
||||
{
|
||||
return *((T*) pointer_buffer[idx]);
|
||||
}
|
||||
};
|
||||
|
||||
}// namespace cpptick
|
||||
10
CppTick/Inc/cpptick/cpptick.hpp
Normal file
10
CppTick/Inc/cpptick/cpptick.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
//
|
||||
// Created by erki on 24/10/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cpptick/argstore.hpp"
|
||||
#include "cpptick/scheduler.hpp"
|
||||
#include "cpptick/slot.hpp"
|
||||
#include "cpptick/timer.hpp"
|
||||
123
CppTick/Inc/cpptick/scheduler.hpp
Normal file
123
CppTick/Inc/cpptick/scheduler.hpp
Normal file
@ -0,0 +1,123 @@
|
||||
//
|
||||
// Created by erki on 24/10/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include <utility_function.hpp>
|
||||
#include <utility_ringbuffer.hpp>
|
||||
|
||||
#include "cpptick/argstore.hpp"
|
||||
#include "cpptick/timer.hpp"
|
||||
|
||||
namespace cpptick
|
||||
{
|
||||
|
||||
struct BaseScheduler
|
||||
{
|
||||
using StoredCall = std::pair<Utility::IFunction<void(ArgStorage&)>*, ArgStorage>;
|
||||
Utility::Ringbuffer<StoredCall, 12> stored_calls;
|
||||
|
||||
using StoredTimer = std::pair<std::uint32_t, Timer*>;
|
||||
std::array<StoredTimer, 12> stored_timers;
|
||||
|
||||
BaseScheduler()
|
||||
{
|
||||
stored_timers.fill({-1u, nullptr});
|
||||
}
|
||||
|
||||
virtual ~BaseScheduler() = default;
|
||||
|
||||
void tick()
|
||||
{
|
||||
if (auto start = stored_timers.begin();
|
||||
start->first != -1u)
|
||||
{
|
||||
checkInvokeTimers();
|
||||
}
|
||||
|
||||
if (!stored_calls.empty())
|
||||
{
|
||||
auto* f = stored_calls.begin()->first;
|
||||
auto& args = stored_calls.begin()->second;
|
||||
(*f)(args);
|
||||
stored_calls.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
ArgStorage& storeCall(Utility::IFunction<void(ArgStorage&)>* call)
|
||||
{
|
||||
// @todo: handle overflow...
|
||||
|
||||
// still constructs double.
|
||||
stored_calls.emplace_back(call, ArgStorage{});
|
||||
auto& storage = stored_calls.back().second;
|
||||
storage.reset();
|
||||
return storage;
|
||||
}
|
||||
|
||||
virtual void storeTimer(Timer* timer) = 0;
|
||||
virtual void checkInvokeTimers() = 0;
|
||||
};
|
||||
|
||||
template<typename HAL>
|
||||
struct Scheduler : BaseScheduler
|
||||
{
|
||||
Scheduler() = default;
|
||||
|
||||
void storeTimer(Timer* timer) override
|
||||
{
|
||||
const std::uint32_t current_time = HAL::getMillis();
|
||||
const std::uint32_t time_to_run = timer->expirationTime(current_time);
|
||||
|
||||
auto empty_slot = std::find_if(stored_timers.begin(), stored_timers.end(),
|
||||
[](const StoredTimer& timer) -> bool {
|
||||
return timer.first == -1u;
|
||||
});
|
||||
|
||||
if (empty_slot == stored_timers.end())
|
||||
return;
|
||||
|
||||
*empty_slot = {time_to_run, timer};
|
||||
std::sort(stored_timers.begin(), stored_timers.end());
|
||||
}
|
||||
|
||||
void checkInvokeTimers() override
|
||||
{
|
||||
const std::uint32_t current_time = HAL::getMillis();
|
||||
|
||||
bool timers_executed = false;
|
||||
for (auto timer = stored_timers.begin(); timer->first != -1u; timer++)
|
||||
{
|
||||
if (timer->first <= current_time)
|
||||
{
|
||||
timers_executed = true;
|
||||
storeCall(timer->second->getSlot());
|
||||
|
||||
if (timer->second->type == Timer::ONE_SHOT)
|
||||
{
|
||||
timer->first = -1u;
|
||||
timer->second = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
timer->first = timer->second->expirationTime(current_time);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (timers_executed)
|
||||
{
|
||||
std::sort(stored_timers.begin(), stored_timers.end());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}// namespace cpptick
|
||||
105
CppTick/Inc/cpptick/slot.hpp
Normal file
105
CppTick/Inc/cpptick/slot.hpp
Normal file
@ -0,0 +1,105 @@
|
||||
//
|
||||
// Created by erki on 12/09/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include <utility_function.hpp>
|
||||
#include <utility_ringbuffer.hpp>
|
||||
|
||||
#include "cpptick/argstore.hpp"
|
||||
#include "cpptick/scheduler.hpp"
|
||||
|
||||
namespace cpptick
|
||||
{
|
||||
|
||||
template<typename>
|
||||
struct Slot;
|
||||
|
||||
template<typename R, SlotArgument... Args>
|
||||
struct Slot<R(Args...)>
|
||||
{
|
||||
std::array<Utility::IFunction<R(Args...)>*, 12> signals;
|
||||
BaseScheduler* scheduler;
|
||||
|
||||
Utility::FunctionOwned<Slot<R(Args...)>, void(ArgStorage&)> invoke_ptr;
|
||||
|
||||
Slot() = delete;
|
||||
explicit Slot(BaseScheduler* sched)
|
||||
: scheduler(sched), invoke_ptr(*this, &Slot<R(Args...)>::callUp)
|
||||
{
|
||||
signals.fill(nullptr);
|
||||
}
|
||||
|
||||
Slot(const Slot&) = delete;
|
||||
Slot(Slot&&) = delete;
|
||||
Slot& operator=(const Slot&) = delete;
|
||||
Slot& operator=(Slot&&) = delete;
|
||||
|
||||
void connect(Utility::IFunction<R(Args...)>* signal)
|
||||
{
|
||||
for (auto*& callable : signals)
|
||||
{
|
||||
if (callable == nullptr)
|
||||
{
|
||||
callable = signal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Utility::IFunction<R(Args...)>* connect(R (*func)(Args...))
|
||||
{
|
||||
auto* f = new Utility::Function<R(Args...)>(func);
|
||||
|
||||
connect(f);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
template<typename Source>
|
||||
Utility::IFunction<R(Args...)>* connect(Source& src, R (Source::*func)(Args...))
|
||||
{
|
||||
auto* f = new Utility::FunctionOwned<Source, R(Args...)>(src, func);
|
||||
|
||||
connect(f);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
template<size_t... Is>
|
||||
void callUpFunction(Utility::IFunction<R(Args...)>* func, ArgStorage& args, std::index_sequence<Is...>)
|
||||
{
|
||||
(*func)(std::forward<Args>(args.at<Args>(Is))...);
|
||||
}
|
||||
|
||||
void callUpFunction(Utility::IFunction<R(Args...)>* func, ArgStorage& args)
|
||||
{
|
||||
callUpFunction(func, args, std::make_index_sequence<sizeof...(Args)>{});
|
||||
}
|
||||
|
||||
void callUp(ArgStorage& args)
|
||||
{
|
||||
for (auto* f : signals)
|
||||
{
|
||||
if (f)
|
||||
callUpFunction(f, args);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
args.cleanUp<Args...>();
|
||||
}
|
||||
|
||||
void invoke(Args... args)
|
||||
{
|
||||
ArgStorage& storage = scheduler->storeCall(&invoke_ptr);
|
||||
(storage.pushArg(std::forward<Args>(args)), ...);
|
||||
}
|
||||
};
|
||||
|
||||
}// namespace cpptick
|
||||
68
CppTick/Inc/cpptick/timer.hpp
Normal file
68
CppTick/Inc/cpptick/timer.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
//
|
||||
// Created by erki on 24/10/23.
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <utility_function.hpp>
|
||||
|
||||
#include "cpptick/argstore.hpp"
|
||||
|
||||
namespace cpptick
|
||||
{
|
||||
|
||||
struct BaseScheduler;
|
||||
|
||||
struct Timer
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
ONE_SHOT,
|
||||
PERIODIC
|
||||
};
|
||||
|
||||
Timer(BaseScheduler* scheduler, const std::uint32_t timeout_ms, const Type type)
|
||||
: scheduler_(scheduler), period_ms(timeout_ms), type(type)
|
||||
{}
|
||||
|
||||
Timer() = delete;
|
||||
Timer(const Timer&) = delete;
|
||||
Timer(Timer&&) = delete;
|
||||
Timer& operator=(const Timer&) = delete;
|
||||
Timer& operator=(Timer&&) = delete;
|
||||
|
||||
void start();
|
||||
|
||||
template<typename T>
|
||||
void setSlot(T& slot)
|
||||
{
|
||||
setSlot(&slot.invoke_ptr);
|
||||
}
|
||||
|
||||
void setSlot(Utility::IFunction<void(ArgStorage&)>* slot)
|
||||
{
|
||||
slot_ = slot;
|
||||
}
|
||||
|
||||
Utility::IFunction<void(ArgStorage&)>* getSlot()
|
||||
{
|
||||
return slot_;
|
||||
}
|
||||
|
||||
std::uint32_t expirationTime(const std::uint32_t current_time) const
|
||||
{
|
||||
return period_ms + current_time;
|
||||
}
|
||||
|
||||
std::uint32_t period_ms;
|
||||
Type type;
|
||||
|
||||
private:
|
||||
BaseScheduler* scheduler_;
|
||||
Utility::IFunction<void(ArgStorage&)>* slot_ = nullptr;
|
||||
};
|
||||
|
||||
}// namespace cpptick
|
||||
17
CppTick/Src/timer.cpp
Normal file
17
CppTick/Src/timer.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by erki on 24/10/23.
|
||||
//
|
||||
|
||||
#include "cpptick/timer.hpp"
|
||||
|
||||
#include "cpptick/scheduler.hpp"
|
||||
|
||||
namespace cpptick
|
||||
{
|
||||
|
||||
void Timer::start()
|
||||
{
|
||||
scheduler_->storeTimer(this);
|
||||
}
|
||||
|
||||
}// namespace cpptick
|
||||
2
CppTick/cpptick-config.cmake.in
Normal file
2
CppTick/cpptick-config.cmake.in
Normal file
@ -0,0 +1,2 @@
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/skullc-cpptick-config-version.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/skullc-cpptick-targets.cmake)
|
||||
@ -31,6 +31,8 @@ add_executable(tests
|
||||
enum_helpers.cpp
|
||||
bytes.cpp
|
||||
filters.cpp
|
||||
cpptick.cpp
|
||||
notnull.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(tests
|
||||
@ -39,11 +41,12 @@ target_link_libraries(tests
|
||||
skullc::messaging
|
||||
skullc::peripherals
|
||||
Catch2::Catch2
|
||||
skullc::cpptick
|
||||
)
|
||||
|
||||
set_target_properties(tests
|
||||
PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD 20
|
||||
)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib)
|
||||
|
||||
268
Tests/cpptick.cpp
Normal file
268
Tests/cpptick.cpp
Normal file
@ -0,0 +1,268 @@
|
||||
//
|
||||
// Created by erki on 12/09/23.
|
||||
//
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include "cpptick/cpptick.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
int callback_count = 0;
|
||||
|
||||
void callbackByOne()
|
||||
{
|
||||
callback_count += 1;
|
||||
}
|
||||
|
||||
void callbackByN(int to_add)
|
||||
{
|
||||
callback_count += to_add;
|
||||
}
|
||||
|
||||
struct TestHal
|
||||
{
|
||||
static std::uint32_t getMillis()
|
||||
{
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::milliseconds;
|
||||
using std::chrono::system_clock;
|
||||
|
||||
return std::uint32_t(
|
||||
duration_cast<milliseconds>(system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
}
|
||||
};
|
||||
|
||||
}// namespace
|
||||
|
||||
|
||||
TEST_CASE("Slot calls function properly with void args.", "[cpptick]")
|
||||
{
|
||||
cpptick::Scheduler<TestHal> scheduler;
|
||||
|
||||
cpptick::Slot<void()> slot(&scheduler);
|
||||
auto* f = slot.connect(callbackByOne);
|
||||
|
||||
callback_count = 0;
|
||||
REQUIRE(callback_count == 0);
|
||||
|
||||
SECTION("invoke() calls the slot appropriately.")
|
||||
{
|
||||
slot.invoke();
|
||||
CHECK(callback_count == 0);
|
||||
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 1);
|
||||
|
||||
SECTION("Second tick doesn't invoke the slot again.")
|
||||
{
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 1);
|
||||
}
|
||||
|
||||
SECTION("Second invoke() will call the slot again.")
|
||||
{
|
||||
slot.invoke();
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 2);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Multiple calls queue properly.")
|
||||
{
|
||||
const int count = 3;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
slot.invoke();
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
CHECK(callback_count == i);
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
delete f;
|
||||
}
|
||||
|
||||
TEST_CASE("Slot calls function properly with args.", "[cpptick]")
|
||||
{
|
||||
cpptick::Scheduler<TestHal> scheduler;
|
||||
|
||||
cpptick::Slot<void(int)> slot(&scheduler);
|
||||
auto* f = slot.connect(callbackByN);
|
||||
|
||||
callback_count = 0;
|
||||
REQUIRE(callback_count == 0);
|
||||
|
||||
SECTION("invoke() passes argument appropriately.")
|
||||
{
|
||||
const int to_add = 5;
|
||||
slot.invoke(to_add);
|
||||
CHECK(callback_count == 0);
|
||||
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == to_add);
|
||||
}
|
||||
|
||||
delete f;
|
||||
}
|
||||
|
||||
TEST_CASE("Timer will be invoked.", "[cpptick]")
|
||||
{
|
||||
cpptick::Scheduler<TestHal> scheduler;
|
||||
|
||||
cpptick::Slot<void()> slot(&scheduler);
|
||||
auto* f = slot.connect(callbackByOne);
|
||||
|
||||
callback_count = 0;
|
||||
REQUIRE(callback_count == 0);
|
||||
|
||||
cpptick::Timer timer(&scheduler, 100, cpptick::Timer::ONE_SHOT);
|
||||
timer.setSlot(slot);
|
||||
|
||||
SECTION("Timer will be invoked after 100 milliseconds.")
|
||||
{
|
||||
timer.start();
|
||||
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 0);
|
||||
|
||||
SECTION("Timer won't be executed prematurely.")
|
||||
{
|
||||
std::this_thread::sleep_for(80ms);
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 0);
|
||||
}
|
||||
|
||||
SECTION("Time will be executed after period elapsed.")
|
||||
{
|
||||
std::this_thread::sleep_for(110ms);
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 1);
|
||||
|
||||
SECTION("Single shot timer isn't executed again.")
|
||||
{
|
||||
CHECK(callback_count == 1);
|
||||
|
||||
std::this_thread::sleep_for(110ms);
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("An unstarted timer does not run.")
|
||||
{
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 0);
|
||||
|
||||
std::this_thread::sleep_for(110ms);
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 0);
|
||||
}
|
||||
|
||||
delete f;
|
||||
}
|
||||
|
||||
TEST_CASE("Periodic timers will be invoked repeatedly.", "[cpptick]")
|
||||
{
|
||||
cpptick::Scheduler<TestHal> scheduler;
|
||||
|
||||
cpptick::Slot<void()> slot(&scheduler);
|
||||
auto* f = slot.connect(callbackByOne);
|
||||
|
||||
callback_count = 0;
|
||||
REQUIRE(callback_count == 0);
|
||||
|
||||
cpptick::Timer timer(&scheduler, 100, cpptick::Timer::PERIODIC);
|
||||
timer.setSlot(slot);
|
||||
|
||||
SECTION("Timer will be invoked after 100 milliseconds.")
|
||||
{
|
||||
timer.start();
|
||||
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 0);
|
||||
|
||||
SECTION("Timer won't be executed prematurely.")
|
||||
{
|
||||
std::this_thread::sleep_for(80ms);
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 0);
|
||||
}
|
||||
|
||||
SECTION("Time will be executed after period elapsed.")
|
||||
{
|
||||
std::this_thread::sleep_for(110ms);
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 1);
|
||||
|
||||
SECTION("Periodic timer is executed again.")
|
||||
{
|
||||
CHECK(callback_count == 1);
|
||||
|
||||
std::this_thread::sleep_for(110ms);
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete f;
|
||||
}
|
||||
|
||||
TEST_CASE("Sequential timers operate appropriately.", "[cpptick]")
|
||||
{
|
||||
cpptick::Scheduler<TestHal> scheduler;
|
||||
|
||||
cpptick::Slot<void()> slot_a(&scheduler);
|
||||
auto* f = slot_a.connect(callbackByOne);
|
||||
|
||||
cpptick::Timer timer_a(&scheduler, 100, cpptick::Timer::PERIODIC);
|
||||
timer_a.setSlot(slot_a);
|
||||
|
||||
auto callback_by_two = []() -> void {
|
||||
callback_count += 2;
|
||||
};
|
||||
cpptick::Slot<void()> slot_b(&scheduler);
|
||||
auto* ff = slot_b.connect(callback_by_two);
|
||||
|
||||
cpptick::Timer timer_b(&scheduler, 50, cpptick::Timer::ONE_SHOT);
|
||||
timer_b.setSlot(slot_b);
|
||||
|
||||
callback_count = 0;
|
||||
REQUIRE(callback_count == 0);
|
||||
|
||||
timer_a.start();
|
||||
timer_b.start();
|
||||
|
||||
SECTION("Shorter timer is called first.", "[cpptick]")
|
||||
{
|
||||
std::this_thread::sleep_for(60ms);
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 2);
|
||||
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 2);
|
||||
|
||||
SECTION("Longer timer is executed afterwards.", "[cpptick]")
|
||||
{
|
||||
std::this_thread::sleep_for(50ms);
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 3);
|
||||
}
|
||||
}
|
||||
|
||||
delete f;
|
||||
delete ff;
|
||||
}
|
||||
59
Tests/notnull.cpp
Normal file
59
Tests/notnull.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
//
|
||||
// Created by erki on 25/10/23.
|
||||
//
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#ifdef NDEBUG
|
||||
#undef NDEBUG
|
||||
#define NDEBUG_WAS_DEFINED
|
||||
#endif
|
||||
|
||||
#include "utility_assert.hpp"
|
||||
#include "utility_notnull.hpp"
|
||||
|
||||
#ifdef NDEBUG_WAS_DEFINED
|
||||
#define NDEBUG
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::atomic<bool> assert_flag = false;
|
||||
|
||||
void assertCallback(const char*, const char*, const int)
|
||||
{
|
||||
assert_flag = true;
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
TEST_CASE("Assert is tripped if nullptr passed.", "[utility],[notnull]")
|
||||
{
|
||||
assert_flag = false;
|
||||
Utility::Assert::setHandler(assertCallback);
|
||||
REQUIRE(assert_flag == false);
|
||||
|
||||
auto call = [](Utility::NotNull<int> i) -> void {};
|
||||
|
||||
call(nullptr);
|
||||
CHECK(assert_flag == true);
|
||||
}
|
||||
|
||||
TEST_CASE("Assert is not tripped if valid pointer is passed.", "[utility],[notnull]")
|
||||
{
|
||||
assert_flag = false;
|
||||
Utility::Assert::setHandler(assertCallback);
|
||||
REQUIRE(assert_flag == false);
|
||||
|
||||
auto call = [](Utility::NotNull<int> i) -> int {
|
||||
return *i;
|
||||
};
|
||||
|
||||
int a = 5;
|
||||
const int test = call(&a);
|
||||
CHECK(assert_flag == false);
|
||||
CHECK(a == test);
|
||||
}
|
||||
51
Utility/Inc/utility_notnull.hpp
Normal file
51
Utility/Inc/utility_notnull.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
//
|
||||
// Created by erki on 25/10/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utility_assert.hpp"
|
||||
|
||||
namespace Utility
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct NotNull
|
||||
{
|
||||
NotNull(T* ptr)
|
||||
: ptr_(ptr)
|
||||
{
|
||||
SKULLC_ASSERT_DEBUG(ptr_ != nullptr);
|
||||
}
|
||||
|
||||
NotNull() = delete;
|
||||
NotNull(const NotNull&) = delete;
|
||||
NotNull(NotNull&&) = delete;
|
||||
NotNull& operator=(const NotNull&) = delete;
|
||||
NotNull& operator=(NotNull&&) = delete;
|
||||
|
||||
const T& operator*() const
|
||||
{
|
||||
return *ptr_;
|
||||
}
|
||||
|
||||
T& operator*()
|
||||
{
|
||||
return *ptr_;
|
||||
}
|
||||
|
||||
const T* operator->() const noexcept
|
||||
{
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
T* operator->() noexcept
|
||||
{
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
private:
|
||||
T* ptr_;
|
||||
};
|
||||
|
||||
}// namespace Utility
|
||||
Loading…
x
Reference in New Issue
Block a user