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

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