Mercurial > louis > mq > lightsd
changeset 465:63f84bfc2981
wip: add testing for the power off transition with a couple fixes
- NULL bulbs properly handled everywhere;
- properly use ctx->duration instead of the skewed effect->duration value;
- remove the dependency on bulb.c in tests_utils.c
author | Louis Opter <kalessin@kalessin.fr> |
---|---|
date | Mon, 13 Jun 2016 16:50:46 +0400 |
parents | f4f5f51b5907 |
children | d88add6a4037 |
files | add_power_transition.patch |
diffstat | 1 files changed, 422 insertions(+), 13 deletions(-) [+] |
line wrap: on
line diff
--- a/add_power_transition.patch Sun Jun 05 17:02:19 2016 +0500 +++ b/add_power_transition.patch Mon Jun 13 16:50:46 2016 +0400 @@ -894,7 +894,7 @@ new file mode 100644 --- /dev/null +++ b/effects/power_transition.c -@@ -0,0 +1,303 @@ +@@ -0,0 +1,305 @@ +// Copyright (c) 2015, Louis Opter <kalessin@kalessin.fr> +// +// This file is part of lighstd. @@ -1015,15 +1015,17 @@ + struct lgtd_lifx_bulb *bulb; + uint8_t *device_id = target->device_id; + bulb = lgtd_effect_power_transition_off_possibly_get_bulb(device_id); -+ struct lgtd_lifx_packet_light_color pkt = { -+ .hue = bulb->state.hue, -+ .saturation = bulb->state.saturation, -+ .brightness = 0, -+ .kelvin = bulb->state.kelvin, -+ .transition = duration -+ }; -+ lgtd_lifx_wire_encode_light_color(&pkt); -+ lgtd_router_send_to_device(bulb, LGTD_LIFX_SET_LIGHT_COLOR, &pkt); ++ if (bulb) { ++ struct lgtd_lifx_packet_light_color pkt = { ++ .hue = bulb->state.hue, ++ .saturation = bulb->state.saturation, ++ .brightness = 0, ++ .kelvin = bulb->state.kelvin, ++ .transition = duration ++ }; ++ lgtd_lifx_wire_encode_light_color(&pkt); ++ lgtd_router_send_to_device(bulb, LGTD_LIFX_SET_LIGHT_COLOR, &pkt); ++ } + } +} + @@ -1057,7 +1059,7 @@ + switch (effect->apply_cnt) { + case 0: + lgtd_effect_power_transition_off_set_brightness_to_zero( -+ &ctx->targets, effect->duration ++ &ctx->targets, ctx->duration + ); + break; + case 1: @@ -1202,7 +1204,7 @@ new file mode 100644 --- /dev/null +++ b/effects/power_transition.h -@@ -0,0 +1,48 @@ +@@ -0,0 +1,51 @@ +// Copyright (c) 2015, Louis Opter <kalessin@kalessin.fr> +// +// This file is part of lighstd. @@ -1245,6 +1247,9 @@ + +struct lgtd_effect_power_transition_ctx { + struct lgtd_effect_power_transition_target_list targets; ++ // For power off transitions the effect duration might be a bit longer than ++ // the actual transition duration (see RESTORE_LIGHT_COLOR_DELAY_MSECS) ++ // hence why the context also contains the duration: + int duration; +}; + @@ -2574,6 +2579,24 @@ #include "mock_client_buf.h" #include "mock_daemon.h" #include "mock_gateway.h" +diff --git a/tests/core/tests_utils.c b/tests/core/tests_utils.c +--- a/tests/core/tests_utils.c ++++ b/tests/core/tests_utils.c +@@ -74,7 +74,13 @@ + .as_scalar = LGTD_BIG_ENDIAN_SYSTEM ? + htobe64(addr) << 16 : htobe64(addr) >> 16 + }; +- struct lgtd_lifx_bulb *bulb = lgtd_lifx_bulb_open(gw, bulb_addr.as_array); ++ ++ struct lgtd_lifx_bulb *bulb = calloc(1, sizeof(*bulb)); ++ bulb->gw = gw; ++ memcpy(bulb->addr, bulb_addr.as_array, sizeof(bulb->addr)); ++ RB_INSERT(lgtd_lifx_bulb_map, &lgtd_lifx_bulbs_table, bulb); ++ LGTD_STATS_ADD_AND_UPDATE_PROCTITLE(bulbs, 1); ++ bulb->last_light_state_at = lgtd_time_monotonic_msecs(); + + SLIST_INSERT_HEAD(&gw->bulbs, bulb, link_by_gw); + diff --git a/tests/effects/CMakeLists.txt b/tests/effects/CMakeLists.txt new file mode 100644 --- /dev/null @@ -2613,7 +2636,7 @@ new file mode 100644 --- /dev/null +++ b/tests/effects/power_transition/CMakeLists.txt -@@ -0,0 +1,18 @@ +@@ -0,0 +1,21 @@ +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} @@ -2621,7 +2644,10 @@ + +ADD_CORE_LIBRARY( + test_effects_power_transition STATIC ++ ${LIGHTSD_SOURCE_DIR}/core/stats.c + ${LIGHTSD_SOURCE_DIR}/core/utils.c ++ ../../core/tests_utils.c ++ ../../core/tests_shims.c +) + +FUNCTION(ADD_POWER_TRANSITION_TEST TEST_SOURCE) @@ -2665,6 +2691,377 @@ + + return 0; +} +diff --git a/tests/effects/power_transition/test_power_transition_off_apply_callback.c b/tests/effects/power_transition/test_power_transition_off_apply_callback.c +new file mode 100644 +--- /dev/null ++++ b/tests/effects/power_transition/test_power_transition_off_apply_callback.c +@@ -0,0 +1,201 @@ ++#include "effects/power_transition.c" ++ ++#include "core/mock_daemon.h" ++#include "core/mock_effect.h" ++#include "core/mock_log.h" ++#define MOCKED_LGTD_ROUTER_SEND_TO_DEVICE ++#include "core/mock_router.h" ++#define MOCKED_LGTD_LIFX_BULB_GET ++#include "lifx/mock_bulb.h" ++#include "lifx/mock_gateway.h" ++#include "lifx/mock_tagging.h" ++#include "lifx/mock_wire_proto.h" ++ ++#include "core/tests_utils.h" ++ ++enum { TEST_TRANSITION_DURATION = 120 }; ++ ++struct lgtd_lifx_bulb *mock_bulb_1 = NULL; ++struct lgtd_lifx_bulb *mock_bulb_2 = NULL; ++enum { MOCK_BULB_2_INITIAL_BRIGHTNESS = 0xafaf }; ++struct lgtd_effect *mock_effect = NULL; ++ ++static int lifx_bulb_get_call_count = 0; ++ ++struct lgtd_lifx_bulb *lgtd_lifx_bulb_get(const uint8_t *addr) ++{ ++ if (lifx_bulb_get_call_count++ >= 2) { ++ return NULL; ++ } ++ ++ if (memcmp(addr, mock_bulb_2->addr, sizeof(mock_bulb_2->addr))) { ++ char bufs[2][LGTD_LIFX_ADDR_STRLEN]; ++ lgtd_errx( ++ 1, "bulb_get: expected bulb %s but got %s instead", ++ LGTD_IEEE8023MACTOA(mock_bulb_2->addr, bufs[0]), ++ LGTD_IEEE8023MACTOA(addr, bufs[1]) ++ ); ++ } ++ ++ return mock_bulb_2; ++} ++ ++static int router_send_to_device_call_count = 0; ++ ++void ++lgtd_router_send_to_device(struct lgtd_lifx_bulb *bulb, ++ enum lgtd_lifx_packet_type pkt_type, ++ void *pkt) ++{ ++ if (bulb != mock_bulb_2) { ++ char bufs[2][LGTD_LIFX_ADDR_STRLEN]; ++ lgtd_errx( ++ 1, "router_send_to_device: expected bulb %s but got %s instead", ++ LGTD_IEEE8023MACTOA(mock_bulb_2->addr, bufs[0]), ++ LGTD_IEEE8023MACTOA(bulb->addr, bufs[1]) ++ ); ++ } ++ ++ struct lgtd_lifx_packet_light_color *pkt_brightness; ++ struct lgtd_lifx_packet_power *pkt_power; ++ switch (router_send_to_device_call_count++) { ++ case 0: // set brightness to zero ++ pkt_brightness = pkt; ++ if (pkt_type != LGTD_LIFX_SET_LIGHT_COLOR) { ++ lgtd_errx( ++ 1, "brightness transition: pkt_type = %d (expected %d)", ++ pkt_type, LGTD_LIFX_SET_LIGHT_COLOR ++ ); ++ } ++ if (pkt_brightness->hue != mock_bulb_2->state.hue) { ++ lgtd_errx( ++ 1, "brightness transition: hue = %hu (expected %hu)", ++ pkt_brightness->hue, mock_bulb_2->state.hue ++ ); ++ } ++ if (pkt_brightness->saturation != mock_bulb_2->state.saturation) { ++ lgtd_errx( ++ 1, "brightness transition: saturation = %hu (expected %hu)", ++ pkt_brightness->saturation, mock_bulb_2->state.saturation ++ ); ++ } ++ if (pkt_brightness->brightness != 0) { ++ lgtd_errx( ++ 1, "brightness transition: brightness = %hu (expected 0)", ++ pkt_brightness->brightness ++ ); ++ } ++ if (pkt_brightness->kelvin != bulb->state.kelvin) { ++ lgtd_errx( ++ 1, "brightness transition: kelvin = %hu (expected 0)", ++ pkt_brightness->kelvin ++ ); ++ } ++ if (pkt_brightness->transition != TEST_TRANSITION_DURATION) { ++ lgtd_errx( ++ 1, "brightness transition: transition = %u (expected %u)", ++ pkt_brightness->transition, TEST_TRANSITION_DURATION ++ ); ++ } ++ break; ++ case 1: // set power off ++ pkt_power = pkt; ++ if (pkt_type != LGTD_LIFX_SET_POWER_STATE) { ++ lgtd_errx( ++ 1, "power off: pkt_type = %d (expected %d)", ++ pkt_type, LGTD_LIFX_SET_POWER_STATE ++ ); ++ } ++ if (pkt_power->power != LGTD_LIFX_POWER_OFF) { ++ lgtd_errx( ++ 1, "powering off: power = %hu (expected %u)", ++ pkt_power->power, LGTD_LIFX_POWER_OFF ++ ); ++ } ++ break; ++ default: ++ break; ++ } ++} ++ ++int ++main(void) ++{ ++ struct lgtd_lifx_gateway *gw_1 = lgtd_tests_insert_mock_gateway(1); ++ mock_bulb_1 = lgtd_tests_insert_mock_bulb(gw_1, 1); ++ mock_bulb_2 = lgtd_tests_insert_mock_bulb(gw_1, 2); ++ mock_bulb_2->state.brightness = MOCK_BULB_2_INITIAL_BRIGHTNESS; ++ mock_bulb_2->state.hue = 0xbae; ++ mock_bulb_2->state.saturation = 0x4444; ++ mock_bulb_2->state.kelvin = 3500; ++ ++ struct lgtd_effect_power_transition_ctx ctx = { ++ .duration = TEST_TRANSITION_DURATION ++ }; ++ struct lgtd_effect_power_transition_target transition_target = { ++ .initial_brightness = mock_bulb_2->state.brightness ++ }; ++ memcpy( ++ &transition_target.device_id, ++ mock_bulb_2->addr, ++ sizeof(mock_bulb_2->addr) ++ ); ++ SLIST_INSERT_HEAD(&ctx.targets, &transition_target, link); ++ ++ mock_effect = calloc(1, sizeof(*mock_effect)); ++ union lgtd_effect_ctx effect_ctx = { .as_ptr = &ctx }; ++ mock_effect->ctx = effect_ctx; ++ ++ // 1st application: should set brightness to zero on bulb_2: ++ // 2nd application: should turn the power off on bulb_2: ++ for (int i = 0; i++ != 2; mock_effect->apply_cnt++) { ++ lgtd_effect_power_transition_off_apply_callback(mock_effect); ++ if (lifx_bulb_get_call_count != i) { ++ lgtd_errx( ++ 1, "lifx_bulb_get_call_count = %d (expected %d)", ++ lifx_bulb_get_call_count, i ++ ); ++ } ++ if (router_send_to_device_call_count != i) { ++ lgtd_errx( ++ 1, "router_send_to_device_call_count = %d (expected %d)", ++ router_send_to_device_call_count, i ++ ); ++ } ++ } ++ ++ // shouldn't do anything: ++ lgtd_effect_power_transition_off_apply_callback(mock_effect); ++ if (lifx_bulb_get_call_count != 2) { ++ lgtd_errx( ++ 1, "lifx_bulb_get_call_count = %d (expected 2)", ++ lifx_bulb_get_call_count ++ ); ++ } ++ if (router_send_to_device_call_count != 2) { ++ lgtd_errx( ++ 1, "router_send_to_device_call_count = %d (expected 2)", ++ router_send_to_device_call_count ++ ); ++ } ++ ++ // repeat the first test case with lgtd_lifx_bulb_get returning NULL: ++ mock_effect->apply_cnt = 0; ++ for (int i = 0; i++ != 2; mock_effect->apply_cnt++) { ++ lgtd_effect_power_transition_off_apply_callback(mock_effect); ++ if (lifx_bulb_get_call_count != 2 + i) { ++ lgtd_errx( ++ 1, "lifx_bulb_get_call_count = %d (expected %d)", ++ lifx_bulb_get_call_count, 2 + i ++ ); ++ } ++ if (router_send_to_device_call_count != 2) { ++ lgtd_errx( ++ 1, "router_send_to_device_call_count = %d (expected 2)", ++ router_send_to_device_call_count ++ ); ++ } ++ } ++ ++ return 0; ++} +diff --git a/tests/effects/power_transition/test_power_transition_off_duration_callback.c b/tests/effects/power_transition/test_power_transition_off_duration_callback.c +new file mode 100644 +--- /dev/null ++++ b/tests/effects/power_transition/test_power_transition_off_duration_callback.c +@@ -0,0 +1,160 @@ ++#include "effects/power_transition.c" ++ ++#include "core/mock_daemon.h" ++#include "core/mock_effect.h" ++#include "core/mock_log.h" ++#define MOCKED_LGTD_ROUTER_SEND_TO_DEVICE ++#include "core/mock_router.h" ++#define MOCKED_LGTD_LIFX_BULB_GET ++#include "lifx/mock_bulb.h" ++#include "lifx/mock_gateway.h" ++#include "lifx/mock_tagging.h" ++#include "lifx/mock_wire_proto.h" ++ ++#include "core/tests_utils.h" ++ ++struct lgtd_lifx_bulb *mock_bulb_1 = NULL; ++struct lgtd_lifx_bulb *mock_bulb_2 = NULL; ++enum { MOCK_BULB_2_INITIAL_BRIGHTNESS = 0xafaf }; ++struct lgtd_effect *mock_effect = NULL; ++ ++static int lifx_bulb_get_call_count = 0; ++ ++struct lgtd_lifx_bulb *lgtd_lifx_bulb_get(const uint8_t *addr) ++{ ++ if (lifx_bulb_get_call_count++) { ++ return NULL; ++ } ++ ++ if (memcmp(addr, mock_bulb_2->addr, sizeof(mock_bulb_2->addr))) { ++ char bufs[2][LGTD_LIFX_ADDR_STRLEN]; ++ lgtd_errx( ++ 1, "bulb_get: expected bulb %s but got %s instead", ++ LGTD_IEEE8023MACTOA(mock_bulb_2->addr, bufs[0]), ++ LGTD_IEEE8023MACTOA(addr, bufs[1]) ++ ); ++ } ++ ++ return mock_bulb_2; ++} ++ ++static int router_send_to_device_call_count = 0; ++ ++void ++lgtd_router_send_to_device(struct lgtd_lifx_bulb *bulb, ++ enum lgtd_lifx_packet_type pkt_type, ++ void *pkt) ++{ ++ router_send_to_device_call_count++; ++ ++ if (bulb != mock_bulb_2) { ++ char bufs[2][LGTD_LIFX_ADDR_STRLEN]; ++ lgtd_errx( ++ 1, "router_send_to_device: expected bulb %s but got %s instead", ++ LGTD_IEEE8023MACTOA(mock_bulb_2->addr, bufs[0]), ++ LGTD_IEEE8023MACTOA(bulb->addr, bufs[1]) ++ ); ++ } ++ ++ struct lgtd_lifx_packet_light_color *pkt_brightness = pkt; ++ if (pkt_type != LGTD_LIFX_SET_LIGHT_COLOR) { ++ lgtd_errx( ++ 1, "router_send_to_device: pkt_type %u (expected %U)", ++ pkt_type, LGTD_LIFX_SET_LIGHT_COLOR ++ ); ++ } ++ if (pkt_brightness->hue != mock_bulb_2->state.hue) { ++ lgtd_errx( ++ 1, "brightness restore: hue = %hu (expected %hu)", ++ pkt_brightness->hue, mock_bulb_2->state.hue ++ ); ++ } ++ if (pkt_brightness->saturation != mock_bulb_2->state.saturation) { ++ lgtd_errx( ++ 1, "brightness restore: saturation = %hu (expected %hu)", ++ pkt_brightness->saturation, mock_bulb_2->state.saturation ++ ); ++ } ++ if (pkt_brightness->brightness != MOCK_BULB_2_INITIAL_BRIGHTNESS) { ++ lgtd_errx( ++ 1, "brightness restore: brightness = %hu (expected %u)", ++ pkt_brightness->brightness, MOCK_BULB_2_INITIAL_BRIGHTNESS ++ ); ++ } ++ if (pkt_brightness->kelvin != bulb->state.kelvin) { ++ lgtd_errx( ++ 1, "brightness restore: kelvin = %hu (expected 0)", ++ pkt_brightness->kelvin ++ ); ++ } ++ if (pkt_brightness->transition != 0) { ++ lgtd_errx( ++ 1, "brightness restore: transition = %u (expected 0)", ++ pkt_brightness->transition ++ ); ++ } ++} ++ ++static struct lgtd_effect_power_transition_ctx * ++allocate_ctx(void) ++{ ++ struct lgtd_effect_power_transition_ctx *ctx = calloc(1, sizeof(*ctx)); ++ struct lgtd_effect_power_transition_target *transition_target = calloc( ++ 1, sizeof(*ctx) ++ ); ++ transition_target->initial_brightness = MOCK_BULB_2_INITIAL_BRIGHTNESS; ++ memcpy( ++ &transition_target->device_id, ++ mock_bulb_2->addr, ++ sizeof(mock_bulb_2->addr) ++ ); ++ SLIST_INSERT_HEAD(&ctx->targets, transition_target, link); ++ ++ return ctx; ++} ++ ++int ++main(void) ++{ ++ struct lgtd_lifx_gateway *gw_1 = lgtd_tests_insert_mock_gateway(1); ++ mock_bulb_1 = lgtd_tests_insert_mock_bulb(gw_1, 1); ++ mock_bulb_2 = lgtd_tests_insert_mock_bulb(gw_1, 2); ++ mock_bulb_2->state.brightness = 0; ++ mock_bulb_2->state.hue = 0xbae; ++ mock_bulb_2->state.saturation = 0x4444; ++ mock_bulb_2->state.kelvin = 3500; ++ ++ mock_effect = calloc(1, sizeof(*mock_effect)); ++ ++ mock_effect->ctx.as_ptr = allocate_ctx(); ++ lgtd_effect_power_transition_off_duration_callback(mock_effect); ++ if (lifx_bulb_get_call_count != 1) { ++ lgtd_errx( ++ 1, "bulb_get_call_count = %d (expected 1)", lifx_bulb_get_call_count ++ ); ++ } ++ if (router_send_to_device_call_count != 1) { ++ lgtd_errx( ++ 1, "bulb_get_call_count = %d (expected 1)", ++ router_send_to_device_call_count ++ ); ++ } ++ ++ // if the bulb was turned before the end of the transition make sure that's ++ // handled correctly: ++ mock_effect->ctx.as_ptr = allocate_ctx(); ++ lgtd_effect_power_transition_off_duration_callback(mock_effect); ++ if (lifx_bulb_get_call_count != 2) { ++ lgtd_errx( ++ 1, "bulb_get_call_count = %d (expected 2)", lifx_bulb_get_call_count ++ ); ++ } ++ if (router_send_to_device_call_count != 1) { ++ lgtd_errx( ++ 1, "bulb_get_call_count = %d (expected 1)", ++ router_send_to_device_call_count ++ ); ++ } ++ ++ return 0; ++} diff --git a/tests/effects/power_transition/test_power_transition_off_possibly_get_bulb.c b/tests/effects/power_transition/test_power_transition_off_possibly_get_bulb.c new file mode 100644 --- /dev/null @@ -2718,3 +3115,15 @@ + + return 0; +} +diff --git a/tests/lifx/mock_bulb.h b/tests/lifx/mock_bulb.h +--- a/tests/lifx/mock_bulb.h ++++ b/tests/lifx/mock_bulb.h +@@ -1,5 +1,8 @@ + #pragma once + ++struct lgtd_lifx_bulb_map lgtd_lifx_bulbs_table = ++ RB_INITIALIZER(&lgtd_lifx_bulbs_table); ++ + #ifndef MOCKED_LGTD_LIFX_BULB_GET + struct lgtd_lifx_bulb * + lgtd_lifx_bulb_get(const uint8_t *addr)