This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git


The following commit(s) were added to refs/heads/master by this push:
     new 01c6a6058 testing/drivers: add watchdog notifier cmocka coverage
01c6a6058 is described below

commit 01c6a6058115f8bddce9afc8a6b7d48324500899
Author: hanzhijian <[email protected]>
AuthorDate: Mon Jul 20 18:00:38 2026 +0800

    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 <[email protected]>
---
 testing/drivers/drivertest/CMakeLists.txt        |   3 +-
 testing/drivers/drivertest/Makefile              |   2 +-
 testing/drivers/drivertest/drivertest_watchdog.c | 256 ++++++++++++++++++++++-
 3 files changed, 253 insertions(+), 8 deletions(-)

diff --git a/testing/drivers/drivertest/CMakeLists.txt 
b/testing/drivers/drivertest/CMakeLists.txt
index 249284230..ccf43d4ea 100644
--- a/testing/drivers/drivertest/CMakeLists.txt
+++ b/testing/drivers/drivertest/CMakeLists.txt
@@ -119,7 +119,8 @@ if(CONFIG_TESTING_DRIVER_TEST)
       drivertest_posix_timer.c)
   endif()
 
-  if(CONFIG_WATCHDOG AND CONFIG_BOARDCTL_RESET_CAUSE)
+  if(CONFIG_WATCHDOG AND (CONFIG_BOARDCTL_RESET_CAUSE
+                          OR CONFIG_WATCHDOG_TIMEOUT_NOTIFIER))
     nuttx_add_application(
       NAME
       cmocka_driver_watchdog
diff --git a/testing/drivers/drivertest/Makefile 
b/testing/drivers/drivertest/Makefile
index 470c91512..786d30d1d 100644
--- a/testing/drivers/drivertest/Makefile
+++ b/testing/drivers/drivertest/Makefile
@@ -62,7 +62,7 @@ endif
 endif
 
 ifneq ($(CONFIG_WATCHDOG),)
-ifneq ($(CONFIG_BOARDCTL_RESET_CAUSE),)
+ifneq ($(CONFIG_BOARDCTL_RESET_CAUSE)$(CONFIG_WATCHDOG_TIMEOUT_NOTIFIER),)
 MAINSRC  += drivertest_watchdog.c
 PROGNAME += cmocka_driver_watchdog
 endif
diff --git a/testing/drivers/drivertest/drivertest_watchdog.c 
b/testing/drivers/drivertest/drivertest_watchdog.c
index 72f5abe0e..0088627f7 100644
--- a/testing/drivers/drivertest/drivertest_watchdog.c
+++ b/testing/drivers/drivertest/drivertest_watchdog.c
@@ -43,8 +43,11 @@
 #include <stdint.h>
 #include <cmocka.h>
 #include <time.h>
+#include <pthread.h>
 
 #include <nuttx/arch.h>
+#include <nuttx/notifier.h>
+#include <nuttx/nuttx.h>
 #include <nuttx/timers/watchdog.h>
 
 /****************************************************************************
@@ -88,18 +91,55 @@ struct wdg_state_s
   uint32_t deviation;
   int test_case;
   bool test_getstatus;
+#ifdef CONFIG_BOARDCTL_RESET_CAUSE
+  sem_t semaphore;
+#endif
+#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
+  unsigned int notifier_calls;
+  unsigned long notifier_action[8];
+  FAR void *notifier_data[8];
+  int notifier_id[8];
+#endif
+};
+
+#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
+
+struct watchdog_notifier_test_nb_s
+{
+  struct notifier_block nb;
+  FAR struct wdg_state_s *state;
+  int                   id;
 };
 
-static sem_t g_semaphore;
+struct watchdog_notifier_race_s
+{
+  struct notifier_block nb;
+  volatile bool stop;
+  volatile unsigned int callbacks;
+};
+
+#endif
 
 /****************************************************************************
  * Private Data
  ****************************************************************************/
 
+#ifdef CONFIG_BOARDCTL_RESET_CAUSE
+
+/* WDIOC_CAPTURE does not accept a caller-provided callback argument.  Keep
+ * the active test state while the capture handler is installed.
+ */
+
+static FAR struct wdg_state_s *g_capture_test_state;
+
+#endif
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
 
+#ifdef CONFIG_BOARDCTL_RESET_CAUSE
+
 /****************************************************************************
  * Name: get_timestamp
  ****************************************************************************/
@@ -163,6 +203,8 @@ static int wdg_init(FAR struct wdg_state_s *state)
   return dev_fd;
 }
 
+#endif /* CONFIG_BOARDCTL_RESET_CAUSE */
+
 /****************************************************************************
  * Name: show_usage
  ****************************************************************************/
@@ -281,16 +323,206 @@ static void parse_commandline(FAR struct wdg_state_s 
*wdg_state, int argc,
     }
 }
 
+#ifdef CONFIG_BOARDCTL_RESET_CAUSE
+
 /****************************************************************************
  * Name: capture_callback
  ****************************************************************************/
 
 static int capture_callback(int irq, FAR void *context, FAR void *arg)
 {
-  sem_post(&g_semaphore);
+  DEBUGASSERT(g_capture_test_state != NULL);
+  sem_post(&g_capture_test_state->semaphore);
+  return OK;
+}
+
+#endif /* CONFIG_BOARDCTL_RESET_CAUSE */
+
+#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
+
+static int watchdog_notifier_test_callback(FAR struct notifier_block *nb,
+                                            unsigned long action,
+                                            FAR void *data)
+{
+  FAR struct watchdog_notifier_test_nb_s *test_nb;
+  unsigned int index;
+
+  test_nb = container_of(nb, struct watchdog_notifier_test_nb_s, nb);
+  index = test_nb->state->notifier_calls;
+  if (index < 8)
+    {
+      test_nb->state->notifier_action[index] = action;
+      test_nb->state->notifier_data[index] = data;
+      test_nb->state->notifier_id[index] = test_nb->id;
+    }
+
+  test_nb->state->notifier_calls++;
   return OK;
 }
 
+static unsigned long watchdog_notifier_test_expected_action(void)
+{
+#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
+  return WATCHDOG_KEEPALIVE_BY_ONESHOT;
+#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
+  return WATCHDOG_KEEPALIVE_BY_TIMER;
+#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WDOG)
+  return WATCHDOG_KEEPALIVE_BY_WDOG;
+#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER)
+  return WATCHDOG_KEEPALIVE_BY_WORKER;
+#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE)
+  return WATCHDOG_KEEPALIVE_BY_CAPTURE;
+#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE)
+  return WATCHDOG_KEEPALIVE_BY_IDLE;
+#else
+#  error "An automonitor source must be selected"
+#endif
+}
+
+static void drivertest_watchdog_notifier(FAR void **state)
+{
+  FAR struct wdg_state_s *wdg_state = *state;
+
+  struct watchdog_notifier_test_nb_s low =
+  {
+    .nb =
+      {
+        .notifier_call = watchdog_notifier_test_callback,
+        .priority = 10
+      },
+    .state = wdg_state,
+    .id = 1
+  };
+
+  struct watchdog_notifier_test_nb_s high =
+  {
+    .nb =
+      {
+        .notifier_call = watchdog_notifier_test_callback,
+        .priority = 20
+      },
+    .state = wdg_state,
+    .id = 2
+  };
+
+  unsigned long expected_action;
+
+  expected_action = watchdog_notifier_test_expected_action();
+  wdg_state->notifier_calls = 0;
+
+  /* Registration is priority ordered, and duplicate registration of the
+   * same notifier must not result in a duplicate callback.
+   */
+
+  watchdog_notifier_chain_register(&low.nb);
+  watchdog_notifier_chain_register(&low.nb);
+  watchdog_notifier_chain_register(&high.nb);
+
+  watchdog_automonitor_timeout();
+
+  assert_int_equal(wdg_state->notifier_calls, 2);
+  assert_int_equal(wdg_state->notifier_id[0], high.id);
+  assert_int_equal(wdg_state->notifier_id[1], low.id);
+  assert_int_equal(wdg_state->notifier_action[0], expected_action);
+  assert_int_equal(wdg_state->notifier_action[1], expected_action);
+  assert_null(wdg_state->notifier_data[0]);
+  assert_null(wdg_state->notifier_data[1]);
+
+  /* Every timeout notification is delivered to all currently registered
+   * callbacks.
+   */
+
+  watchdog_automonitor_timeout();
+  assert_int_equal(wdg_state->notifier_calls, 4);
+  assert_int_equal(wdg_state->notifier_id[2], high.id);
+  assert_int_equal(wdg_state->notifier_id[3], low.id);
+
+  /* Unregistering one callback removes only that callback. */
+
+  watchdog_notifier_chain_unregister(&high.nb);
+  watchdog_automonitor_timeout();
+  assert_int_equal(wdg_state->notifier_calls, 5);
+  assert_int_equal(wdg_state->notifier_id[4], low.id);
+
+  watchdog_notifier_chain_unregister(&low.nb);
+  watchdog_automonitor_timeout();
+  assert_int_equal(wdg_state->notifier_calls, 5);
+}
+
+static int watchdog_notifier_race_callback(FAR struct notifier_block *nb,
+                                            unsigned long action,
+                                            FAR void *data)
+{
+  FAR struct watchdog_notifier_race_s *race;
+
+  race = container_of(nb, struct watchdog_notifier_race_s, nb);
+  UNUSED(action);
+  UNUSED(data);
+  race->callbacks++;
+  return OK;
+}
+
+static FAR void *watchdog_notifier_race_worker(FAR void *arg)
+{
+  FAR struct watchdog_notifier_race_s *race = arg;
+  unsigned int count;
+
+  for (count = 0; count < 2000 && !race->stop; count++)
+    {
+      watchdog_notifier_chain_register(&race->nb);
+      watchdog_automonitor_timeout();
+      watchdog_notifier_chain_unregister(&race->nb);
+    }
+
+  return NULL;
+}
+
+static void drivertest_watchdog_notifier_race(FAR void **state)
+{
+  struct watchdog_notifier_race_s race =
+    {
+      .nb =
+        {
+          .notifier_call = watchdog_notifier_race_callback,
+          .priority = 10
+        }
+    };
+
+  pthread_t thread;
+  unsigned int count;
+  int ret;
+
+  UNUSED(state);
+  watchdog_notifier_chain_register(&race.nb);
+  ret = pthread_create(&thread, NULL, watchdog_notifier_race_worker, &race);
+  assert_int_equal(ret, 0);
+  usleep(1000);
+
+  /* Interleave timeout delivery with registration and unregistration from
+   * another task.  The test is successful if the notifier chain remains
+   * usable and continues to invoke the callback.
+   */
+
+  for (count = 0; count < 2000; count++)
+    {
+      watchdog_automonitor_timeout();
+      if ((count & 0x3f) == 0)
+        {
+          usleep(1000);
+        }
+    }
+
+  race.stop = true;
+  ret = pthread_join(thread, NULL);
+  assert_int_equal(ret, 0);
+  watchdog_notifier_chain_unregister(&race.nb);
+  assert_true(race.callbacks > 0);
+}
+
+#endif /* CONFIG_WATCHDOG_TIMEOUT_NOTIFIER */
+
+#ifdef CONFIG_BOARDCTL_RESET_CAUSE
+
 /****************************************************************************
  * Name: drivertest_watchdog_feeding
  *
@@ -493,9 +725,10 @@ static void drivertest_watchdog_api(FAR void **state)
 
   /* Test capture. */
 
-  ret = sem_init(&g_semaphore, 0, 0);
+  ret = sem_init(&wdg_state->semaphore, 0, 0);
   assert_return_code(ret, OK);
 
+  g_capture_test_state = wdg_state;
   watchdog_capture.newhandler = capture_callback;
   ret = ioctl(dev_fd, WDIOC_CAPTURE, &watchdog_capture);
   assert_return_code(ret, OK);
@@ -504,12 +737,14 @@ static void drivertest_watchdog_api(FAR void **state)
 
   up_udelay(2 * wdg_state->timeout * 1000);
 
-  sem_wait(&g_semaphore);
-  sem_destroy(&g_semaphore);
+  sem_wait(&wdg_state->semaphore);
 
   watchdog_capture.newhandler = watchdog_capture.oldhandler;
   ret = ioctl(dev_fd, WDIOC_CAPTURE, &watchdog_capture);
   assert_return_code(ret, OK);
+  g_capture_test_state = NULL;
+
+  sem_destroy(&wdg_state->semaphore);
 
   /* Then stop pinging */
 
@@ -520,6 +755,8 @@ static void drivertest_watchdog_api(FAR void **state)
   assert_return_code(ret, OK);
 }
 
+#endif /* CONFIG_BOARDCTL_RESET_CAUSE */
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -545,11 +782,18 @@ int main(int argc, FAR char *argv[])
 
   const struct CMUnitTest tests[] =
   {
+#ifdef CONFIG_BOARDCTL_RESET_CAUSE
     cmocka_unit_test_prestate(drivertest_watchdog_feeding, &wdg_state),
     cmocka_unit_test_prestate(drivertest_watchdog_interrupts, &wdg_state),
     cmocka_unit_test_prestate(drivertest_watchdog_loop, &wdg_state),
 #if !defined(CONFIG_ARCH_ARMV7A) || !defined(CONFIG_ARCH_HAVE_TRUSTZONE)
-    cmocka_unit_test_prestate(drivertest_watchdog_api, &wdg_state)
+    cmocka_unit_test_prestate(drivertest_watchdog_api, &wdg_state),
+#endif
+#endif
+
+#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
+    cmocka_unit_test_prestate(drivertest_watchdog_notifier, &wdg_state),
+    cmocka_unit_test_prestate(drivertest_watchdog_notifier_race, &wdg_state)
 #endif
   };
 

Reply via email to