root/include/crm/common/unittest_internal.h

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

INCLUDED FROM


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

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