Files
TimbreESP32/test/test_native_pattern_engine/test_main.cpp
Angel Ivan 2a9eabce14 Fist commit
2026-10-02 12:08:21 -06:00

154 lines
5.5 KiB
C++
Executable File

#include <unity.h>
#include <vector>
#include "PatternEngine.h"
using namespace timbre_core;
void setUp(void) {}
void tearDown(void) {}
struct OutputEvent {
RelayId relay;
bool on;
};
static void test_absolute_deadlines_and_final_off(void) {
std::vector<OutputEvent> events;
PatternEngine engine([&events](RelayId relay, bool on) {
events.push_back(OutputEvent{relay, on});
});
const Pattern pattern{PatternId::A, {2, 1, 1, 1}};
TEST_ASSERT_TRUE(engine.start(pattern, 1000));
TEST_ASSERT_TRUE(engine.running());
TEST_ASSERT_TRUE(engine.outputOn());
TEST_ASSERT_EQUAL_UINT64(3000, engine.deadlineMs());
TEST_ASSERT_EQUAL_size_t(1, events.size());
TEST_ASSERT_TRUE(events[0].on);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(RelayId::Main),
static_cast<std::uint8_t>(events[0].relay));
TEST_ASSERT_FALSE(engine.update(2999));
TEST_ASSERT_TRUE(engine.outputOn());
TEST_ASSERT_TRUE(engine.update(3000));
TEST_ASSERT_FALSE(engine.outputOn());
TEST_ASSERT_EQUAL_UINT8(1, engine.activePhase());
TEST_ASSERT_EQUAL_UINT64(4000, engine.deadlineMs());
TEST_ASSERT_TRUE(engine.update(5000));
// At 5000 ms the third phase has also reached its boundary; the
// absolute-deadline engine therefore advances to the final OFF phase.
TEST_ASSERT_FALSE(engine.outputOn());
TEST_ASSERT_EQUAL_UINT8(3, engine.activePhase());
TEST_ASSERT_EQUAL_UINT64(6000, engine.deadlineMs());
TEST_ASSERT_TRUE(engine.update(7000));
TEST_ASSERT_FALSE(engine.running());
TEST_ASSERT_FALSE(engine.outputOn());
TEST_ASSERT_EQUAL_UINT8(PatternEngine::kNoPhase, engine.activePhase());
TEST_ASSERT_EQUAL_size_t(4, events.size());
TEST_ASSERT_FALSE(events.back().on);
}
static void test_clock_regression_turns_output_off(void) {
std::vector<OutputEvent> events;
PatternEngine engine([&events](RelayId relay, bool on) {
events.push_back({relay, on});
});
const Pattern pattern{PatternId::A, {2, 1}};
TEST_ASSERT_TRUE(engine.start(pattern, 1000));
TEST_ASSERT_TRUE(engine.outputOn());
TEST_ASSERT_TRUE(engine.update(500));
TEST_ASSERT_FALSE(engine.running());
TEST_ASSERT_FALSE(engine.outputOn());
TEST_ASSERT_FALSE(events.empty());
TEST_ASSERT_FALSE(events.back().on);
}
static void test_late_poll_does_not_accumulate_or_leave_on(void) {
std::vector<OutputEvent> events;
PatternEngine engine([&events](RelayId, bool on) { events.push_back({RelayId::Main, on}); });
const Pattern pattern{PatternId::B, {1, 1, 1, 1, 1, 1}};
TEST_ASSERT_TRUE(engine.start(pattern, 10));
TEST_ASSERT_TRUE(engine.update(1000000));
TEST_ASSERT_FALSE(engine.running());
TEST_ASSERT_FALSE(engine.outputOn());
TEST_ASSERT_EQUAL_UINT64(0, engine.deadlineMs());
TEST_ASSERT_FALSE(events.empty());
TEST_ASSERT_FALSE(events.back().on);
}
static void test_fixed_queue_capacity_and_absolute_request_time(void) {
std::vector<OutputEvent> events;
PatternEngine engine([&events](RelayId relay, bool on) {
events.push_back({relay, on});
});
const Pattern pattern{PatternId::C, {1, 1}};
for (std::size_t i = 0; i < 8; ++i) {
TEST_ASSERT_TRUE(engine.enqueue(pattern, 1000));
}
TEST_ASSERT_EQUAL_size_t(8, engine.queueSize());
TEST_ASSERT_FALSE(engine.enqueue(pattern, 1000));
TEST_ASSERT_EQUAL_size_t(8, engine.queueSize());
TEST_ASSERT_FALSE(engine.running());
engine.update(999);
TEST_ASSERT_FALSE(engine.running());
TEST_ASSERT_EQUAL_size_t(8, engine.queueSize());
engine.update(1000);
TEST_ASSERT_TRUE(engine.running());
TEST_ASSERT_EQUAL_size_t(7, engine.queueSize());
TEST_ASSERT_TRUE(engine.outputOn());
TEST_ASSERT_EQUAL_UINT64(2000, engine.deadlineMs());
}
static void test_invalid_pattern_id_and_zero_phase_are_safe(void) {
std::vector<OutputEvent> events;
PatternEngine engine([&events](RelayId relay, bool on) {
events.push_back({relay, on});
});
TEST_ASSERT_FALSE(engine.start(static_cast<PatternId>(99), 0));
TEST_ASSERT_FALSE(engine.running());
TEST_ASSERT_TRUE(events.empty());
const Pattern silent{PatternId::A, {0, 0}};
TEST_ASSERT_TRUE(engine.start(silent, 0));
TEST_ASSERT_FALSE(engine.running());
TEST_ASSERT_FALSE(engine.outputOn());
TEST_ASSERT_EQUAL_size_t(0, events.size());
}
static void test_reserved_gpio_is_never_driven(void) {
std::vector<OutputEvent> events;
PatternEngine engine([&events](RelayId relay, bool on) {
events.push_back({relay, on});
});
const Pattern pattern{PatternId::A, {1, 1}};
TEST_ASSERT_TRUE(engine.start(pattern, 0));
TEST_ASSERT_TRUE(engine.update(2000));
TEST_ASSERT_FALSE(engine.reservedRelayDriven());
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(RelayId::Main),
static_cast<std::uint8_t>(engine.lastOutputRelay()));
for (const OutputEvent& event : events) {
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(RelayId::Main),
static_cast<std::uint8_t>(event.relay));
}
}
int main(int, char**) {
UNITY_BEGIN();
RUN_TEST(test_absolute_deadlines_and_final_off);
RUN_TEST(test_clock_regression_turns_output_off);
RUN_TEST(test_late_poll_does_not_accumulate_or_leave_on);
RUN_TEST(test_fixed_queue_capacity_and_absolute_request_time);
RUN_TEST(test_invalid_pattern_id_and_zero_phase_are_safe);
RUN_TEST(test_reserved_gpio_is_never_driven);
return UNITY_END();
}