166 lines
6.7 KiB
C++
Executable File
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();
|
|
}
|