diff --git a/core/testutil/testdevice.h b/core/testutil/testdevice.h new file mode 100644 index 0000000..cfd3ae9 --- /dev/null +++ b/core/testutil/testdevice.h @@ -0,0 +1,106 @@ +#pragma once + +#include "../../device.h" +#include "../backlight/color.h" +#include "../../common/constants.h" + +namespace testutil +{ + +/** + * Device implementation for testing purposes. + */ +class TestDeviceBase : public Device +{ +public: + virtual void sleep_millis(const int) { } + virtual void sleep_micros(const int) { } + virtual void gpio_setup(const uint8_t, const PinMode) { } + virtual void gpio_write(const uint8_t, const PinState) { } + virtual PinState gpio_read(const uint8_t) { return {}; } + + virtual void set_keyboard_key1(const uint8_t) { } + virtual void set_keyboard_key2(const uint8_t) { } + virtual void set_keyboard_key3(const uint8_t) { } + virtual void set_keyboard_key4(const uint8_t) { } + virtual void set_keyboard_key5(const uint8_t) { } + virtual void set_keyboard_key6(const uint8_t) { } + virtual void set_keyboard_modifier(const uint16_t) { } + virtual void set_keyboard_media(const uint16_t) { } + virtual void keyboard_send() { } + + // TODO may need more work + // virtual bool sd_read(const char* filename, char* buffer, const uint32_t buffer_size) { } + + virtual uint16_t get_keyboard_leds() { return 0; } + + virtual void serial_begin(const uint32_t) { } + virtual void serial_print(const char*) { } + + virtual void serial_print(uint8_t) { } + virtual void serial_print(uint16_t) { } + virtual void serial_print(uint32_t) { } + virtual void serial_print(int8_t) { } + virtual void serial_print(int16_t) { } + virtual void serial_print(int32_t) { } + + virtual void serial_println(const char*) { } + virtual void serial_println(uint8_t) { } + virtual void serial_println(uint16_t) { } + virtual void serial_println(uint32_t) { } + virtual void serial_println(int8_t) { } + virtual void serial_println(int16_t) { } + virtual void serial_println(int32_t) { } + + virtual void start_timer() { } + virtual uint32_t get_timer_micros() { return 0; } + + /** + * Milliseconds since the device was started. + */ + virtual uint32_t millis() const { return 0; } + + virtual void set_led(uint16_t, uint8_t, uint8_t, uint8_t) { } + virtual void update_leds() { } + +}; + +class TestTimerLEDDevice : public TestDeviceBase +{ +public: + TestTimerLEDDevice() + { + for (int i = 0; i < common::constants::TOTAL_NUM_LEDS; ++i) + { + led_colors_r[i] = 0; + led_colors_g[i] = 0; + led_colors_b[i] = 0; + } + } + + uint32_t current_millis = 0; + uint32_t millis() const override + { + return current_millis; + } + + bool leds_updated = true; + void update_leds() override + { + leds_updated = true; + } + + uint8_t led_colors_r[common::constants::TOTAL_NUM_LEDS]; + uint8_t led_colors_g[common::constants::TOTAL_NUM_LEDS]; + uint8_t led_colors_b[common::constants::TOTAL_NUM_LEDS]; + + void set_led(uint16_t index, uint8_t r, uint8_t g, uint8_t b) override + { + led_colors_r[index] = r; + led_colors_g[index] = g; + led_colors_b[index] = b; + } +}; + +} + diff --git a/core/testutil/testutilities.h b/core/testutil/testutilities.h new file mode 100644 index 0000000..c32ddd9 --- /dev/null +++ b/core/testutil/testutilities.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace testutil +{ + +inline bool float_equals(float f1, float f2, float delta = 0.00001) +{ + return fabs(f1 - f2) < delta; +} + +} diff --git a/core/util/test/test_timer.cpp b/core/util/test/test_timer.cpp new file mode 100644 index 0000000..14b0eb3 --- /dev/null +++ b/core/util/test/test_timer.cpp @@ -0,0 +1,49 @@ +#include +#include "../../testutil/testdevice.h" +#include "../../testutil/testutilities.h" +#include "../timer.h" + + +TEST_CASE("Test timer", "[Timer]") +{ + testutil::TestTimerLEDDevice device; + + core::util::Timer timer; + timer.duration = 100; + + // the timer should be able to restart without reinitialization + for (int i = 0; i < 2; ++i) + { + const uint32_t time_offset = i * 200; + device.current_millis = 0 + time_offset; + timer.start(device); + + timer.update(device); + // no time has passed yet + CHECK(timer.remaining_time() == 100); + CHECK(testutil::float_equals(timer.progress(), 0.0f)); + CHECK(!timer.is_finished()); + + device.current_millis = 50 + time_offset; + + timer.update(device); + // 50 milliseconds have passed + CHECK(timer.remaining_time() == 50); + CHECK(testutil::float_equals(timer.progress(), 0.5f)); + CHECK(!timer.is_finished()); + + device.current_millis = 99 + time_offset; + + timer.update(device); + CHECK(timer.remaining_time() == 1); + CHECK(testutil::float_equals(timer.progress(), 0.99f)); + CHECK(!timer.is_finished()); + + device.current_millis = 101 + time_offset; + + timer.update(device); + CHECK(timer.remaining_time() == 0); + CHECK(testutil::float_equals(timer.progress(), 1.0f)); + CHECK(timer.is_finished()); + } +} diff --git a/core/util/timer.cpp b/core/util/timer.cpp index 3382906..97bc756 100644 --- a/core/util/timer.cpp +++ b/core/util/timer.cpp @@ -4,38 +4,28 @@ namespace core::util { -void Timer::start(Device& device) +void Timer::start(const Device& device) { running = true; - remainingTime = duration; + time_remaining = duration; start_time = device.millis(); } -void Timer::stop() -{ - running = false; -} - -void Timer::update(Device& device) +void Timer::update(const Device& device) { if (running) { uint32_t current_time = device.millis(); uint32_t elapsed = current_time - start_time; - remainingTime = duration - elapsed; + time_remaining = duration - elapsed; if (elapsed >= duration) { running = false; - remainingTime = 0; + time_remaining = 0; } } } -bool Timer::is_running() const -{ - return running; -} - bool Timer::is_finished() const { return !running; @@ -43,12 +33,12 @@ bool Timer::is_finished() const uint32_t Timer::remaining_time() const { - return remainingTime; + return time_remaining; } float Timer::progress() const { - return 1.0f - static_cast(remainingTime) / static_cast(duration); + return 1.0f - static_cast(time_remaining) / static_cast(duration); } } diff --git a/core/util/timer.h b/core/util/timer.h index 083fc99..d7fe08e 100644 --- a/core/util/timer.h +++ b/core/util/timer.h @@ -6,20 +6,46 @@ namespace core::util { +// Note: the reason we cannot have Device& as a field in the Timer struct is because +// we would need to have a non-default constructor, which causes a problem for LEDState, +// which is in an array so it cannot be initialized using a non-default constructor. + struct Timer { - void start(Device& device); - void stop(); - void update(Device& device); - bool is_running() const; + /** + * Starts the timer, resetting the timer remaining to the duration. + */ + void start(const Device& device); + + /** + * Updates the internal state of the timer, run this before reading the timer state. + */ + void update(const Device& device); + + /** + * Whether the timer duration has elapsed. + */ bool is_finished() const; + + /** + * The number of milliseconds remaining. + */ uint32_t remaining_time() const; + + /** + * The percentage amount of time between the start and the duration. + * 0 means just started, 1 means finished. + */ float progress() const; + + /** + * The timer duration in milliseconds. + */ uint32_t duration; private: - uint32_t start_time; - uint32_t remainingTime; + uint32_t start_time = 0; + uint32_t time_remaining = 0; bool running = false; };