summaryrefslogtreecommitdiffstats
path: root/include/crm/common/unittest_internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/crm/common/unittest_internal.h')
-rw-r--r--include/crm/common/unittest_internal.h84
1 files changed, 84 insertions, 0 deletions
diff --git a/include/crm/common/unittest_internal.h b/include/crm/common/unittest_internal.h
new file mode 100644
index 0000000..b8f78cf
--- /dev/null
+++ b/include/crm/common/unittest_internal.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <cmocka.h>
+
+#ifndef CRM_COMMON_UNITTEST_INTERNAL__H
+#define CRM_COMMON_UNITTEST_INTERNAL__H
+
+/* internal unit testing related utilities */
+
+/*!
+ * \internal
+ * \brief Assert that a statement aborts through CRM_ASSERT().
+ *
+ * \param[in] stmt Statement to execute; can be an expression.
+ *
+ * A cmocka-like assert macro for use in unit testing. This one verifies that a
+ * statement aborts through CRM_ASSERT(), erroring out if that is not the case.
+ *
+ * This macro works by running the statement in a forked child process with core
+ * dumps disabled (CRM_ASSERT() calls \c abort(), which will write out a core
+ * dump). The parent waits for the child to exit and checks why. If the child
+ * received a \c SIGABRT, the test passes. For all other cases, the test fails.
+ *
+ * \note If cmocka's expect_*() or will_return() macros are called along with
+ * pcmk__assert_asserts(), they must be called within a block that is
+ * passed as the \c stmt argument. That way, the values are added only to
+ * the child's queue. Otherwise, values added to the parent's queue will
+ * never be popped, and the test will fail.
+ */
+#define pcmk__assert_asserts(stmt) \
+ do { \
+ pid_t p = fork(); \
+ if (p == 0) { \
+ struct rlimit cores = { 0, 0 }; \
+ setrlimit(RLIMIT_CORE, &cores); \
+ stmt; \
+ _exit(0); \
+ } else if (p > 0) { \
+ int wstatus = 0; \
+ if (waitpid(p, &wstatus, 0) == -1) { \
+ fail_msg("waitpid failed"); \
+ } \
+ if (!(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT)) { \
+ fail_msg("statement terminated in child without asserting"); \
+ } \
+ } else { \
+ fail_msg("unable to fork for assert test"); \
+ } \
+ } while (0);
+
+/* Generate the main function of most unit test files. Typically, group_setup
+ * and group_teardown will be NULL. The rest of the arguments are a list of
+ * calls to cmocka_unit_test or cmocka_unit_test_setup_teardown to run the
+ * individual unit tests.
+ */
+#define PCMK__UNIT_TEST(group_setup, group_teardown, ...) \
+int \
+main(int argc, char **argv) \
+{ \
+ const struct CMUnitTest t[] = { \
+ __VA_ARGS__ \
+ }; \
+ cmocka_set_message_output(CM_OUTPUT_TAP); \
+ return cmocka_run_group_tests(t, group_setup, group_teardown); \
+}
+
+#endif /* CRM_COMMON_UNITTEST_INTERNAL__H */