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 /*!
  54  * \internal
  55  * \brief Perform setup for a group of unit tests that will manipulate XML
  56  *
  57  * This function is suitable for being passed as the first argument to the
  58  * \c PCMK__UNIT_TEST macro.
  59  *
  60  * \param[in] state     The cmocka state object, currently unused by this
  61  *                      function
  62  */
  63 int pcmk__xml_test_setup_group(void **state);
  64 
  65 /*!
  66  * \internal
  67  * \brief Copy the given CIB file to a temporary file so it can be modified
  68  *        as part of doing unit tests, returning the full temporary file or
  69  *        \c NULL on error.
  70  *
  71  * This function should be called as part of the process of setting up any
  72  * single unit test that would access and modify a CIB.  That is, it should
  73  * be called from whatever function is the second argument to
  74  * cmocka_unit_test_setup_teardown.
  75  *
  76  * \param[in]   in_file     The filename of the input CIB file, which must
  77  *                          exist in the \c $PCMK_CTS_CLI_DIR directory.  This
  78  *                          should only be the filename, not the complete
  79  *                          path.
  80  */
  81 char *pcmk__cib_test_copy_cib(const char *in_file);
  82 
  83 /*!
  84  * \internal
  85  * \brief Clean up whatever was done by a previous call to
  86  *        \c pcmk__cib_test_copy_cib.
  87  *
  88  * This function should be called as part of the process of tearing down
  89  * any single unit test that accessed a CIB.  That is, it should be called
  90  * from whatever function is the third argument to
  91  * \c cmocka_unit_test_setup_teardown.
  92  *
  93  * \param[in]   out_path    The complete path to the temporary CIB location.
  94  *                          This is the return value of
  95  *                          \c pcmk__cib_test_copy_cib.
  96  */
  97 void pcmk__cib_test_cleanup(char *out_path);
  98 
  99 void pcmk__test_init_logging(const char *name, const char *filename);
 100 
 101 /*!
 102  * \internal
 103  * \brief Assert that a statement aborts through CRM_ASSERT().
 104  *
 105  * \param[in] stmt  Statement to execute; can be an expression.
 106  *
 107  * A cmocka-like assert macro for use in unit testing. This one verifies that a
 108  * statement aborts through CRM_ASSERT(), erroring out if that is not the case.
 109  *
 110  * This macro works by running the statement in a forked child process with core
 111  * dumps disabled (CRM_ASSERT() calls \c abort(), which will write out a core
 112  * dump). The parent waits for the child to exit and checks why. If the child
 113  * received a \c SIGABRT, the test passes. For all other cases, the test fails.
 114  *
 115  * \note If cmocka's expect_*() or will_return() macros are called along with
 116  *       pcmk__assert_asserts(), they must be called within a block that is
 117  *       passed as the \c stmt argument. That way, the values are added only to
 118  *       the child's queue. Otherwise, values added to the parent's queue will
 119  *       never be popped, and the test will fail.
 120  */
 121 #define pcmk__assert_asserts(stmt) \
 122     do { \
 123         pid_t p = fork(); \
 124         if (p == 0) { \
 125             struct rlimit cores = { 0, 0 }; \
 126             setrlimit(RLIMIT_CORE, &cores); \
 127             stmt; \
 128             __gcov_dump(); \
 129             _exit(0); \
 130         } else if (p > 0) { \
 131             int wstatus = 0; \
 132             if (waitpid(p, &wstatus, 0) == -1) { \
 133                 fail_msg("waitpid failed"); \
 134             } \
 135             if (!(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT)) { \
 136                 fail_msg("statement terminated in child without asserting"); \
 137             } \
 138         } else { \
 139             fail_msg("unable to fork for assert test"); \
 140         } \
 141     } while (0);
 142 
 143 /*!
 144  * \internal
 145  * \brief Assert that a statement aborts
 146  *
 147  * This is exactly the same as pcmk__assert_asserts (CRM_ASSERT() is implemented
 148  * with abort()), but given a different name for clarity.
 149  */
 150 #define pcmk__assert_aborts(stmt) pcmk__assert_asserts(stmt)
 151 
 152 /*!
 153  * \internal
 154  * \brief Assert that a statement exits with the expected exit status.
 155  *
 156  * \param[in] stmt  Statement to execute; can be an expression.
 157  * \param[in] rc    The expected exit status.
 158  *
 159  * This functions just like \c pcmk__assert_asserts, except that it tests for
 160  * an expected exit status.  Abnormal termination or incorrect exit status is
 161  * treated as a failure of the test.
 162  *
 163  * In the event that stmt does not exit at all, the special code \c CRM_EX_NONE
 164  * will be returned.  It is expected that this code is not used anywhere, thus
 165  * always causing an error.
 166  */
 167 #define pcmk__assert_exits(rc, stmt) \
 168     do { \
 169         pid_t p = fork(); \
 170         if (p == 0) { \
 171             struct rlimit cores = { 0, 0 }; \
 172             setrlimit(RLIMIT_CORE, &cores); \
 173             stmt; \
 174             __gcov_dump(); \
 175             _exit(CRM_EX_NONE); \
 176         } else if (p > 0) { \
 177             int wstatus = 0; \
 178             if (waitpid(p, &wstatus, 0) == -1) { \
 179                 fail_msg("waitpid failed"); \
 180             } \
 181             if (!WIFEXITED(wstatus)) { \
 182                 fail_msg("statement terminated abnormally"); \
 183             } else if (WEXITSTATUS(wstatus) != rc) { \
 184                 fail_msg("statement exited with %d, not expected %d", WEXITSTATUS(wstatus), rc); \
 185             } \
 186         } else { \
 187             fail_msg("unable to fork for assert test"); \
 188         } \
 189     } while (0);
 190 
 191 /* Generate the main function of most unit test files.  Typically, group_setup
 192  * and group_teardown will be NULL.  The rest of the arguments are a list of
 193  * calls to cmocka_unit_test or cmocka_unit_test_setup_teardown to run the
 194  * individual unit tests.
 195  */
 196 #define PCMK__UNIT_TEST(group_setup, group_teardown, ...) \
 197 int \
 198 main(int argc, char **argv) \
 199 { \
 200     const struct CMUnitTest t[] = { \
 201         __VA_ARGS__ \
 202     }; \
 203     cmocka_set_message_output(CM_OUTPUT_TAP); \
 204     return cmocka_run_group_tests(t, group_setup, group_teardown); \
 205 }
 206 
 207 #endif /* CRM_COMMON_UNITTEST_INTERNAL__H */

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