#include #include #include #include #include "../memory_store.h" #include "event_bus.h" #include "settings.h" using namespace roro; void setUp() {} void tearDown() {} void test_defaults_when_store_is_empty() { MemoryStore store; EventBus bus; Settings s(store, bus); s.load(); TEST_ASSERT_FALSE(s.getBool(Setting::SetupDone)); TEST_ASSERT_EQUAL(60, s.getInt(Setting::Brightness)); TEST_ASSERT_EQUAL(30, s.getInt(Setting::DimTimeoutS)); TEST_ASSERT_EQUAL(60, s.getInt(Setting::OffTimeoutS)); TEST_ASSERT_TRUE(s.getBool(Setting::Sound)); TEST_ASSERT_TRUE(s.getBool(Setting::ProbeMacRaw)); TEST_ASSERT_TRUE(s.getBool(Setting::WifiEnabled)); TEST_ASSERT_EQUAL_STRING("EU868", s.getString(Setting::Region).c_str()); TEST_ASSERT_EQUAL_STRING("CET-1CEST,M3.5.0,M10.5.0/3", s.getString(Setting::Timezone).c_str()); } void test_set_persists_and_survives_reload() { MemoryStore store; EventBus bus; { Settings s(store, bus); s.load(); TEST_ASSERT_TRUE(s.setInt(Setting::Brightness, 80)); TEST_ASSERT_TRUE(s.setString(Setting::LongName, "Clément")); } Settings again(store, bus); again.load(); TEST_ASSERT_EQUAL(80, again.getInt(Setting::Brightness)); TEST_ASSERT_EQUAL_STRING("Clément", again.getString(Setting::LongName).c_str()); } void test_out_of_range_values_are_rejected_and_not_stored() { MemoryStore store; EventBus bus; Settings s(store, bus); s.load(); TEST_ASSERT_FALSE(s.setInt(Setting::Brightness, 5)); // would make the screen unreadable TEST_ASSERT_FALSE(s.setInt(Setting::Brightness, 101)); TEST_ASSERT_EQUAL(60, s.getInt(Setting::Brightness)); TEST_ASSERT_EQUAL(0, store.ints.count(Settings::key(Setting::Brightness))); } void test_corrupt_stored_value_falls_back_to_default() { MemoryStore store; EventBus bus; store.ints[Settings::key(Setting::Brightness)] = 9999; Settings s(store, bus); s.load(); TEST_ASSERT_EQUAL(60, s.getInt(Setting::Brightness)); } void test_dim_timeout_must_stay_below_off_timeout() { MemoryStore store; EventBus bus; Settings s(store, bus); s.load(); TEST_ASSERT_FALSE(s.setInt(Setting::DimTimeoutS, 60)); // off is 60 TEST_ASSERT_FALSE(s.setInt(Setting::OffTimeoutS, 30)); // dim is 30 TEST_ASSERT_TRUE(s.setInt(Setting::OffTimeoutS, 120)); TEST_ASSERT_TRUE(s.setInt(Setting::DimTimeoutS, 90)); } void test_short_name_is_one_to_four_bytes() { MemoryStore store; EventBus bus; Settings s(store, bus); s.load(); TEST_ASSERT_FALSE(s.setString(Setting::ShortName, "")); TEST_ASSERT_FALSE(s.setString(Setting::ShortName, "ROROX")); TEST_ASSERT_TRUE(s.setString(Setting::ShortName, "RORO")); } void test_long_name_is_limited_to_39_bytes() { MemoryStore store; EventBus bus; Settings s(store, bus); s.load(); TEST_ASSERT_TRUE(s.setString(Setting::LongName, std::string(39, 'a'))); TEST_ASSERT_FALSE(s.setString(Setting::LongName, std::string(40, 'a'))); TEST_ASSERT_FALSE(s.setString(Setting::LongName, "")); } void test_only_known_regions_are_accepted() { MemoryStore store; EventBus bus; Settings s(store, bus); s.load(); TEST_ASSERT_FALSE(s.setString(Setting::Region, "US915")); TEST_ASSERT_TRUE(s.setString(Setting::Region, "EU868")); } void test_type_mismatch_is_rejected() { MemoryStore store; EventBus bus; Settings s(store, bus); s.load(); TEST_ASSERT_FALSE(s.setString(Setting::Brightness, "80")); TEST_ASSERT_FALSE(s.setInt(Setting::LongName, 3)); } void test_change_publishes_setting_changed_once() { MemoryStore store; EventBus bus; Settings s(store, bus); s.load(); int changes = 0; int32_t which = -1; bus.subscribe(EventType::SettingChanged, [&](const Event& e) { changes++; which = e.a; }); s.setInt(Setting::Brightness, 70); s.setInt(Setting::Brightness, 70); // same value: no event s.setInt(Setting::Brightness, 1); // invalid: no event bus.dispatch(); TEST_ASSERT_EQUAL(1, changes); TEST_ASSERT_EQUAL(static_cast(Setting::Brightness), which); } void test_transmit_requires_confirmed_region() { MemoryStore store; EventBus bus; Settings s(store, bus); s.load(); TEST_ASSERT_FALSE(s.canTransmit()); s.setBool(Setting::RegionConfirmed, true); TEST_ASSERT_TRUE(s.canTransmit()); } void test_storage_keys_fit_nvs_limit() { for (int i = 0; i < static_cast(Setting::Count); i++) TEST_ASSERT_TRUE(std::strlen(Settings::key(static_cast(i))) <= 15); } int main() { UNITY_BEGIN(); RUN_TEST(test_defaults_when_store_is_empty); RUN_TEST(test_set_persists_and_survives_reload); RUN_TEST(test_out_of_range_values_are_rejected_and_not_stored); RUN_TEST(test_corrupt_stored_value_falls_back_to_default); RUN_TEST(test_dim_timeout_must_stay_below_off_timeout); RUN_TEST(test_short_name_is_one_to_four_bytes); RUN_TEST(test_long_name_is_limited_to_39_bytes); RUN_TEST(test_only_known_regions_are_accepted); RUN_TEST(test_type_mismatch_is_rejected); RUN_TEST(test_change_publishes_setting_changed_once); RUN_TEST(test_transmit_requires_confirmed_region); RUN_TEST(test_storage_keys_fit_nvs_limit); return UNITY_END(); }