changeset 462:cd963f484771

Finish a first test case on that effect module It's not perfect but eh. Also, the effect module is a bit more generic and verbose now. We want that since it's the low level api on top which all sorts of effect will be built.
author Louis Opter <kalessin@kalessin.fr>
date Tue, 31 May 2016 10:23:57 +0500
parents b19134003e8f
children c87d8c4ee935
files add_power_transition.patch
diffstat 1 files changed, 285 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/add_power_transition.patch	Sat May 28 19:00:30 2016 -0700
+++ b/add_power_transition.patch	Tue May 31 10:23:57 2016 +0500
@@ -1,5 +1,5 @@
 # HG changeset patch
-# Parent  f2d3c102353fd3d3ca1ab2a8a27d7961458a25a6
+# Parent  37abecb2b0d3417349a2081a541cfbe4609f6892
 Add a transition argument to the power functions
 
 Unlike LIFX's implementation, lightsd will properly get the bulbs to
@@ -17,7 +17,7 @@
 diff --git a/CMakeLists.txt b/CMakeLists.txt
 --- a/CMakeLists.txt
 +++ b/CMakeLists.txt
-@@ -99,6 +99,7 @@
+@@ -98,6 +98,7 @@
  
  ADD_SUBDIRECTORY(compat)
  ADD_SUBDIRECTORY(core)
@@ -59,7 +59,7 @@
 new file mode 100644
 --- /dev/null
 +++ b/core/effect.c
-@@ -0,0 +1,171 @@
+@@ -0,0 +1,214 @@
 +// Copyright (c) 2015, Louis Opter <kalessin@kalessin.fr>
 +//
 +// This file is part of lighstd.
@@ -115,6 +115,10 @@
 +{
 +    struct lgtd_effect *effect = (struct lgtd_effect *)ctx.as_ptr;
 +    if (effect->duration_cb) {
++        lgtd_info(
++            "calling duration callback for effect %s, id=%p",
++            effect->name, effect
++        );
 +        effect->duration_cb(effect);
 +    }
 +    lgtd_timer_stop(timer);
@@ -134,7 +138,7 @@
 +        // maybe the computer was sleepy
 +        if (diff > LGTD_EFFECT_STALE_THRESHOLD_MSECS) {
 +            lgtd_warnx(
-+                "stopping stale effect %s created %jums ago, id=%p, "
++                "stopping stale periodic effect %s created %jums ago, id=%p, "
 +                "duration=%dms",
 +                effect->name, (uintmax_t)diff, effect, effect->duration
 +            );
@@ -143,6 +147,10 @@
 +        }
 +    }
 +
++    lgtd_info(
++        "calling apply callback for effect %s, id=%p", effect->name, effect
++    );
++    assert(effect->apply_cb);
 +    effect->apply_cb(effect);
 +    effect->apply_cnt++;
 +}
@@ -157,10 +165,10 @@
 +                  union lgtd_effect_ctx ctx)
 +{
 +    assert(name);
++    assert(duration >= 0);
 +    assert(timer_ms >= 0);
-+    assert(timer_ms < duration);
-+    if (timer_ms) {
-+        assert(apply_cb);
++    if (timer_ms && duration) {
++        assert(timer_ms < duration);
 +    }
 +
 +    struct lgtd_effect *effect = calloc(1, sizeof(*effect));
@@ -191,24 +199,59 @@
 +        }
 +    }
 +
-+    if (timer_ms) {
++    if (timer_ms) { // periodic or delayed effect
++        assert(apply_cb);
++        if (!apply_cb) {
++            lgtd_warnx(
++                "cannot create periodic effect %s without a callback", name
++            );
++            goto err;
++        }
++        assert(duration || (timer_flags & LGTD_TIMER_PERSISTENT));
++        if (!duration && !(timer_flags & LGTD_TIMER_PERSISTENT)) {
++            lgtd_warnx(
++                "cannot create infinite effect %s without a persistent timer",
++                name
++            );
++            goto err;
++        }
 +        effect->timer = lgtd_timer_start(
 +            timer_flags, timer_ms, lgtd_effect_timer_callback, timer_ctx
 +        );
 +        if (!effect->timer) {
 +            goto err;
 +        }
-+        lgtd_info(
-+            "starting effect %s, id=%p, duration=%dms, tick=%dms",
-+            name, effect, duration, timer_ms
-+        );
-+    } else {
-+        lgtd_info(
-+            "starting effect %s, id=%p, duration=%dms", name, effect, duration
-+        );
++        if (duration) {
++            lgtd_info(
++                "starting effect %s, id=%p, tick=%dms, duration=%dms",
++                name, effect, timer_ms, duration
++            );
++        } else {
++            lgtd_info(
++                "starting effect %s, id=%p, tick=%dms, duration=infinite",
++                name, effect, timer_ms
++            );
++        }
++    } else { // finite or instant effect
++        if (duration) {
++            lgtd_info(
++                "starting effect %s, id=%p, duration=%dms", name, effect, duration
++            );
++        } else {
++            lgtd_info(
++                "starting effect %s, id=%p, duration=instant", name, effect
++            );
++        }
 +        if (effect->apply_cb) {
 +            effect->apply_cb(effect);
 +        }
++        if (!duration) { // instant effect
++            if (effect->duration_cb) {
++                effect->duration_cb(effect);
++            }
++            lgtd_effect_stop(effect);
++            return NULL;
++        }
 +    }
 +
 +    return effect;
@@ -283,7 +326,7 @@
 +    return (uintptr_t)effect;
 +}
 +
-+struct lgtd_effect *lgtd_effect_start(const char *,
++struct lgtd_effect *lgtd_effect_start(const char *, // name
 +                                      int, // duration
 +                                      void (*)(const struct lgtd_effect *), // duration cb
 +                                      int, // timer flags
@@ -804,22 +847,23 @@
  -----------------
  
 -.. function:: power_off(target)
+-
+-   Power off the given bulb(s).
 +.. function:: power_off(target[, transition])
  
--   Power off the given bulb(s).
+-.. function:: power_on(target)
 +   Power off the given bulb(s) with an optional transition.
  
--.. function:: power_on(target)
+-   Power on the given bulb(s).
 +   :param int transition: Optional time in ms it will take for the bulb to turn
 +                          off.
  
--   Power on the given bulb(s).
+-.. function:: power_toggle(target)
 +.. function:: power_on(target[, transition])
  
--.. function:: power_toggle(target)
+-   Power on (if they are off) or power off (if they are on) the given bulb(s).
 +   Power on the given bulb(s) with an optional transition.
- 
--   Power on (if they are off) or power off (if they are on) the given bulb(s).
++
 +   :param int transition: Optional time in ms it will take for the bulb to turn
 +                          on.
 +
@@ -1381,41 +1425,241 @@
 new file mode 100644
 --- /dev/null
 +++ b/tests/core/effect/test_effect_start.c
-@@ -0,0 +1,38 @@
+@@ -0,0 +1,238 @@
 +#include "core/effect.c"
 +
 +#include "mock_log.h"
++#define MOCKED_LGTD_TIMER_START
++#define MOCKED_LGTD_TIMER_STOP
 +#include "mock_timer.h"
 +
-+union lgtd_effect_ctx TEST_EFFECT_CTX = { .as_uint = 0 };
-+
 +enum {
-+    FINITE_WITHOUT_CALLBACK,    // duration != 0
-+    FINITE_WITH_CALLBACK,
-+    INSTANT,    // duration == 0
-+    PERIODIC,   // timer_ms != 0
-+    ONE_SHOT,   // timer_ms == 0
-+} test_case = 0;;
++    FINITE_WITHOUT_CALLBACKS = 0,   // timer_ms == 0, duration != 0
++    FINITE_WITH_CALLBACKS,
++    INSTANT_WITHOUT_CALLBACKS,      // duration == 0
++    INSTANT_WITH_CALLBACKS,         // duration == 0
++    PERIODIC_INFINITE,              // timer_ms != 0, duration == 0
++    PERIODIC_FINITE,                // timer_ms != 0, duration != 0
++    TEST_CASES_COUNT,
++} test_case = FINITE_WITHOUT_CALLBACKS;
++
++static void test_apply_callback(const struct lgtd_effect *);
++static void test_duration_callback(const struct lgtd_effect *);
 +
-+static int test_apply_callback_call_count = 0;
++const struct test_parameters {
++    const char              *name;
++    int                     duration;
++    void                    (*duration_cb)(const struct lgtd_effect *);
++    int                     timer_flags;
++    int                     timer_ms;
++    void                    (*apply_cb)(const struct lgtd_effect *);
++    union lgtd_effect_ctx   ctx;
++    int                     expected_timer_start_calls;
++    int                     expected_duration_callback_calls;
++    int                     expected_apply_callback_calls;
++    bool                    expected_effect;
++} test_cases_parameters[] = {
++    {
++        .name = "FINITE_WITHOUT_CALLBACKS",
++        .duration = 420,
++        .ctx = { .as_uint = 1 },
++        .expected_timer_start_calls = 1,
++        .expected_effect = true,
++    },
++    {
++        .name = "FINITE_WITH_CALLBACKS",
++        .duration = 419,
++        .duration_cb = test_duration_callback,
++        .apply_cb = test_apply_callback,
++        .ctx = { .as_uint = 2 },
++        .expected_timer_start_calls = 1,
++        .expected_apply_callback_calls = 1,
++        .expected_effect = true,
++    },
++    {
++        .name = "INSTANT_WITHOUT_CALLBACKS",
++        .expected_effect = false,
++        .ctx = { .as_uint = 3 },
++    },
++    {
++        .name = "INSTANT_WITH_CALLBACKS",
++        .expected_effect = false,
++        .apply_cb = test_apply_callback,
++        .ctx = { .as_uint = 4 },
++        .duration_cb = test_duration_callback,
++        .expected_apply_callback_calls = 1,
++        .expected_duration_callback_calls = 1,
++    },
++    {
++        .name = "PERIODIC_FINITE",
++        .expected_effect = true,
++        .apply_cb = test_apply_callback,
++        .duration_cb = test_duration_callback,
++        .duration = 370,
++        .timer_ms = 180,
++        .timer_flags = LGTD_TIMER_PERSISTENT,
++        .ctx = { .as_uint = 5 },
++        .expected_timer_start_calls = 2,
++    },
++    {
++        .name = "PERIODIC_INFINITE",
++        .expected_effect = true,
++        .apply_cb = test_apply_callback,
++        .timer_ms = 60,
++        .timer_flags = LGTD_TIMER_PERSISTENT|LGTD_TIMER_ACTIVATE_NOW,
++        .ctx = { .as_uint = 6 },
++        .expected_timer_start_calls = 1,
++    },
++};
++
++
++static int timer_start_call_count = 0;
++
++struct lgtd_timer *
++lgtd_timer_start(int flags,
++                 int ms,
++                 void (*cb)(struct lgtd_timer *,
++                            union lgtd_timer_ctx),
++                 union lgtd_timer_ctx ctx)
++{
++    const struct test_parameters *params = &test_cases_parameters[test_case];
++    if (cb == lgtd_effect_duration_callback) {
++        if (flags != LGTD_TIMER_DEFAULT_FLAGS) {
++            lgtd_errx(
++                1, "lgtd_timer_start called with unexpected flags %#x "
++                "(expected %#x)", flags, LGTD_TIMER_DEFAULT_FLAGS
++            );
++        }
++        if (ms != params->duration) {
++            lgtd_errx(
++                1, "lgtd_timer_start called with unexpected duration %d "
++                "(expected %d)", ms, params->duration
++            );
++        }
++    } else if (cb == lgtd_effect_timer_callback) {
++        if (flags != params->timer_flags) {
++            lgtd_errx(
++                1, "lgtd_timer_start called with unexpected flags %#x "
++                "(expected %#x)", flags, params->timer_flags
++            );
++        }
++        if (ms != params->timer_ms) {
++            lgtd_errx(
++                1, "lgtd_timer_start called with unexpected duration %d "
++                "(expected %d)", ms, params->timer_ms
++            );
++        }
++    } else {
++        lgtd_errx(
++            1, "lgtd_timer_start called with unexpected callback at %p", cb
++        );
++    }
++
++    if (!ctx.as_ptr) {
++        lgtd_errx(1, "lgtd_timer_start called without a callback context");
++    }
++
++    timer_start_call_count++;
++
++    return (struct lgtd_timer *)(intptr_t)timer_start_call_count;
++}
++
++static int timer_stop_call_count = 0;
++
++void
++lgtd_timer_stop(struct lgtd_timer *timer)
++{
++    if (!timer) {
++        lgtd_errx(1, "lgtd_timer_stop called without a timer");
++    }
++
++    timer_stop_call_count++;
++}
++
++static int apply_callback_call_count = 0;
 +
 +static void
 +test_apply_callback(const struct lgtd_effect *effect)
 +{
-+    test_apply_callback_call_count++;
++    if (!effect) {
++        lgtd_errx(1, "test_apply_callback didn't receive an effect");
++    }
++
++    const struct test_parameters *params = &test_cases_parameters[test_case];
++    if (params->name != effect->name) {
++        lgtd_errx(
++            1, "test_apply_callback got effect %s (expected %s)",
++            effect->name, params->name
++        );
++    }
++
++    apply_callback_call_count++;
 +}
 +
++static int duration_callback_call_count = 0;
++
++static void
++test_duration_callback(const struct lgtd_effect *effect)
++{
++    if (!effect) {
++        lgtd_errx(1, "test_duration_callback didn't receive an effect");
++    }
++
++    const struct test_parameters *params = &test_cases_parameters[test_case];
++    if (params->name != effect->name) {
++        lgtd_errx(
++            1, "test_duration_callback got effect %s (expected %s)",
++            effect->name, params->name
++        );
++    }
++    duration_callback_call_count++;
++}
++
++static void
++reset_call_counts(void)
++{
++    timer_start_call_count = 0;
++    timer_stop_call_count = 0;
++    apply_callback_call_count = 0;
++    duration_callback_call_count = 0;
++}
 +
 +int
 +main(void)
 +{
-+    switch (test_case++) {
-+        case FINITE_WITH_CALLBACK:
-+            lgtd_effect_start(
-+                "test", 420, NULL, 0, 0, test_apply_callback, TEST_EFFECT_CTX
-+            );
-+        default:
-+            break;
++#define CHECK_CALL_COUNT(cnt_name) do {                         \
++    int call_count = cnt_name ## _call_count;                   \
++    int expectation = params->expected_ ## cnt_name ## _calls;  \
++    if (call_count != expectation) {                            \
++        lgtd_errx(                                              \
++            1, # cnt_name " = %d (expected) %d",                \
++            call_count, expectation                             \
++        );                                                      \
++    }                                                           \
++} while (0)
++
++    for (; test_case != TEST_CASES_COUNT; test_case++) {
++        reset_call_counts();
++
++        const struct test_parameters *params;
++        params = &test_cases_parameters[test_case];
++        lgtd_info("--- test case: %s ---", params->name);
++        const struct lgtd_effect *effect = lgtd_effect_start(
++            params->name,
++            params->duration, params->duration_cb,
++            params->timer_flags, params->timer_ms, params->apply_cb,
++            params->ctx
++        );
++        if (!effect && params->expected_effect) {
++            lgtd_errx(1, "lgtd_effect_start didn't return an effect object");
++        }
++        if (effect && !params->expected_effect) {
++            lgtd_errx(1, "lgtd_effect_start returned an effect object");
++        }
++        CHECK_CALL_COUNT(timer_start);
++        CHECK_CALL_COUNT(apply_callback);
++        CHECK_CALL_COUNT(duration_callback);
++        lgtd_info("--- end ---");
 +    }
 +
 +    return 0;