287 lines
8.5 KiB
C
287 lines
8.5 KiB
C
#include <string.h>
|
|
#include "unity.h"
|
|
#include "light/nl_engine.h"
|
|
|
|
static uint8_t last[NL_CH_COUNT];
|
|
static int calls;
|
|
|
|
static void out(const uint8_t ch[NL_CH_COUNT], void *ctx)
|
|
{
|
|
(void)ctx;
|
|
memcpy(last, ch, NL_CH_COUNT);
|
|
calls++;
|
|
}
|
|
|
|
static nl_light_state_t st(bool on, uint8_t bri, nl_light_mode_t m, uint8_t r, uint8_t g, uint8_t b, uint16_t ct)
|
|
{
|
|
nl_light_state_t s = {on, bri, m, r, g, b, ct};
|
|
return s;
|
|
}
|
|
|
|
static nl_visual_t cur(void)
|
|
{
|
|
nl_visual_t v;
|
|
nl_engine_current(&v);
|
|
return v;
|
|
}
|
|
|
|
static int sum(void)
|
|
{
|
|
int s = 0;
|
|
for (int i = 0; i < NL_CH_COUNT; i++)
|
|
s += last[i];
|
|
return s;
|
|
}
|
|
|
|
void setUp(void)
|
|
{
|
|
nl_engine_cfg_t cfg = {2700, 6500, out, NULL};
|
|
memset(last, 0xAA, sizeof last);
|
|
calls = 0;
|
|
nl_engine_init(&cfg);
|
|
}
|
|
|
|
void tearDown(void) {}
|
|
|
|
static void test_init_is_dark_and_idle(void)
|
|
{
|
|
TEST_ASSERT_TRUE(calls >= 1);
|
|
TEST_ASSERT_EQUAL_INT(0, sum());
|
|
TEST_ASSERT_EQUAL_UINT16(0, cur().bri);
|
|
TEST_ASSERT_TRUE(nl_engine_idle());
|
|
}
|
|
|
|
static void test_instant_set(void)
|
|
{
|
|
nl_light_state_t s = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 2700);
|
|
nl_engine_set(&s, 0);
|
|
TEST_ASSERT_EQUAL_UINT8(255, last[NL_CH_WW]);
|
|
TEST_ASSERT_EQUAL_UINT8(0, last[NL_CH_CW]);
|
|
TEST_ASSERT_TRUE(nl_engine_idle());
|
|
}
|
|
|
|
static void test_fade_in_is_linear_in_brightness(void)
|
|
{
|
|
nl_light_state_t s = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 2700);
|
|
nl_engine_set(&s, 1000);
|
|
nl_engine_tick(500);
|
|
TEST_ASSERT_EQUAL_UINT16(5000, cur().bri);
|
|
TEST_ASSERT_FALSE(nl_engine_idle());
|
|
nl_engine_tick(500);
|
|
TEST_ASSERT_EQUAL_UINT16(10000, cur().bri);
|
|
TEST_ASSERT_TRUE(nl_engine_idle());
|
|
TEST_ASSERT_EQUAL_UINT8(255, last[NL_CH_WW]);
|
|
}
|
|
|
|
static void test_fade_out_keeps_current_color(void)
|
|
{
|
|
nl_light_state_t red = st(true, 100, NL_MODE_COLOR, 255, 0, 0, 2700);
|
|
nl_light_state_t off = st(false, 100, NL_MODE_WHITE, 0, 0, 0, 4000);
|
|
nl_engine_set(&red, 0);
|
|
nl_engine_set(&off, 1000);
|
|
nl_engine_tick(500);
|
|
TEST_ASSERT_EQUAL(NL_MODE_COLOR, cur().mode);
|
|
TEST_ASSERT_EQUAL_UINT16(5000, cur().bri);
|
|
TEST_ASSERT_TRUE(last[NL_CH_R] > 0);
|
|
nl_engine_tick(500);
|
|
TEST_ASSERT_EQUAL_INT(0, sum());
|
|
TEST_ASSERT_TRUE(nl_engine_idle());
|
|
TEST_ASSERT_EQUAL(NL_MODE_WHITE, cur().mode); /* target values applied at the end */
|
|
}
|
|
|
|
static void test_mode_switch_passes_through_dark(void)
|
|
{
|
|
nl_light_state_t red = st(true, 100, NL_MODE_COLOR, 255, 0, 0, 2700);
|
|
nl_light_state_t white = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 4000);
|
|
nl_engine_set(&red, 0);
|
|
nl_engine_set(&white, 1000);
|
|
nl_engine_tick(250);
|
|
TEST_ASSERT_EQUAL(NL_MODE_COLOR, cur().mode);
|
|
TEST_ASSERT_EQUAL_UINT16(5000, cur().bri);
|
|
nl_engine_tick(250);
|
|
TEST_ASSERT_EQUAL_UINT16(0, cur().bri);
|
|
TEST_ASSERT_EQUAL_INT(0, sum());
|
|
nl_engine_tick(250);
|
|
TEST_ASSERT_EQUAL(NL_MODE_WHITE, cur().mode);
|
|
TEST_ASSERT_EQUAL_UINT16(5000, cur().bri);
|
|
nl_engine_tick(250);
|
|
TEST_ASSERT_EQUAL_UINT16(10000, cur().bri);
|
|
TEST_ASSERT_TRUE(nl_engine_idle());
|
|
}
|
|
|
|
static void test_leftover_time_carries_into_next_segment(void)
|
|
{
|
|
nl_light_state_t red = st(true, 100, NL_MODE_COLOR, 255, 0, 0, 2700);
|
|
nl_light_state_t white = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 4000);
|
|
nl_engine_set(&red, 0);
|
|
nl_engine_set(&white, 1000);
|
|
nl_engine_tick(750);
|
|
TEST_ASSERT_EQUAL(NL_MODE_WHITE, cur().mode);
|
|
TEST_ASSERT_EQUAL_UINT16(5000, cur().bri);
|
|
}
|
|
|
|
static void test_new_target_starts_from_current_value(void)
|
|
{
|
|
nl_light_state_t full = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 2700);
|
|
nl_light_state_t dim = st(true, 20, NL_MODE_WHITE, 0, 0, 0, 2700);
|
|
nl_engine_set(&full, 1000);
|
|
nl_engine_tick(500);
|
|
nl_engine_set(&dim, 1000);
|
|
nl_engine_tick(500);
|
|
TEST_ASSERT_EQUAL_UINT16(3500, cur().bri);
|
|
}
|
|
|
|
static void test_color_interpolates(void)
|
|
{
|
|
nl_light_state_t red = st(true, 100, NL_MODE_COLOR, 255, 0, 0, 2700);
|
|
nl_light_state_t blue = st(true, 100, NL_MODE_COLOR, 0, 0, 255, 2700);
|
|
nl_engine_set(&red, 0);
|
|
nl_engine_set(&blue, 1000);
|
|
nl_engine_tick(500);
|
|
TEST_ASSERT_INT_WITHIN(1, 128, cur().r);
|
|
TEST_ASSERT_INT_WITHIN(1, 127, cur().b);
|
|
}
|
|
|
|
static void test_blink_overrides_and_restores(void)
|
|
{
|
|
nl_engine_blink(2);
|
|
TEST_ASSERT_EQUAL_UINT8(255, last[NL_CH_WW]);
|
|
TEST_ASSERT_FALSE(nl_engine_idle());
|
|
nl_engine_tick(300);
|
|
TEST_ASSERT_EQUAL_INT(0, sum());
|
|
nl_engine_tick(300);
|
|
TEST_ASSERT_EQUAL_UINT8(255, last[NL_CH_WW]);
|
|
nl_engine_tick(300);
|
|
TEST_ASSERT_EQUAL_INT(0, sum());
|
|
nl_engine_tick(300);
|
|
TEST_ASSERT_EQUAL_INT(0, sum()); /* back to the (dark) state */
|
|
TEST_ASSERT_TRUE(nl_engine_idle());
|
|
}
|
|
|
|
static void test_pulse(void)
|
|
{
|
|
nl_engine_pulse(true);
|
|
uint8_t low = last[NL_CH_WW];
|
|
TEST_ASSERT_TRUE(low > 0);
|
|
nl_engine_tick(1500);
|
|
TEST_ASSERT_TRUE(last[NL_CH_WW] > low);
|
|
TEST_ASSERT_FALSE(nl_engine_idle());
|
|
nl_engine_pulse(false);
|
|
TEST_ASSERT_EQUAL_INT(0, sum());
|
|
TEST_ASSERT_TRUE(nl_engine_idle());
|
|
}
|
|
|
|
static void test_blink_max_clamped(void)
|
|
{
|
|
nl_engine_blink(200); /* > NL_BLINK_MAX, should clamp to 100 */
|
|
TEST_ASSERT_FALSE(nl_engine_idle());
|
|
/* After 99 blinks (99*2 phases = 198), should still be blinking */
|
|
nl_engine_tick(300 * 99 * 2);
|
|
TEST_ASSERT_FALSE(nl_engine_idle());
|
|
/* After 2 more phases to complete the last blink (total 100 blinks), should be idle */
|
|
nl_engine_tick(300 * 2);
|
|
TEST_ASSERT_TRUE(nl_engine_idle());
|
|
}
|
|
|
|
static void test_override_takes_over_output(void)
|
|
{
|
|
nl_light_state_t on = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 3000);
|
|
uint8_t ov[NL_CH_COUNT] = { 10, 20, 30, 40, 50 };
|
|
|
|
nl_engine_set(&on, 0);
|
|
nl_engine_tick(10);
|
|
TEST_ASSERT_FALSE(nl_engine_override_active());
|
|
|
|
nl_engine_override(ov);
|
|
nl_engine_tick(10);
|
|
TEST_ASSERT_TRUE(nl_engine_override_active());
|
|
TEST_ASSERT_EQUAL_UINT8(10, last[NL_CH_R]);
|
|
TEST_ASSERT_EQUAL_UINT8(50, last[NL_CH_WW]);
|
|
}
|
|
|
|
static void test_override_clear_without_fade_is_immediate(void)
|
|
{
|
|
nl_light_state_t off = st(false, 100, NL_MODE_WHITE, 0, 0, 0, 3000);
|
|
uint8_t ov[NL_CH_COUNT] = { 255, 255, 255, 255, 255 };
|
|
|
|
nl_engine_set(&off, 0);
|
|
nl_engine_tick(10);
|
|
nl_engine_override(ov);
|
|
nl_engine_tick(10);
|
|
TEST_ASSERT_EQUAL_UINT8(255, last[NL_CH_WW]);
|
|
|
|
nl_engine_override_clear(0);
|
|
nl_engine_tick(10);
|
|
TEST_ASSERT_FALSE(nl_engine_override_active());
|
|
TEST_ASSERT_EQUAL_UINT8(0, last[NL_CH_WW]); /* die Lampe war aus, also sofort dunkel */
|
|
}
|
|
|
|
static void test_override_clear_fades_back(void)
|
|
{
|
|
nl_light_state_t off = st(false, 100, NL_MODE_WHITE, 0, 0, 0, 3000);
|
|
uint8_t ov[NL_CH_COUNT] = { 0, 0, 0, 0, 200 };
|
|
|
|
nl_engine_set(&off, 0);
|
|
nl_engine_tick(10);
|
|
nl_engine_override(ov);
|
|
nl_engine_tick(10);
|
|
|
|
nl_engine_override_clear(1000);
|
|
nl_engine_tick(500);
|
|
/* auf halber Strecke zwischen 200 und 0, mit Spielraum fuer die Rundung */
|
|
TEST_ASSERT_UINT8_WITHIN(12, 100, last[NL_CH_WW]);
|
|
nl_engine_tick(500);
|
|
TEST_ASSERT_EQUAL_UINT8(0, last[NL_CH_WW]);
|
|
TEST_ASSERT_FALSE(nl_engine_override_active());
|
|
}
|
|
|
|
static void test_blink_wins_over_override(void)
|
|
{
|
|
uint8_t ov[NL_CH_COUNT] = { 0, 0, 0, 0, 0 };
|
|
|
|
nl_engine_override(ov);
|
|
nl_engine_blink(3);
|
|
nl_engine_tick(10);
|
|
/* Blink leuchtet hell weiss, obwohl der Override alles auf 0 haelt */
|
|
TEST_ASSERT_NOT_EQUAL_UINT8(0, last[NL_CH_WW]);
|
|
}
|
|
|
|
static void test_underlying_state_survives_the_override(void)
|
|
{
|
|
nl_light_state_t on = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 3000);
|
|
uint8_t ov[NL_CH_COUNT] = { 0, 0, 0, 0, 0 };
|
|
uint8_t before;
|
|
|
|
nl_engine_set(&on, 0);
|
|
nl_engine_tick(10);
|
|
before = last[NL_CH_WW];
|
|
|
|
nl_engine_override(ov);
|
|
nl_engine_tick(2000);
|
|
nl_engine_override_clear(0);
|
|
nl_engine_tick(10);
|
|
TEST_ASSERT_EQUAL_UINT8(before, last[NL_CH_WW]);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_init_is_dark_and_idle);
|
|
RUN_TEST(test_instant_set);
|
|
RUN_TEST(test_fade_in_is_linear_in_brightness);
|
|
RUN_TEST(test_fade_out_keeps_current_color);
|
|
RUN_TEST(test_mode_switch_passes_through_dark);
|
|
RUN_TEST(test_leftover_time_carries_into_next_segment);
|
|
RUN_TEST(test_new_target_starts_from_current_value);
|
|
RUN_TEST(test_color_interpolates);
|
|
RUN_TEST(test_blink_overrides_and_restores);
|
|
RUN_TEST(test_pulse);
|
|
RUN_TEST(test_blink_max_clamped);
|
|
RUN_TEST(test_override_takes_over_output);
|
|
RUN_TEST(test_override_clear_without_fade_is_immediate);
|
|
RUN_TEST(test_override_clear_fades_back);
|
|
RUN_TEST(test_blink_wins_over_override);
|
|
RUN_TEST(test_underlying_state_survives_the_override);
|
|
return UNITY_END();
|
|
}
|