Files
Angel Ivan 2a9eabce14 Fist commit
2026-10-02 12:08:21 -06:00

166 lines
6.7 KiB
C++
Executable File

#include <unity.h>
#include "ScheduleValidator.h"
#include "TimbreService.h"
using namespace timbre_core;
void setUp(void) {}
void tearDown(void) {}
static AppState validState() {
AppState state;
state.profiles.push_back(Profile{7, "Principal"});
state.activeProfileId = 7;
state.patterns[0] = Pattern{PatternId::A, {1, 1, 1, 1, 1, 1}};
state.patterns[1] = Pattern{PatternId::B, {2, 1, 2, 1, 2, 1}};
state.patterns[2] = Pattern{PatternId::C, {1, 2, 1, 2, 1, 2}};
return state;
}
static void test_limits_and_canonical_patterns(void) {
TEST_ASSERT_EQUAL_size_t(8, kMaxProfiles);
TEST_ASSERT_EQUAL_size_t(3, kMaxPatterns);
TEST_ASSERT_EQUAL_size_t(100, kMaxSchedules);
TEST_ASSERT_EQUAL_size_t(64, kMaxHolidays);
TEST_ASSERT_EQUAL_size_t(8, kPatternQueueCapacity);
TEST_ASSERT_EQUAL_UINT8(99, kMaxPhaseDurationSeconds);
TEST_ASSERT_EQUAL_UINT16(600, kMaxPatternTotalSeconds);
TEST_ASSERT_TRUE(isPatternId(PatternId::A));
TEST_ASSERT_TRUE(isPatternId(PatternId::B));
TEST_ASSERT_TRUE(isPatternId(PatternId::C));
TEST_ASSERT_FALSE(isPatternId(static_cast<PatternId>(3)));
TEST_ASSERT_EQUAL_UINT8(22, gpioForRelay(RelayId::Main));
TEST_ASSERT_EQUAL_UINT8(23, gpioForRelay(RelayId::Reserved));
TEST_ASSERT_TRUE(isReservedRelay(RelayId::Reserved));
TEST_ASSERT_FALSE(isReservedRelay(RelayId::Main));
}
static void test_valid_state_and_phase_limits(void) {
AppState state = validState();
TEST_ASSERT_TRUE(ScheduleValidator::validate(state).valid);
state.patterns[0].phases[0] = 100;
ValidationResult result = ScheduleValidator::validate(state);
TEST_ASSERT_FALSE(result.valid);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(ValidationCode::InvalidPhaseDuration),
static_cast<std::uint8_t>(result.code));
state = validState();
state.patterns[1].phaseCount = kMaxPatternPhases + 1;
result = ScheduleValidator::validate(state);
TEST_ASSERT_FALSE(result.valid);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(ValidationCode::InvalidPhaseCount),
static_cast<std::uint8_t>(result.code));
state = validState();
state.patterns[2].phaseCount = 6;
for (std::size_t i = 0; i < 6; ++i) {
state.patterns[2].phases[i] = 99;
}
TEST_ASSERT_TRUE(ScheduleValidator::validate(state).valid);
}
static void test_schedule_references_and_stable_ids(void) {
AppState state = validState();
Schedule schedule{10, 7, PatternId::A, 8 * 60 + 30, 0x7F, true};
state.schedules.push_back(schedule);
TEST_ASSERT_TRUE(ScheduleValidator::validate(state).valid);
Schedule* found = state.schedule(10);
TEST_ASSERT_NOT_NULL(found);
TEST_ASSERT_EQUAL_UINT16(510, found->minuteOfDay);
state.schedules[0].profileId = 99;
ValidationResult result = ScheduleValidator::validate(state);
TEST_ASSERT_FALSE(result.valid);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(ValidationCode::UnknownProfile),
static_cast<std::uint8_t>(result.code));
state = validState();
state.profiles.push_back(Profile{7, "Duplicado"});
result = ScheduleValidator::validate(state);
TEST_ASSERT_FALSE(result.valid);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(ValidationCode::DuplicateProfileId),
static_cast<std::uint8_t>(result.code));
}
static void test_holiday_validation_and_capacity(void) {
AppState state = validState();
state.holidays.push_back(Holiday{1, 1, 1, true});
state.holidays.push_back(Holiday{1, 1, 2, true});
ValidationResult result = ScheduleValidator::validate(state);
TEST_ASSERT_FALSE(result.valid);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(ValidationCode::DuplicateHolidayId),
static_cast<std::uint8_t>(result.code));
state = validState();
for (std::size_t i = 0; i < 64; ++i) {
const std::uint8_t month = static_cast<std::uint8_t>((i % 12) + 1);
const std::uint8_t day = static_cast<std::uint8_t>((i / 12) + 1);
TEST_ASSERT_TRUE(state.holidays.push_back(
Holiday{static_cast<HolidayId>(i + 1), month, day, true}));
}
TEST_ASSERT_EQUAL_size_t(64, state.holidays.size());
TEST_ASSERT_TRUE(ScheduleValidator::validate(state).valid);
state.holidays[0].month = 0;
result = ScheduleValidator::validate(state);
TEST_ASSERT_FALSE(result.valid);
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(ValidationCode::InvalidHolidayDate),
static_cast<std::uint8_t>(result.code));
}
static void test_schedule_and_profile_capacity(void) {
AppState state;
for (std::size_t i = 0; i < kMaxProfiles; ++i) {
TEST_ASSERT_TRUE(state.profiles.push_back(
Profile{static_cast<ProfileId>(i + 1), "P"}));
}
state.activeProfileId = 1;
for (std::size_t i = 0; i < kMaxSchedules; ++i) {
TEST_ASSERT_TRUE(state.schedules.push_back(
Schedule{static_cast<ScheduleId>(i + 1), 1, PatternId::A, 0, 0x7F, true}));
}
TEST_ASSERT_TRUE(ScheduleValidator::validate(state).valid);
TEST_ASSERT_EQUAL_size_t(kMaxProfiles, state.profiles.capacity());
TEST_ASSERT_EQUAL_size_t(kMaxSchedules, state.schedules.capacity());
}
static void test_transaction_publishes_only_after_commit(void) {
MemoryStateRepository repository;
AppState initial = validState();
repository.seed(initial);
TimbreService service(repository);
TEST_ASSERT_TRUE(service.load());
const std::size_t before = service.state().schedules.size();
repository.setCommitSucceeds(false);
TEST_ASSERT_FALSE(service.transact([](AppState& candidate) {
candidate.schedules.push_back(Schedule{44, 7, PatternId::B, 60, 0x7F, true});
}));
TEST_ASSERT_EQUAL_size_t(before, service.state().schedules.size());
TEST_ASSERT_EQUAL_UINT8(static_cast<std::uint8_t>(ServiceCode::CommitFailed),
static_cast<std::uint8_t>(service.lastError()));
repository.setCommitSucceeds(true);
TEST_ASSERT_TRUE(service.transact([](AppState& candidate) {
candidate.schedules.push_back(Schedule{44, 7, PatternId::B, 60, 0x7F, true});
}));
TEST_ASSERT_EQUAL_size_t(before + 1, service.state().schedules.size());
TEST_ASSERT_EQUAL_UINT16(before == 0 ? 1 : 0,
service.state().schedules.back().id == 44 ? 1 : 0);
}
int main(int, char**) {
UNITY_BEGIN();
RUN_TEST(test_limits_and_canonical_patterns);
RUN_TEST(test_valid_state_and_phase_limits);
RUN_TEST(test_schedule_references_and_stable_ids);
RUN_TEST(test_holiday_validation_and_capacity);
RUN_TEST(test_schedule_and_profile_capacity);
RUN_TEST(test_transaction_publishes_only_after_commit);
return UNITY_END();
}