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 */