Skip to content

Commit 01c6a60

Browse files
Zepp-Hanzjacassis
authored andcommitted
testing/drivers: add watchdog notifier cmocka coverage
Extend the watchdog drivertest with notifier ordering, duplicate registration, repeated delivery, unregister, NULL-data, and concurrent registration coverage. Register the watchdog test when either reset-cause or timeout-notifier support is available. Keep hardware watchdog cases gated by reset-cause support and allow notifier-only simulator builds through both Make and CMake. Keep test state in the watchdog fixture and notifier blocks so callbacks do not depend on shared notifier state. Assisted-by: OpenAI Codex Signed-off-by: hanzhijian <hanzhijian@zepp.com>
1 parent 067b0cc commit 01c6a60

3 files changed

Lines changed: 253 additions & 8 deletions

File tree

testing/drivers/drivertest/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ if(CONFIG_TESTING_DRIVER_TEST)
119119
drivertest_posix_timer.c)
120120
endif()
121121

122-
if(CONFIG_WATCHDOG AND CONFIG_BOARDCTL_RESET_CAUSE)
122+
if(CONFIG_WATCHDOG AND (CONFIG_BOARDCTL_RESET_CAUSE
123+
OR CONFIG_WATCHDOG_TIMEOUT_NOTIFIER))
123124
nuttx_add_application(
124125
NAME
125126
cmocka_driver_watchdog

testing/drivers/drivertest/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ endif
6262
endif
6363

6464
ifneq ($(CONFIG_WATCHDOG),)
65-
ifneq ($(CONFIG_BOARDCTL_RESET_CAUSE),)
65+
ifneq ($(CONFIG_BOARDCTL_RESET_CAUSE)$(CONFIG_WATCHDOG_TIMEOUT_NOTIFIER),)
6666
MAINSRC += drivertest_watchdog.c
6767
PROGNAME += cmocka_driver_watchdog
6868
endif

testing/drivers/drivertest/drivertest_watchdog.c

Lines changed: 250 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@
4343
#include <stdint.h>
4444
#include <cmocka.h>
4545
#include <time.h>
46+
#include <pthread.h>
4647

4748
#include <nuttx/arch.h>
49+
#include <nuttx/notifier.h>
50+
#include <nuttx/nuttx.h>
4851
#include <nuttx/timers/watchdog.h>
4952

5053
/****************************************************************************
@@ -88,18 +91,55 @@ struct wdg_state_s
8891
uint32_t deviation;
8992
int test_case;
9093
bool test_getstatus;
94+
#ifdef CONFIG_BOARDCTL_RESET_CAUSE
95+
sem_t semaphore;
96+
#endif
97+
#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
98+
unsigned int notifier_calls;
99+
unsigned long notifier_action[8];
100+
FAR void *notifier_data[8];
101+
int notifier_id[8];
102+
#endif
103+
};
104+
105+
#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
106+
107+
struct watchdog_notifier_test_nb_s
108+
{
109+
struct notifier_block nb;
110+
FAR struct wdg_state_s *state;
111+
int id;
91112
};
92113

93-
static sem_t g_semaphore;
114+
struct watchdog_notifier_race_s
115+
{
116+
struct notifier_block nb;
117+
volatile bool stop;
118+
volatile unsigned int callbacks;
119+
};
120+
121+
#endif
94122

95123
/****************************************************************************
96124
* Private Data
97125
****************************************************************************/
98126

127+
#ifdef CONFIG_BOARDCTL_RESET_CAUSE
128+
129+
/* WDIOC_CAPTURE does not accept a caller-provided callback argument. Keep
130+
* the active test state while the capture handler is installed.
131+
*/
132+
133+
static FAR struct wdg_state_s *g_capture_test_state;
134+
135+
#endif
136+
99137
/****************************************************************************
100138
* Private Functions
101139
****************************************************************************/
102140

141+
#ifdef CONFIG_BOARDCTL_RESET_CAUSE
142+
103143
/****************************************************************************
104144
* Name: get_timestamp
105145
****************************************************************************/
@@ -163,6 +203,8 @@ static int wdg_init(FAR struct wdg_state_s *state)
163203
return dev_fd;
164204
}
165205

206+
#endif /* CONFIG_BOARDCTL_RESET_CAUSE */
207+
166208
/****************************************************************************
167209
* Name: show_usage
168210
****************************************************************************/
@@ -281,16 +323,206 @@ static void parse_commandline(FAR struct wdg_state_s *wdg_state, int argc,
281323
}
282324
}
283325

326+
#ifdef CONFIG_BOARDCTL_RESET_CAUSE
327+
284328
/****************************************************************************
285329
* Name: capture_callback
286330
****************************************************************************/
287331

288332
static int capture_callback(int irq, FAR void *context, FAR void *arg)
289333
{
290-
sem_post(&g_semaphore);
334+
DEBUGASSERT(g_capture_test_state != NULL);
335+
sem_post(&g_capture_test_state->semaphore);
336+
return OK;
337+
}
338+
339+
#endif /* CONFIG_BOARDCTL_RESET_CAUSE */
340+
341+
#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
342+
343+
static int watchdog_notifier_test_callback(FAR struct notifier_block *nb,
344+
unsigned long action,
345+
FAR void *data)
346+
{
347+
FAR struct watchdog_notifier_test_nb_s *test_nb;
348+
unsigned int index;
349+
350+
test_nb = container_of(nb, struct watchdog_notifier_test_nb_s, nb);
351+
index = test_nb->state->notifier_calls;
352+
if (index < 8)
353+
{
354+
test_nb->state->notifier_action[index] = action;
355+
test_nb->state->notifier_data[index] = data;
356+
test_nb->state->notifier_id[index] = test_nb->id;
357+
}
358+
359+
test_nb->state->notifier_calls++;
291360
return OK;
292361
}
293362

363+
static unsigned long watchdog_notifier_test_expected_action(void)
364+
{
365+
#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
366+
return WATCHDOG_KEEPALIVE_BY_ONESHOT;
367+
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
368+
return WATCHDOG_KEEPALIVE_BY_TIMER;
369+
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WDOG)
370+
return WATCHDOG_KEEPALIVE_BY_WDOG;
371+
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER)
372+
return WATCHDOG_KEEPALIVE_BY_WORKER;
373+
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE)
374+
return WATCHDOG_KEEPALIVE_BY_CAPTURE;
375+
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE)
376+
return WATCHDOG_KEEPALIVE_BY_IDLE;
377+
#else
378+
# error "An automonitor source must be selected"
379+
#endif
380+
}
381+
382+
static void drivertest_watchdog_notifier(FAR void **state)
383+
{
384+
FAR struct wdg_state_s *wdg_state = *state;
385+
386+
struct watchdog_notifier_test_nb_s low =
387+
{
388+
.nb =
389+
{
390+
.notifier_call = watchdog_notifier_test_callback,
391+
.priority = 10
392+
},
393+
.state = wdg_state,
394+
.id = 1
395+
};
396+
397+
struct watchdog_notifier_test_nb_s high =
398+
{
399+
.nb =
400+
{
401+
.notifier_call = watchdog_notifier_test_callback,
402+
.priority = 20
403+
},
404+
.state = wdg_state,
405+
.id = 2
406+
};
407+
408+
unsigned long expected_action;
409+
410+
expected_action = watchdog_notifier_test_expected_action();
411+
wdg_state->notifier_calls = 0;
412+
413+
/* Registration is priority ordered, and duplicate registration of the
414+
* same notifier must not result in a duplicate callback.
415+
*/
416+
417+
watchdog_notifier_chain_register(&low.nb);
418+
watchdog_notifier_chain_register(&low.nb);
419+
watchdog_notifier_chain_register(&high.nb);
420+
421+
watchdog_automonitor_timeout();
422+
423+
assert_int_equal(wdg_state->notifier_calls, 2);
424+
assert_int_equal(wdg_state->notifier_id[0], high.id);
425+
assert_int_equal(wdg_state->notifier_id[1], low.id);
426+
assert_int_equal(wdg_state->notifier_action[0], expected_action);
427+
assert_int_equal(wdg_state->notifier_action[1], expected_action);
428+
assert_null(wdg_state->notifier_data[0]);
429+
assert_null(wdg_state->notifier_data[1]);
430+
431+
/* Every timeout notification is delivered to all currently registered
432+
* callbacks.
433+
*/
434+
435+
watchdog_automonitor_timeout();
436+
assert_int_equal(wdg_state->notifier_calls, 4);
437+
assert_int_equal(wdg_state->notifier_id[2], high.id);
438+
assert_int_equal(wdg_state->notifier_id[3], low.id);
439+
440+
/* Unregistering one callback removes only that callback. */
441+
442+
watchdog_notifier_chain_unregister(&high.nb);
443+
watchdog_automonitor_timeout();
444+
assert_int_equal(wdg_state->notifier_calls, 5);
445+
assert_int_equal(wdg_state->notifier_id[4], low.id);
446+
447+
watchdog_notifier_chain_unregister(&low.nb);
448+
watchdog_automonitor_timeout();
449+
assert_int_equal(wdg_state->notifier_calls, 5);
450+
}
451+
452+
static int watchdog_notifier_race_callback(FAR struct notifier_block *nb,
453+
unsigned long action,
454+
FAR void *data)
455+
{
456+
FAR struct watchdog_notifier_race_s *race;
457+
458+
race = container_of(nb, struct watchdog_notifier_race_s, nb);
459+
UNUSED(action);
460+
UNUSED(data);
461+
race->callbacks++;
462+
return OK;
463+
}
464+
465+
static FAR void *watchdog_notifier_race_worker(FAR void *arg)
466+
{
467+
FAR struct watchdog_notifier_race_s *race = arg;
468+
unsigned int count;
469+
470+
for (count = 0; count < 2000 && !race->stop; count++)
471+
{
472+
watchdog_notifier_chain_register(&race->nb);
473+
watchdog_automonitor_timeout();
474+
watchdog_notifier_chain_unregister(&race->nb);
475+
}
476+
477+
return NULL;
478+
}
479+
480+
static void drivertest_watchdog_notifier_race(FAR void **state)
481+
{
482+
struct watchdog_notifier_race_s race =
483+
{
484+
.nb =
485+
{
486+
.notifier_call = watchdog_notifier_race_callback,
487+
.priority = 10
488+
}
489+
};
490+
491+
pthread_t thread;
492+
unsigned int count;
493+
int ret;
494+
495+
UNUSED(state);
496+
watchdog_notifier_chain_register(&race.nb);
497+
ret = pthread_create(&thread, NULL, watchdog_notifier_race_worker, &race);
498+
assert_int_equal(ret, 0);
499+
usleep(1000);
500+
501+
/* Interleave timeout delivery with registration and unregistration from
502+
* another task. The test is successful if the notifier chain remains
503+
* usable and continues to invoke the callback.
504+
*/
505+
506+
for (count = 0; count < 2000; count++)
507+
{
508+
watchdog_automonitor_timeout();
509+
if ((count & 0x3f) == 0)
510+
{
511+
usleep(1000);
512+
}
513+
}
514+
515+
race.stop = true;
516+
ret = pthread_join(thread, NULL);
517+
assert_int_equal(ret, 0);
518+
watchdog_notifier_chain_unregister(&race.nb);
519+
assert_true(race.callbacks > 0);
520+
}
521+
522+
#endif /* CONFIG_WATCHDOG_TIMEOUT_NOTIFIER */
523+
524+
#ifdef CONFIG_BOARDCTL_RESET_CAUSE
525+
294526
/****************************************************************************
295527
* Name: drivertest_watchdog_feeding
296528
*
@@ -493,9 +725,10 @@ static void drivertest_watchdog_api(FAR void **state)
493725

494726
/* Test capture. */
495727

496-
ret = sem_init(&g_semaphore, 0, 0);
728+
ret = sem_init(&wdg_state->semaphore, 0, 0);
497729
assert_return_code(ret, OK);
498730

731+
g_capture_test_state = wdg_state;
499732
watchdog_capture.newhandler = capture_callback;
500733
ret = ioctl(dev_fd, WDIOC_CAPTURE, &watchdog_capture);
501734
assert_return_code(ret, OK);
@@ -504,12 +737,14 @@ static void drivertest_watchdog_api(FAR void **state)
504737

505738
up_udelay(2 * wdg_state->timeout * 1000);
506739

507-
sem_wait(&g_semaphore);
508-
sem_destroy(&g_semaphore);
740+
sem_wait(&wdg_state->semaphore);
509741

510742
watchdog_capture.newhandler = watchdog_capture.oldhandler;
511743
ret = ioctl(dev_fd, WDIOC_CAPTURE, &watchdog_capture);
512744
assert_return_code(ret, OK);
745+
g_capture_test_state = NULL;
746+
747+
sem_destroy(&wdg_state->semaphore);
513748

514749
/* Then stop pinging */
515750

@@ -520,6 +755,8 @@ static void drivertest_watchdog_api(FAR void **state)
520755
assert_return_code(ret, OK);
521756
}
522757

758+
#endif /* CONFIG_BOARDCTL_RESET_CAUSE */
759+
523760
/****************************************************************************
524761
* Public Functions
525762
****************************************************************************/
@@ -545,11 +782,18 @@ int main(int argc, FAR char *argv[])
545782

546783
const struct CMUnitTest tests[] =
547784
{
785+
#ifdef CONFIG_BOARDCTL_RESET_CAUSE
548786
cmocka_unit_test_prestate(drivertest_watchdog_feeding, &wdg_state),
549787
cmocka_unit_test_prestate(drivertest_watchdog_interrupts, &wdg_state),
550788
cmocka_unit_test_prestate(drivertest_watchdog_loop, &wdg_state),
551789
#if !defined(CONFIG_ARCH_ARMV7A) || !defined(CONFIG_ARCH_HAVE_TRUSTZONE)
552-
cmocka_unit_test_prestate(drivertest_watchdog_api, &wdg_state)
790+
cmocka_unit_test_prestate(drivertest_watchdog_api, &wdg_state),
791+
#endif
792+
#endif
793+
794+
#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
795+
cmocka_unit_test_prestate(drivertest_watchdog_notifier, &wdg_state),
796+
cmocka_unit_test_prestate(drivertest_watchdog_notifier_race, &wdg_state)
553797
#endif
554798
};
555799

0 commit comments

Comments
 (0)