root/include/crm/common/unittest_internal.h

/* [previous][next][first][last][top][bottom][index][help] */

INCLUDED FROM


   1 /*
   2  * Copyright 2022-2025 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #ifndef PCMK__CRM_COMMON_UNITTEST_INTERNAL__H
  11 #define PCMK__CRM_COMMON_UNITTEST_INTERNAL__H
  12 
  13 #include <signal.h>
  14 #include <stdarg.h>
  15 #include <stdint.h>
  16 #include <setjmp.h>
  17 #include <sys/resource.h>
  18 #include <sys/types.h>
  19 #include <sys/wait.h>
  20 #include <unistd.h>
  21 
  22 #include <cmocka.h>
  23 
  24 #include <crm/common/xml.h>
  25 
  26 #ifdef __cplusplus
  27 extern "C" {
  28 #endif
  29 
  30 /* internal unit testing related utilities */
  31 
  32 #if (PCMK__WITH_COVERAGE == 1)
  33 /* This function isn't exposed anywhere.  The following prototype was taken from
  34  * /usr/lib/gcc/x86_64-redhat-linux/??/include/gcov.h
  35  */
  36 extern void __gcov_dump(void);
  37 #else
  38 #define __gcov_dump()
  39 #endif
  40 
  41 /*!
  42  * \internal
  43  * \brief Assert that the XML output from an API function is valid
  44  *
  45  * \param[in] xml   The XML output of some public pacemaker API function
  46  *
  47  * Run the given XML through xmllint and attempt to validate it against the
  48  * api-result.rng schema file.  Assert if validation fails.
  49  *
  50  * \note PCMK_schema_directory needs to be set to the directory containing
  51  *       the built schema files before calling this function.  Typically,
  52  *       this will be done in Makefile.am.
  53  */
  54 void pcmk__assert_validates(xmlNode *xml);
  55 
  56 /*!
  57  * \internal
  58  * \brief Perform setup for a group of unit tests that will manipulate XML
  59  *
  60  * This function is suitable for being passed as the first argument to the
  61  * \c PCMK__UNIT_TEST macro.
  62  *
  63  * \param[in] state     The cmocka state object, currently unused by this
  64  *                      function
  65  */
  66 int pcmk__xml_test_setup_group(void **state);
  67 
  68 int pcmk__xml_test_teardown_group(void **state);
  69 
  70 /*!
  71  * \internal
  72  * \brief Copy the given CIB file to a temporary file so it can be modified
  73  *        as part of doing unit tests, returning the full temporary file or
  74  *        \c NULL on error.
  75  *
  76  * This function should be called as part of the process of setting up any
  77  * single unit test that would access and modify a CIB.  That is, it should
  78  * be called from whatever function is the second argument to
  79  * cmocka_unit_test_setup_teardown.
  80  *
  81  * \param[in]   in_file     The filename of the input CIB file, which must
  82  *                          exist in the \c $PCMK_CTS_CLI_DIR directory.  This
  83  *                          should only be the filename, not the complete
  84  *                          path.
  85  */
  86 char *pcmk__cib_test_copy_cib(const char *in_file);
  87 
  88 /*!
  89  * \internal
  90  * \brief Clean up whatever was done by a previous call to
  91  *        \c pcmk__cib_test_copy_cib.
  92  *
  93  * This function should be called as part of the process of tearing down
  94  * any single unit test that accessed a CIB.  That is, it should be called
  95  * from whatever function is the third argument to
  96  * \c cmocka_unit_test_setup_teardown.
  97  *
  98  * \param[in]   out_path    The complete path to the temporary CIB location.
  99  *                          This is the return value of
 100  *                          \c pcmk__cib_test_copy_cib.
 101  */
 102 void pcmk__cib_test_cleanup(char *out_path);
 103 
 104 void pcmk__test_init_logging(const char *name, const char *filename);
 105 
 106 /*!
 107  * \internal
 108  * \brief Assert that a statement aborts through pcmk__assert().
 109  *
 110  * \param[in] stmt  Statement to execute; can be an expression.
 111  *
 112  * A cmocka-like assert macro for use in unit testing. This one verifies that a
 113  * statement aborts through pcmk__assert(), erroring out if that is not the
 114  * case.
 115  *
 116  * This macro works by running the statement in a forked child process with core
 117  * dumps disabled (pcmk__assert() calls \c abort(), which will write out a core
 118  * dump). The parent waits for the child to exit and checks why. If the child
 119  * received a \c SIGABRT, the test passes. For all other cases, the test fails.
 120  *
 121  * \note If cmocka's expect_*() or will_return() macros are called along with
 122  *       pcmk__assert_asserts(), they must be called within a block that is
 123  *       passed as the \c stmt argument. That way, the values are added only to
 124  *       the child's queue. Otherwise, values added to the parent's queue will
 125  *       never be popped, and the test will fail.
 126  */
 127 #define pcmk__assert_asserts(stmt) \
 128     do { \
 129         pid_t p = fork(); \
 130         if (p == 0) { \
 131             struct rlimit cores = { 0, 0 }; \
 132             setrlimit(RLIMIT_CORE, &cores); \
 133             stmt; \
 134             __gcov_dump(); \
 135             _exit(0); \
 136         } else if (p > 0) { \
 137             int wstatus = 0; \
 138             if (waitpid(p, &wstatus, 0) == -1) { \
 139                 fail_msg("waitpid failed"); \
 140             } \
 141             if (!(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT)) { \
 142                 fail_msg("statement terminated in child without asserting"); \
 143             } \
 144         } else { \
 145             fail_msg("unable to fork for assert test"); \
 146         } \
 147     } while (0);
 148 
 149 /*!
 150  * \internal
 151  * \brief Assert that a statement aborts
 152  *
 153  * This is exactly the same as pcmk__assert_asserts (pcmk__assert() is
 154  * implemented with abort()), but given a different name for clarity.
 155  */
 156 #define pcmk__assert_aborts(stmt) pcmk__assert_asserts(stmt)
 157 
 158 /*!
 159  * \internal
 160  * \brief Assert that a statement exits with the expected exit status.
 161  *
 162  * \param[in] stmt  Statement to execute; can be an expression.
 163  * \param[in] rc    The expected exit status.
 164  *
 165  * This functions just like \c pcmk__assert_asserts, except that it tests for
 166  * an expected exit status.  Abnormal termination or incorrect exit status is
 167  * treated as a failure of the test.
 168  *
 169  * In the event that stmt does not exit at all, the special code \c CRM_EX_NONE
 170  * will be returned.  It is expected that this code is not used anywhere, thus
 171  * always causing an error.
 172  */
 173 #define pcmk__assert_exits(rc, stmt) \
 174     do { \
 175         pid_t p = fork(); \
 176         if (p == 0) { \
 177             struct rlimit cores = { 0, 0 }; \
 178             setrlimit(RLIMIT_CORE, &cores); \
 179             stmt; \
 180             __gcov_dump(); \
 181             _exit(CRM_EX_NONE); \
 182         } else if (p > 0) { \
 183             int wstatus = 0; \
 184             if (waitpid(p, &wstatus, 0) == -1) { \
 185                 fail_msg("waitpid failed"); \
 186             } \
 187             if (!WIFEXITED(wstatus)) { \
 188                 fail_msg("statement terminated abnormally"); \
 189             } else if (WEXITSTATUS(wstatus) != rc) { \
 190                 fail_msg("statement exited with %d, not expected %d", WEXITSTATUS(wstatus), rc); \
 191             } \
 192         } else { \
 193             fail_msg("unable to fork for assert test"); \
 194         } \
 195     } while (0);
 196 
 197 /* Generate the main function of most unit test files.  Typically, group_setup
 198  * and group_teardown will be NULL.  The rest of the arguments are a list of
 199  * calls to cmocka_unit_test or cmocka_unit_test_setup_teardown to run the
 200  * individual unit tests.
 201  */
 202 #define PCMK__UNIT_TEST(group_setup, group_teardown, ...) \
 203 int \
 204 main(int argc, char **argv) \
 205 { \
 206     const struct CMUnitTest t[] = { \
 207         __VA_ARGS__ \
 208     }; \
 209     cmocka_set_message_output(CM_OUTPUT_TAP); \
 210     return cmocka_run_group_tests(t, group_setup, group_teardown); \
 211 }
 212 
 213 #ifdef __cplusplus
 214 }
 215 #endif
 216 
 217 #endif // PCMK__CRM_COMMON_UNITTEST_INTERNAL__H

/* [previous][next][first][last][top][bottom][index][help] */