/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <sys/onoff.h>
static struct onoff_client spinwait_cli;
static int callback_res;
static void *callback_ud;
static void callback(struct onoff_service *srv,
struct onoff_client *cli,
void *ud,
int res)
{
callback_ud = ud;
callback_res = res;
}
static inline void init_notify_sig(struct onoff_client *cli,
struct k_poll_signal *sig)
{
k_poll_signal_init(sig);
onoff_client_init_signal(cli, sig);
}
static inline void init_notify_cb(struct onoff_client *cli)
{
onoff_client_init_callback(cli, callback, NULL);
}
static inline void init_spinwait(struct onoff_client *cli)
{
onoff_client_init_spinwait(cli);
}
static inline int cli_result(const struct onoff_client *cli)
{
int result;
int rc = onoff_client_fetch_result(cli, &result);
if (rc == 0) {
rc = result;
}
return rc;
}
struct transit_state {
const char *tag;
bool async;
int retval;
onoff_service_notify_fn notify;
struct onoff_service *srv;
};
static void reset_transit_state(struct transit_state *tsp)
{
tsp->async = false;
tsp->retval = 0;
tsp->notify = NULL;
tsp->srv = NULL;
}
static void run_transit(struct onoff_service *srv,
onoff_service_notify_fn notify,
struct transit_state *tsp)
{
if (tsp->async) {
TC_PRINT("%s async\n", tsp->tag);
tsp->notify = notify;
tsp->srv = srv;
} else {
TC_PRINT("%s notify %d\n", tsp->tag, tsp->retval);
notify(srv, tsp->retval);
}
}
static void notify(struct transit_state *tsp)
{
TC_PRINT("%s settle %d\n", tsp->tag, tsp->retval);
tsp->notify(tsp->srv, tsp->retval);
tsp->notify = NULL;
tsp->srv = NULL;
}
static struct k_sem isr_sync;
static struct k_timer isr_timer;
static void isr_notify(struct k_timer *timer)
{
struct transit_state *tsp = k_timer_user_data_get(timer);
TC_PRINT("ISR NOTIFY %s %d\n", tsp->tag, k_is_in_isr());
notify(tsp);
k_sem_give(&isr_sync);
}
struct isr_call_state {
struct onoff_service *srv;
struct onoff_client *cli;
int result;
};
static void isr_request(struct k_timer *timer)
{
struct isr_call_state *rsp = k_timer_user_data_get(timer);
rsp->result = onoff_request(rsp->srv, rsp->cli);
k_sem_give(&isr_sync);
}
static void isr_release(struct k_timer *timer)
{
struct isr_call_state *rsp = k_timer_user_data_get(timer);
rsp->result = onoff_release(rsp->srv, rsp->cli);
k_sem_give(&isr_sync);
}
static void isr_reset(struct k_timer *timer)
{
struct isr_call_state *rsp = k_timer_user_data_get(timer);
rsp->result = onoff_service_reset(rsp->srv, rsp->cli);
k_sem_give(&isr_sync);
}
static struct transit_state start_state = {
.tag = "start",
};
static void start(struct onoff_service *srv,
onoff_service_notify_fn notify)
{
run_transit(srv, notify, &start_state);
}
static struct transit_state stop_state = {
.tag = "stop",
};
static void stop(struct onoff_service *srv,
onoff_service_notify_fn notify)
{
run_transit(srv, notify, &stop_state);
}
static struct transit_state reset_state = {
.tag = "reset",
};
static void reset(struct onoff_service *srv,
onoff_service_notify_fn notify)
{
run_transit(srv, notify, &reset_state);
}
static void clear_transit(void)
{
callback_res = 0;
reset_transit_state(&start_state);
reset_transit_state(&stop_state);
reset_transit_state(&reset_state);
}
static void test_service_init_validation(void)
{
int rc;
struct onoff_service srv;
clear_transit();
rc = onoff_service_init(NULL, NULL, NULL, NULL, 0);
zassert_equal(rc, -EINVAL,
"init null srv %d", rc);
rc = onoff_service_init(&srv, NULL, NULL, NULL, 0);
zassert_equal(rc, -EINVAL,
"init null transit %d", rc);
rc = onoff_service_init(&srv, start, NULL, NULL, 0);
zassert_equal(rc, -EINVAL,
"init null stop %d", rc);
rc = onoff_service_init(&srv, NULL, stop, NULL, 0);
zassert_equal(rc, -EINVAL,
"init null start %d", rc);
rc = onoff_service_init(&srv, start, stop, NULL,
ONOFF_SERVICE_INTERNAL_BASE);
zassert_equal(rc, -EINVAL,
"init bad flags %d", rc);
u32_t flags = ONOFF_SERVICE_START_SLEEPS;
memset(&srv, 0xA5, sizeof(srv));
zassert_false(sys_slist_is_empty(&srv.clients),
"slist empty");
rc = onoff_service_init(&srv, start, stop, reset, flags);
zassert_equal(rc, 0,
"init good %d", rc);
zassert_equal(srv.start, start,
"init start mismatch");
zassert_equal(srv.stop, stop,
"init stop mismatch");
zassert_equal(srv.reset, reset,
"init reset mismatch");
zassert_equal(srv.flags, ONOFF_SERVICE_START_SLEEPS,
"init flags mismatch");
zassert_equal(srv.refs, 0,
"init refs mismatch");
zassert_true(sys_slist_is_empty(&srv.clients),
"init slist empty");
}
static void test_client_init_validation(void)
{
struct onoff_client cli;
clear_transit();
memset(&cli, 0xA5, sizeof(cli));
onoff_client_init_spinwait(&cli);
zassert_equal(z_snode_next_peek(&cli.node), NULL,
"cli node mismatch");
zassert_equal(cli.flags, ONOFF_CLIENT_NOTIFY_SPINWAIT,
"cli spinwait flags");
struct k_poll_signal sig;
memset(&cli, 0xA5, sizeof(cli));
onoff_client_init_signal(&cli, &sig);
zassert_equal(z_snode_next_peek(&cli.node), NULL,
"cli signal node");
zassert_equal(cli.flags, ONOFF_CLIENT_NOTIFY_SIGNAL,
"cli signal flags");
zassert_equal(cli.async.signal, &sig,
"cli signal async");
memset(&cli, 0xA5, sizeof(cli));
onoff_client_init_callback(&cli, callback, &sig);
zassert_equal(z_snode_next_peek(&cli.node), NULL,
"cli callback node");
zassert_equal(cli.flags, ONOFF_CLIENT_NOTIFY_CALLBACK,
"cli callback flags");
zassert_equal(cli.async.callback.handler, callback,
"cli callback handler");
zassert_equal(cli.async.callback.user_data, &sig,
"cli callback user_data");
}
static void test_validate_args(void)
{
int rc;
struct onoff_service srv;
struct k_poll_signal sig;
struct onoff_client cli;
clear_transit();
/* The internal validate_args is invoked from request,
* release, and reset; test it through the request API.
*/
rc = onoff_service_init(&srv, start, stop, NULL, 0);
zassert_equal(rc, 0,
"service init");
rc = onoff_request(NULL, NULL);
zassert_equal(rc, -EINVAL,
"validate req null srv");
rc = onoff_release(NULL, NULL);
zassert_equal(rc, -EINVAL,
"validate rel null srv");
rc = onoff_release(&srv, NULL);
zassert_equal(rc, -EINVAL,
"validate rel null cli");
rc = onoff_request(&srv, NULL);
zassert_equal(rc, -EINVAL,
"validate req null cli");
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_true(rc > 0,
"trigger to on");
memset(&cli, 0xA3, sizeof(cli));
rc = onoff_request(&srv, &cli);
zassert_equal(rc, -EINVAL,
"validate req cli flags");
init_spinwait(&cli);
cli.flags = ONOFF_CLIENT_NOTIFY_INVALID;
rc = onoff_request(&srv, &cli);
zassert_equal(rc, -EINVAL,
"validate req cli mode");
init_notify_sig(&cli, &sig);
rc = onoff_request(&srv, &cli);
zassert_equal(rc, 0,
"validate req cli signal: %d", rc);
init_notify_sig(&cli, &sig);
cli.async.signal = NULL;
rc = onoff_request(&srv, &cli);
zassert_equal(rc, -EINVAL,
"validate req cli signal null");
init_notify_cb(&cli);
rc = onoff_request(&srv, &cli);
zassert_equal(rc, 0,
"validate req cli callback");
init_notify_cb(&cli);
cli.async.callback.handler = NULL;
rc = onoff_request(&srv, &cli);
zassert_equal(rc, -EINVAL,
"validate req cli callback null");
memset(&cli, 0x3C, sizeof(cli)); /* makes flags invalid */
rc = onoff_request(&srv, &cli);
zassert_equal(rc, -EINVAL,
"validate req cli notify mode");
}
static void test_reset(void)
{
int rc;
struct onoff_service srv;
struct k_poll_signal sig;
struct onoff_client cli;
unsigned int signalled = 0;
int result = 0;
clear_transit();
rc = onoff_service_init(&srv, start, stop, NULL, 0);
zassert_equal(rc, 0,
"service init");
rc = onoff_service_reset(&srv, &cli);
zassert_equal(rc, -ENOTSUP,
"reset: %d", rc);
rc = onoff_service_init(&srv, start, stop, reset, 0);
zassert_equal(rc, 0,
"service init");
rc = onoff_service_reset(&srv, NULL);
zassert_equal(rc, -EINVAL,
"rst no cli");
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_true(rc > 0,
"req ok");
zassert_equal(srv.refs, 1U,
"reset req refs: %u", srv.refs);
zassert_false(onoff_service_has_error(&srv),
"has error");
reset_state.retval = 57;
init_notify_sig(&cli, &sig);
rc = onoff_service_reset(&srv, &cli);
zassert_equal(rc, -EALREADY,
"reset: %d", rc);
stop_state.retval = -23;
init_notify_sig(&cli, &sig);
rc = onoff_release(&srv, &cli);
zassert_equal(rc, 2,
"rel trigger: %d", rc);
zassert_equal(srv.refs, 0U,
"reset req refs: %u", srv.refs);
zassert_true(onoff_service_has_error(&srv),
"has error");
zassert_equal(cli_result(&cli), stop_state.retval,
"cli result");
signalled = 0;
result = -1;
k_poll_signal_check(&sig, &signalled, &result);
zassert_true(signalled != 0,
"signalled");
zassert_equal(result, stop_state.retval,
"result");
k_poll_signal_reset(&sig);
reset_state.retval = -59;
init_notify_sig(&cli, &sig);
rc = onoff_service_reset(&srv, &cli);
zassert_equal(rc, 0U,
"reset: %d", rc);
zassert_equal(cli_result(&cli), reset_state.retval,
"reset result");
zassert_equal(srv.refs, 0U,
"reset req refs: %u", srv.refs);
zassert_true(onoff_service_has_error(&srv),
"has error");
reset_state.retval = 62;
init_notify_sig(&cli, &sig);
rc = onoff_service_reset(&srv, &cli);
zassert_equal(rc, 0U,
"reset: %d", rc);
zassert_equal(cli_result(&cli), reset_state.retval,
"reset result");
zassert_false(onoff_service_has_error(&srv),
"has error");
signalled = 0;
result = -1;
k_poll_signal_check(&sig, &signalled, &result);
zassert_true(signalled != 0,
"signalled");
zassert_equal(result, reset_state.retval,
"result");
zassert_equal(srv.refs, 0U,
"reset req refs: %u", srv.refs);
zassert_false(onoff_service_has_error(&srv),
"has error");
rc = onoff_service_init(&srv, start, stop, reset,
ONOFF_SERVICE_RESET_SLEEPS);
zassert_equal(rc, 0,
"service init");
start_state.retval = -23;
zassert_false(onoff_service_has_error(&srv),
"has error");
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_true(onoff_service_has_error(&srv),
"has error");
struct isr_call_state isr_state = {
.srv = &srv,
.cli = &spinwait_cli,
};
struct k_timer timer;
init_spinwait(&spinwait_cli);
k_timer_init(&timer, isr_reset, NULL);
k_timer_user_data_set(&timer, &isr_state);
k_timer_start(&timer, K_MSEC(1), K_NO_WAIT);
rc = k_sem_take(&isr_sync, K_MSEC(10));
zassert_equal(rc, 0,
"isr sync");
zassert_equal(isr_state.result, -EWOULDBLOCK,
"isr reset");
zassert_equal(cli_result(&spinwait_cli), -EAGAIN,
"is reset result");
}
static void test_request(void)
{
int rc;
struct onoff_service srv;
clear_transit();
rc = onoff_service_init(&srv, start, stop, reset, 0);
zassert_equal(rc, 0,
"service init");
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_true(rc >= 0,
"reset req: %d", rc);
zassert_equal(srv.refs, 1U,
"reset req refs: %u", srv.refs);
zassert_equal(cli_result(&spinwait_cli), 0,
"reset req result: %d", cli_result(&spinwait_cli));
/* Can't reset when no error present. */
init_spinwait(&spinwait_cli);
rc = onoff_service_reset(&srv, &spinwait_cli);
zassert_equal(rc, -EALREADY,
"reset spin client");
/* Reference overflow produces -EAGAIN */
u32_t refs = srv.refs;
srv.refs = UINT16_MAX;
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_equal(rc, -EAGAIN,
"reset req overflow: %d", rc);
srv.refs = refs;
/* Force an error. */
stop_state.retval = -32;
init_spinwait(&spinwait_cli);
rc = onoff_release(&srv, &spinwait_cli);
zassert_equal(rc, 2,
"error release");
zassert_equal(cli_result(&spinwait_cli), stop_state.retval,
"error retval");
zassert_true(onoff_service_has_error(&srv),
"has error");
/* Can't request when error present. */
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_equal(rc, -EIO,
"req with error");
/* Can't release when error present. */
init_spinwait(&spinwait_cli);
rc = onoff_release(&srv, &spinwait_cli);
zassert_equal(rc, -EIO,
"rel with error");
struct k_poll_signal sig;
struct onoff_client cli;
/* Clear the error */
init_notify_sig(&cli, &sig);
rc = onoff_service_reset(&srv, &cli);
zassert_equal(rc, 0,
"reset");
zassert_false(onoff_service_has_error(&srv),
"has error");
/* Error on start */
start_state.retval = -12;
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_equal(rc, 2,
"req with error");
zassert_equal(cli_result(&spinwait_cli), start_state.retval,
"req with error");
zassert_true(onoff_service_has_error(&srv),
"has error");
/* Clear the error */
init_spinwait(&spinwait_cli);
rc = onoff_service_reset(&srv, &spinwait_cli);
zassert_equal(rc, 0,
"reset");
zassert_false(onoff_service_has_error(&srv),
"has error");
/* Diagnose a no-wait delayed start */
rc = onoff_service_init(&srv, start, stop, reset,
ONOFF_SERVICE_START_SLEEPS);
zassert_equal(rc, 0,
"service init");
start_state.async = true;
start_state.retval = 12;
struct isr_call_state isr_state = {
.srv = &srv,
.cli = &spinwait_cli,
};
struct k_timer timer;
init_spinwait(&spinwait_cli);
k_timer_init(&timer, isr_request, NULL);
k_timer_user_data_set(&timer, &isr_state);
k_timer_start(&timer, K_MSEC(1), K_NO_WAIT);
rc = k_sem_take(&isr_sync, K_MSEC(10));
zassert_equal(rc, 0,
"isr sync");
zassert_equal(isr_state.result, -EWOULDBLOCK,
"isr request");
zassert_equal(cli_result(&spinwait_cli), -EAGAIN,
"isr request result");
}
static void test_sync(void)
{
int rc;
struct onoff_service srv;
clear_transit();
rc = onoff_service_init(&srv, start, stop, reset, 0);
zassert_equal(rc, 0,
"service init");
/* WHITEBOX: request that triggers on returns positive */
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_equal(rc, 2, /* WHITEBOX starting request */
"req ok");
zassert_equal(srv.refs, 1U,
"reset req refs: %u", srv.refs);
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_equal(rc, 0, /* WHITEBOX on request */
"req ok");
zassert_equal(srv.refs, 2U,
"reset req refs: %u", srv.refs);
init_spinwait(&spinwait_cli);
rc = onoff_release(&srv, &spinwait_cli);
zassert_equal(rc, 1, /* WHITEBOX non-stopping release */
"rel ok");
zassert_equal(srv.refs, 1U,
"reset rel refs: %u", srv.refs);
init_spinwait(&spinwait_cli);
rc = onoff_release(&srv, &spinwait_cli);
zassert_equal(rc, 2, /* WHITEBOX stopping release*/
"rel ok: %d", rc);
zassert_equal(srv.refs, 0U,
"reset rel refs: %u", srv.refs);
init_spinwait(&spinwait_cli);
rc = onoff_release(&srv, &spinwait_cli);
zassert_equal(rc, -EALREADY,
"rel noent");
}
static void test_async(void)
{
int rc;
struct onoff_service srv;
struct k_poll_signal sig[2];
struct onoff_client cli[2];
unsigned int signalled = 0;
int result = 0;
clear_transit();
start_state.async = true;
start_state.retval = 23;
stop_state.async = true;
stop_state.retval = 17;
rc = onoff_service_init(&srv, start, stop, reset,
ONOFF_SERVICE_START_SLEEPS
| ONOFF_SERVICE_STOP_SLEEPS);
zassert_equal(rc, 0,
"service init");
/* WHITEBOX: request that triggers on returns positive */
init_notify_sig(&cli[0], &sig[0]);
rc = onoff_request(&srv, &cli[0]);
zassert_equal(rc, 2, /* WHITEBOX starting request */
"req ok");
k_poll_signal_check(&sig[0], &signalled, &result);
zassert_equal((bool)signalled, false,
"cli signalled");
zassert_equal(srv.refs, 0U,
"reset req refs: %u", srv.refs);
/* Non-initial request from ISR is OK */
struct onoff_client isrcli;
struct isr_call_state isr_state = {
.srv = &srv,
.cli = &isrcli,
};
struct k_timer timer;
init_spinwait(&isrcli);
k_timer_init(&timer, isr_request, NULL);
k_timer_user_data_set(&timer, &isr_state);
k_timer_start(&timer, K_MSEC(1), K_NO_WAIT);
rc = k_sem_take(&isr_sync, K_MSEC(10));
zassert_equal(rc, 0,
"isr sync");
zassert_equal(isr_state.result, 1, /* WHITEBOX pending request */
"isr request: %d", isr_state.result);
zassert_equal(cli_result(&isrcli), -EAGAIN,
"isr request result");
/* Off while on pending is not supported */
init_notify_sig(&cli[1], &sig[1]);
rc = onoff_release(&srv, &cli[1]);
zassert_equal(rc, -EBUSY,
"rel in to-on");
/* Second request is delayed for first. */
init_notify_sig(&cli[1], &sig[1]);
rc = onoff_request(&srv, &cli[1]);
zassert_equal(rc, 1, /* WHITEBOX pending request */
"req ok");
k_poll_signal_check(&sig[1], &signalled, &result);
zassert_equal((bool)signalled, false,
"cli signalled");
zassert_equal(srv.refs, 0U,
"reset req refs: %u", srv.refs);
/* Complete the transition. */
notify(&start_state);
k_poll_signal_check(&sig[0], &signalled, &result);
k_poll_signal_reset(&sig[0]);
zassert_equal((bool)signalled, true,
"cli signalled");
zassert_equal(result, start_state.retval,
"cli result");
zassert_equal(cli_result(&isrcli), start_state.retval,
"isrcli result");
k_poll_signal_check(&sig[1], &signalled, &result);
k_poll_signal_reset(&sig[1]);
zassert_equal((bool)signalled, true,
"cli2 signalled");
zassert_equal(result, start_state.retval,
"cli2 result");
zassert_equal(srv.refs, 3U,
"reset req refs: %u", srv.refs);
/* Non-final release decrements refs and completes. */
init_notify_sig(&cli[0], &sig[0]);
rc = onoff_release(&srv, &cli[0]);
zassert_equal(rc, 1, /* WHITEBOX non-stopping release */
"rel ok");
zassert_equal(srv.refs, 2U,
"reset rel refs: %u", srv.refs);
k_poll_signal_check(&sig[0], &signalled, &result);
k_poll_signal_reset(&sig[0]);
zassert_equal((bool)signalled, true,
"cli signalled");
zassert_equal(result, 0,
"cli result");
/* Non-final release from ISR is OK */
init_spinwait(&isrcli);
k_timer_init(&timer, isr_release, NULL);
k_timer_user_data_set(&timer, &isr_state);
k_timer_start(&timer, K_MSEC(1), K_NO_WAIT);
rc = k_sem_take(&isr_sync, K_MSEC(10));
zassert_equal(rc, 0,
"isr sync");
zassert_equal(isr_state.result, 1, /* WHITEBOX pending request */
"isr release: %d", isr_state.result);
zassert_equal(cli_result(&isrcli), 0,
"isr release result");
zassert_equal(srv.refs, 1U,
"reset rel refs: %u", srv.refs);
/* Final release cannot be from ISR */
init_spinwait(&isrcli);
k_timer_start(&timer, K_MSEC(1), K_NO_WAIT);
rc = k_sem_take(&isr_sync, K_MSEC(10));
zassert_equal(rc, 0,
"isr sync");
zassert_equal(isr_state.result, -EWOULDBLOCK,
"isr release");
zassert_equal(cli_result(&isrcli), -EAGAIN,
"is release result");
/* Final async release holds until notify */
init_notify_sig(&cli[1], &sig[1]);
rc = onoff_release(&srv, &cli[1]);
zassert_equal(rc, 2, /* WHITEBOX stopping release */
"rel ok: %d", rc);
zassert_equal(srv.refs, 1U,
"reset rel refs: %u", srv.refs);
/* Redundant release in to-off */
init_notify_sig(&cli[0], &sig[0]);
rc = onoff_release(&srv, &cli[0]);
zassert_equal(rc, -EALREADY,
"rel to-off: %d", rc);
zassert_equal(srv.refs, 1U,
"reset rel refs: %u", srv.refs);
k_poll_signal_check(&sig[0], &signalled, &result);
zassert_equal((bool)signalled, false,
"cli signalled");
/* Request when turning off is queued */
init_notify_sig(&cli[0], &sig[0]);
rc = onoff_request(&srv, &cli[0]);
zassert_equal(rc, 3, /* WHITEBOX stopping request */
"req in to-off");
/* Finalize release, queues start */
zassert_true(start_state.notify == NULL,
"start not invoked");
notify(&stop_state);
zassert_false(start_state.notify == NULL,
"start invoked");
zassert_equal(srv.refs, 0U,
"reset rel refs: %u", srv.refs);
k_poll_signal_check(&sig[1], &signalled, &result);
k_poll_signal_reset(&sig[1]);
zassert_equal((bool)signalled, true,
"cli signalled");
zassert_equal(result, stop_state.retval,
"cli result");
/* Release when starting is an error */
init_notify_sig(&cli[0], &sig[0]);
rc = onoff_release(&srv, &cli[0]);
zassert_equal(rc, -EBUSY,
"rel to-off: %d", rc);
/* Finalize queued start, gets us to on */
cli[0].result = 1 + start_state.retval;
zassert_equal(cli_result(&cli[0]), -EAGAIN,
"fetch failed");
zassert_false(start_state.notify == NULL,
"start invoked");
notify(&start_state);
zassert_equal(cli_result(&cli[0]), start_state.retval,
"start notified");
zassert_equal(srv.refs, 1U,
"reset rel refs: %u", srv.refs);
}
static void test_half_sync(void)
{
int rc;
struct onoff_service srv;
struct k_poll_signal sig;
struct onoff_client cli;
clear_transit();
start_state.retval = 23;
stop_state.async = true;
stop_state.retval = 17;
rc = onoff_service_init(&srv, start, stop, NULL,
ONOFF_SERVICE_STOP_SLEEPS);
zassert_equal(rc, 0,
"service init");
/* Test that a synchronous start delayed by a pending
* asynchronous stop is accepted.
*/
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_equal(rc, 2,
"req0");
zassert_equal(srv.refs, 1U,
"active");
zassert_equal(cli_result(&spinwait_cli), start_state.retval,
"request");
zassert_true(stop_state.notify == NULL,
"not stopping");
init_notify_sig(&cli, &sig);
rc = onoff_release(&srv, &cli);
zassert_equal(rc, 2,
"rel0");
zassert_equal(srv.refs, 1U,
"active");
zassert_false(stop_state.notify == NULL,
"stop pending");
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_equal(rc, 3, /* WHITEBOX start delayed for stop */
"restart");
zassert_equal(cli_result(&cli), -EAGAIN,
"stop incomplete");
zassert_equal(cli_result(&spinwait_cli), -EAGAIN,
"restart incomplete");
notify(&stop_state);
zassert_equal(cli_result(&cli), stop_state.retval,
"stop complete");
zassert_equal(cli_result(&spinwait_cli), start_state.retval,
"restart complete");
}
static void test_cancel_request_waits(void)
{
int rc;
struct onoff_service srv;
struct k_poll_signal sig;
struct onoff_client cli;
clear_transit();
start_state.async = true;
start_state.retval = 14;
stop_state.async = true;
stop_state.retval = 31;
rc = onoff_service_init(&srv, start, stop, NULL,
ONOFF_SERVICE_START_SLEEPS
| ONOFF_SERVICE_STOP_SLEEPS);
zassert_equal(rc, 0,
"service init");
init_notify_sig(&cli, &sig);
rc = onoff_request(&srv, &cli);
zassert_true(rc > 0,
"request pending");
zassert_false(start_state.notify == NULL,
"start pending");
zassert_equal(cli_result(&cli), -EAGAIN,
"start pending");
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_equal(rc, 1, /* WHITEBOX secondary request */
"start2 pending");
zassert_equal(cli_result(&spinwait_cli), -EAGAIN,
"start2 pending");
/* Allowed to cancel in-progress start if doing so leaves
* something to receive the start completion.
*/
rc = onoff_cancel(&srv, &cli);
zassert_equal(rc, 0,
"cancel failed: %d", rc);
zassert_equal(cli_result(&cli), -ECANCELED,
"cancel notified");
zassert_false(onoff_service_has_error(&srv),
"has error");
/* Not allowed to cancel the last pending start.
*/
rc = onoff_cancel(&srv, &spinwait_cli);
zassert_equal(rc, -EWOULDBLOCK,
"last cancel", rc);
zassert_false(onoff_service_has_error(&srv),
"has error");
zassert_equal(cli_result(&spinwait_cli), -EAGAIN,
"last request");
notify(&start_state);
zassert_equal(cli_result(&spinwait_cli), start_state.retval,
"last request");
zassert_false(onoff_service_has_error(&srv),
"has error");
/* Issue a stop, then confirm that you can request and cancel
* a restart.
*/
init_spinwait(&cli);
rc = onoff_release(&srv, &cli);
zassert_equal(rc, 2, /* WHITEBOX stop pending */
"stop pending, %d", rc);
zassert_equal(cli_result(&cli), -EAGAIN,
"stop pending");
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_equal(rc, 3, /* WHITEBOX restart pending */
"restart pending");
rc = onoff_cancel(&srv, &spinwait_cli);
zassert_equal(rc, 0,
"restart cancel");
zassert_equal(cli_result(&spinwait_cli), -ECANCELED,
"restart cancel");
zassert_false(onoff_service_has_error(&srv),
"has error");
zassert_equal(cli_result(&cli), -EAGAIN,
"stop pending");
notify(&stop_state);
zassert_equal(cli_result(&cli), stop_state.retval,
"released");
zassert_false(onoff_service_has_error(&srv),
"has error");
}
static void test_cancel_request_ok(void)
{
int rc;
struct onoff_service srv;
struct k_poll_signal sig;
struct onoff_client cli;
clear_transit();
start_state.async = true;
start_state.retval = 14;
stop_state.retval = 31;
rc = onoff_service_init(&srv, start, stop, NULL,
ONOFF_SERVICE_START_SLEEPS);
zassert_equal(rc, 0,
"service init");
init_notify_sig(&cli, &sig);
rc = onoff_request(&srv, &cli);
zassert_true(rc > 0,
"request pending");
zassert_false(start_state.notify == NULL,
"start pending");
/* You can't cancel the last start request */
rc = onoff_cancel(&srv, &cli);
zassert_equal(rc, -EWOULDBLOCK,
"cancel");
zassert_equal(srv.refs, 0,
"refs empty");
notify(&start_state);
zassert_equal(srv.refs, 1,
"refs");
zassert_false(onoff_service_has_error(&srv),
"has error");
zassert_equal(cli_result(&cli), start_state.retval,
"cancel notified");
zassert_false(onoff_service_has_error(&srv),
"has error");
/* You can "cancel" an request that isn't active */
init_spinwait(&cli);
rc = onoff_cancel(&srv, &cli);
zassert_equal(rc, -EALREADY,
"unregistered");
/* Error if cancel params invalid */
rc = onoff_cancel(&srv, NULL);
zassert_equal(rc, -EINVAL,
"invalid");
}
static void test_blocked_restart(void)
{
int rc;
struct onoff_service srv;
unsigned int signalled = 0;
int result;
struct k_poll_signal sig[2];
struct onoff_client cli[2];
clear_transit();
start_state.async = true;
start_state.retval = 14;
stop_state.async = true;
stop_state.retval = 31;
rc = onoff_service_init(&srv, start, stop, NULL,
ONOFF_SERVICE_START_SLEEPS
| ONOFF_SERVICE_STOP_SLEEPS);
zassert_equal(rc, 0,
"service init");
init_notify_sig(&cli[0], &sig[0]);
rc = onoff_request(&srv, &cli[0]);
zassert_true(rc > 0,
"started");
zassert_false(start_state.notify == NULL,
"start pending");
notify(&start_state);
result = -start_state.retval;
k_poll_signal_check(&sig[0], &signalled, &result);
zassert_true(signalled != 0,
"signalled");
zassert_equal(result, start_state.retval,
"result");
k_poll_signal_reset(&sig[0]);
start_state.async = true;
init_notify_sig(&cli[0], &sig[0]);
rc = onoff_release(&srv, &cli[0]);
zassert_true(rc > 0,
"stop initiated");
zassert_false(stop_state.notify == NULL,
"stop pending");
init_notify_sig(&cli[1], &sig[1]);
rc = onoff_request(&srv, &cli[1]);
zassert_true(rc > 0,
"start pending");
result = start_state.retval + stop_state.retval;
k_poll_signal_check(&sig[0], &signalled, &result);
zassert_true(signalled == 0,
"stop signalled");
k_poll_signal_check(&sig[1], &signalled, &result);
zassert_true(signalled == 0,
"restart signalled");
k_timer_user_data_set(&isr_timer, &stop_state);
k_timer_start(&isr_timer, K_MSEC(1), K_NO_WAIT);
rc = k_sem_take(&isr_sync, K_MSEC(10));
zassert_equal(rc, 0,
"isr sync");
/* Fail-to-restart is not an error */
zassert_false(onoff_service_has_error(&srv),
"has error");
k_poll_signal_check(&sig[0], &signalled, &result);
zassert_false(signalled == 0,
"stop pending");
zassert_equal(result, stop_state.retval,
"stop succeeded");
k_poll_signal_check(&sig[1], &signalled, &result);
zassert_false(signalled == 0,
"restart pending");
zassert_equal(result, -EWOULDBLOCK,
"restart failed");
}
static void test_cancel_release(void)
{
int rc;
struct onoff_service srv;
clear_transit();
start_state.retval = 16;
stop_state.async = true;
stop_state.retval = 94;
rc = onoff_service_init(&srv, start, stop, NULL,
ONOFF_SERVICE_STOP_SLEEPS);
zassert_equal(rc, 0,
"service init");
init_spinwait(&spinwait_cli);
rc = onoff_request(&srv, &spinwait_cli);
zassert_true(rc > 0,
"request done");
zassert_equal(cli_result(&spinwait_cli), start_state.retval,
"started");
init_spinwait(&spinwait_cli);
rc = onoff_release(&srv, &spinwait_cli);
zassert_true(rc > 0,
"release pending");
zassert_false(stop_state.notify == NULL,
"release pending");
zassert_equal(cli_result(&spinwait_cli), -EAGAIN,
"release pending");
/* You can't cancel a stop request. */
rc = onoff_cancel(&srv, &spinwait_cli);
zassert_equal(rc, -EWOULDBLOCK,
"cancel succeeded");
zassert_false(onoff_service_has_error(&srv),
"has error");
notify(&stop_state);
zassert_equal(cli_result(&spinwait_cli), stop_state.retval,
"release pending");
zassert_false(onoff_service_has_error(&srv),
"has error");
}
void test_main(void)
{
k_sem_init(&isr_sync, 0, 1);
k_timer_init(&isr_timer, isr_notify, NULL);
ztest_test_suite(onoff_api,
ztest_unit_test(test_service_init_validation),
ztest_unit_test(test_client_init_validation),
ztest_unit_test(test_validate_args),
ztest_unit_test(test_reset),
ztest_unit_test(test_request),
ztest_unit_test(test_sync),
ztest_unit_test(test_async),
ztest_unit_test(test_half_sync),
ztest_unit_test(test_cancel_request_waits),
ztest_unit_test(test_cancel_request_ok),
ztest_unit_test(test_blocked_restart),
ztest_unit_test(test_cancel_release));
ztest_run_test_suite(onoff_api);
}