Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Commit

Permalink
Add hal::callback_recorder
Browse files Browse the repository at this point in the history
Resolves #502
  • Loading branch information
kammce committed Nov 11, 2022
1 parent d1cb3fa commit c75b8ec
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 1 deletion.
150 changes: 150 additions & 0 deletions include/libhal/callback_recorder.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#pragma once

#include <cstdint>
#include <functional>

namespace hal::mock {
/**
* @addtogroup mock
* @{
*/

/**
* @brief General class which will be used to allow for signature to be used and
* then split by the below class.
*
* @tparam signature function signature to be split up in the callback_recorder
* specialization
*/
template<typename signature>
class callback_recorder;

/**
* @brief Specialization of callback_recorder with the return type and arguments
* split up.
*
* @tparam return_t function's return type
* @tparam args_t function's set of arguments
*/
template<typename return_t, typename... args_t>
class callback_recorder<return_t(args_t... p_args)>
{
public:
using callback_function = std::function<return_t(args_t... p_args)>;

/**
* @brief Construct a new callback_recorder without providing a callback
*
* @tparam U - return type of the callback function
* @tparam V - type of the callback function
*/
template<typename U = return_t,
typename V = std::enable_if_t<std::is_void_v<U>>*>
callback_recorder() noexcept
{
m_callback = [this]([[maybe_unused]] args_t... p_args) { m_call_count++; };
}

/**
* @brief Construct a new callback_recorder that returns a default value
*
* @tparam U - return type of the callback function
* @tparam V - type of the callback function
* @param p_default - default value to return from callback function
* @param p_callback - when the static callback function is called, it will
* call this callback
*/
template<typename U = return_t,
typename V = std::enable_if_t<!std::is_void_v<U>>*>
callback_recorder(U p_default = U{}, V p_callback = 0) noexcept
{
m_callback =
[this, p_default, p_callback]([[maybe_unused]] args_t... p_args) -> U {
m_call_count++;
return p_default;
};
}

/**
* @brief Construct a new static callable object
*
* @param p_callback - when the static callback function is called, it will
* call this callback
*/
explicit callback_recorder(callback_function p_callback) noexcept
{
m_callback = [this,
p_callback]([[maybe_unused]] args_t... p_args) -> return_t {
m_call_count++;
return p_callback(p_args...);
};
}

/**
* @brief Get the static function's address
*
* @return auto* - static function's address
*/
[[nodiscard]] auto& callback() noexcept
{
return m_callback;
}

/**
* @brief Returns true if the callback was ever called
*
* @return true - was called
* @return false - has not been called
*/
[[nodiscard]] bool was_called() noexcept
{
return m_call_count > 0;
}

/**
* @brief Returns true if the callback was called exactly once
*
* @return true - called exactly once
* @return false - called more or less than once
*/
[[nodiscard]] bool was_called_once() noexcept
{
return m_call_count == 1;
}

/**
* @brief Returns true if the callback was called exactly n times
*
* @return true - called exactly n times
* @return false - called more or less than n times
*/
[[nodiscard]] bool was_called_n_times(std::uint32_t p_times_called) noexcept
{
return m_call_count == p_times_called;
}

/**
* @brief Get the number of calls to the handler
*
* @return auto - number of calls to the callback
*/
[[nodiscard]] auto call_count() noexcept
{
return m_call_count;
}

/**
* @brief Clear call count
*
*/
void clear_call_count() noexcept
{
m_call_count = 0;
}

private:
std::function<return_t(args_t... p_args)> m_callback;
std::uint32_t m_call_count = 0;
};
/** @} */
} // namespace hal::mock
3 changes: 2 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ add_executable(${PROJECT_NAME}
math.test.cpp
error.test.cpp
map.test.cpp
units.test.cpp)
units.test.cpp
callback_recorder.test.cpp)

target_include_directories(${PROJECT_NAME} PUBLIC . ../include)
target_compile_options(${PROJECT_NAME} PRIVATE -Werror -Wall -Wextra
Expand Down
94 changes: 94 additions & 0 deletions tests/callback_recorder.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#include <libhal/callback_recorder.hpp>

#include <boost/ut.hpp>

namespace hal {
boost::ut::suite callback_recorder_test = []() {
using namespace boost::ut;

"callback_recorder::ctor(callback_function)"_test = []() {
// Setup
int counter = 0;
const std::function<void(void)> expected_callback = [&counter](void) {
counter++;
};
hal::mock::callback_recorder<void(void)> recorder(expected_callback);

// Exercise + Verify
auto recorded_callback = recorder.callback();
expect(that % 0 == counter);
expect(that % 0 == recorder.call_count());
expect(that % false == recorder.was_called());
expect(that % false == recorder.was_called_once());

recorded_callback();
expect(that % 1 == counter);
expect(that % 1 == recorder.call_count());
expect(that % true == recorder.was_called());
expect(that % true == recorder.was_called_once());
expect(that % true == recorder.was_called_n_times(1));
recorded_callback();
expect(that % false == recorder.was_called_once());
expect(that % true == recorder.was_called_n_times(2));

recorder.clear_call_count();
expect(that % 0 == recorder.call_count());
expect(that % false == recorder.was_called());
expect(that % false == recorder.was_called_once());

recorded_callback();
expect(that % 3 == counter);
expect(that % 1 == recorder.call_count());
expect(that % true == recorder.was_called());
expect(that % true == recorder.was_called_once());
};

"callback_recorder::ctor(void)"_test = []() {
// Setup
hal::mock::callback_recorder<void(void)> recorder;

// Exercise + Verify
auto recorded_callback = recorder.callback();
expect(that % 0 == recorder.call_count());
expect(that % false == recorder.was_called());
expect(that % false == recorder.was_called_once());

recorded_callback();
expect(that % 1 == recorder.call_count());
expect(that % true == recorder.was_called());
expect(that % true == recorder.was_called_once());
expect(that % true == recorder.was_called_n_times(1));
recorded_callback();
expect(that % false == recorder.was_called_once());
expect(that % true == recorder.was_called_n_times(2));

recorder.clear_call_count();
expect(that % 0 == recorder.call_count());
expect(that % false == recorder.was_called());
expect(that % false == recorder.was_called_once());
};

"callback_recorder::ctor(default)"_test = []() {
// Setup
hal::mock::callback_recorder<int(void)> recorder(5);

// Exercise + Verify
auto recorded_callback = recorder.callback();
expect(that % 0 == recorder.call_count());
expect(that % false == recorder.was_called());
expect(that % false == recorder.was_called_once());

auto result = recorded_callback();
expect(that % 5 == result);
expect(that % 1 == recorder.call_count());
expect(that % true == recorder.was_called());
expect(that % true == recorder.was_called_once());
expect(that % true == recorder.was_called_n_times(1));

recorder.clear_call_count();
expect(that % 0 == recorder.call_count());
expect(that % false == recorder.was_called());
expect(that % false == recorder.was_called_once());
};
};
} // namespace hal

0 comments on commit c75b8ec

Please sign in to comment.