summaryrefslogtreecommitdiffstats
path: root/lib/common/mock.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:53:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:53:20 +0000
commite5a812082ae033afb1eed82c0f2df3d0f6bdc93f (patch)
treea6716c9275b4b413f6c9194798b34b91affb3cc7 /lib/common/mock.c
parentInitial commit. (diff)
downloadpacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.tar.xz
pacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.zip
Adding upstream version 2.1.6.upstream/2.1.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/common/mock.c')
-rw-r--r--lib/common/mock.c427
1 files changed, 427 insertions, 0 deletions
diff --git a/lib/common/mock.c b/lib/common/mock.c
new file mode 100644
index 0000000..2bd8334
--- /dev/null
+++ b/lib/common/mock.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2021-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 <errno.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <grp.h>
+
+#include <cmocka.h>
+#include "mock_private.h"
+
+/* This file is only used when running "make check". It is built into
+ * libcrmcommon_test.a, not into libcrmcommon.so. It is used to support
+ * constructing mock versions of library functions for unit testing.
+ *
+ * HOW TO ADD A MOCKED FUNCTION:
+ *
+ * - In this file, declare a bool pcmk__mock_X variable, and define a __wrap_X
+ * function with the same prototype as the actual function that performs the
+ * desired behavior if pcmk__mock_X is true and calls __real_X otherwise.
+ * You can use cmocka's mock_type() and mock_ptr_type() to pass extra
+ * information to the mocked function (see existing examples for details).
+ *
+ * - In mock_private.h, add declarations for extern bool pcmk__mock_X and the
+ * __real_X and __wrap_X function prototypes.
+ *
+ * - In mk/tap.mk, add the function name to the WRAPPED variable.
+ *
+ * HOW TO USE A MOCKED FUNCTION:
+ *
+ * - #include "mock_private.h" in your test file.
+ *
+ * - Write your test cases using pcmk__mock_X and cmocka's will_return() as
+ * needed per the comments for the mocked function below. See existing test
+ * cases for examples.
+ */
+
+// LCOV_EXCL_START
+/* calloc()
+ *
+ * If pcmk__mock_calloc is set to true, later calls to calloc() will return
+ * NULL and must be preceded by:
+ *
+ * expect_*(__wrap_calloc, nmemb[, ...]);
+ * expect_*(__wrap_calloc, size[, ...]);
+ *
+ * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
+ */
+
+bool pcmk__mock_calloc = false;
+
+void *
+__wrap_calloc(size_t nmemb, size_t size)
+{
+ if (!pcmk__mock_calloc) {
+ return __real_calloc(nmemb, size);
+ }
+ check_expected(nmemb);
+ check_expected(size);
+ return NULL;
+}
+
+
+/* getenv()
+ *
+ * If pcmk__mock_getenv is set to true, later calls to getenv() must be preceded
+ * by:
+ *
+ * expect_*(__wrap_getenv, name[, ...]);
+ * will_return(__wrap_getenv, return_value);
+ *
+ * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
+ */
+
+bool pcmk__mock_getenv = false;
+
+char *
+__wrap_getenv(const char *name)
+{
+ if (!pcmk__mock_getenv) {
+ return __real_getenv(name);
+ }
+ check_expected_ptr(name);
+ return mock_ptr_type(char *);
+}
+
+
+/* setenv()
+ *
+ * If pcmk__mock_setenv is set to true, later calls to setenv() must be preceded
+ * by:
+ *
+ * expect_*(__wrap_setenv, name[, ...]);
+ * expect_*(__wrap_setenv, value[, ...]);
+ * expect_*(__wrap_setenv, overwrite[, ...]);
+ * will_return(__wrap_setenv, errno_to_set);
+ *
+ * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
+ *
+ * The mocked function will return 0 if errno_to_set is 0, and -1 otherwise.
+ */
+bool pcmk__mock_setenv = false;
+
+int
+__wrap_setenv(const char *name, const char *value, int overwrite)
+{
+ if (!pcmk__mock_setenv) {
+ return __real_setenv(name, value, overwrite);
+ }
+ check_expected_ptr(name);
+ check_expected_ptr(value);
+ check_expected(overwrite);
+ errno = mock_type(int);
+ return (errno == 0)? 0 : -1;
+}
+
+
+/* unsetenv()
+ *
+ * If pcmk__mock_unsetenv is set to true, later calls to unsetenv() must be
+ * preceded by:
+ *
+ * expect_*(__wrap_unsetenv, name[, ...]);
+ * will_return(__wrap_setenv, errno_to_set);
+ *
+ * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
+ *
+ * The mocked function will return 0 if errno_to_set is 0, and -1 otherwise.
+ */
+bool pcmk__mock_unsetenv = false;
+
+int
+__wrap_unsetenv(const char *name)
+{
+ if (!pcmk__mock_unsetenv) {
+ return __real_unsetenv(name);
+ }
+ check_expected_ptr(name);
+ errno = mock_type(int);
+ return (errno == 0)? 0 : -1;
+}
+
+
+/* getpid()
+ *
+ * If pcmk__mock_getpid is set to true, later calls to getpid() must be preceded
+ * by:
+ *
+ * will_return(__wrap_getpid, return_value);
+ */
+
+bool pcmk__mock_getpid = false;
+
+pid_t
+__wrap_getpid(void)
+{
+ return pcmk__mock_getpid? mock_type(pid_t) : __real_getpid();
+}
+
+
+/* setgrent(), getgrent() and endgrent()
+ *
+ * If pcmk__mock_grent is set to true, getgrent() will behave as if the only
+ * groups on the system are:
+ *
+ * - grp0 (user0, user1)
+ * - grp1 (user1)
+ * - grp2 (user2, user1)
+ */
+
+bool pcmk__mock_grent = false;
+
+// Index of group that will be returned next from getgrent()
+static int group_idx = 0;
+
+// Data used for testing
+static const char* grp0_members[] = {
+ "user0", "user1", NULL
+};
+
+static const char* grp1_members[] = {
+ "user1", NULL
+};
+
+static const char* grp2_members[] = {
+ "user2", "user1", NULL
+};
+
+/* An array of "groups" (a struct from grp.h)
+ *
+ * The members of the groups are initalized here to some testing data, casting
+ * away the consts to make the compiler happy and simplify initialization. We
+ * never actually change these variables during the test!
+ *
+ * string literal = const char* (cannot be changed b/c ? )
+ * vs. char* (it's getting casted to this)
+ */
+static const int NUM_GROUPS = 3;
+static struct group groups[] = {
+ {(char*)"grp0", (char*)"", 0, (char**)grp0_members},
+ {(char*)"grp1", (char*)"", 1, (char**)grp1_members},
+ {(char*)"grp2", (char*)"", 2, (char**)grp2_members},
+};
+
+// This function resets the group_idx to 0.
+void
+__wrap_setgrent(void) {
+ if (pcmk__mock_grent) {
+ group_idx = 0;
+ } else {
+ __real_setgrent();
+ }
+}
+
+/* This function returns the next group entry in the list of groups, or
+ * NULL if there aren't any left.
+ * group_idx is a global variable which keeps track of where you are in the list
+ */
+struct group *
+__wrap_getgrent(void) {
+ if (pcmk__mock_grent) {
+ if (group_idx >= NUM_GROUPS) {
+ return NULL;
+ }
+ return &groups[group_idx++];
+ } else {
+ return __real_getgrent();
+ }
+}
+
+void
+__wrap_endgrent(void) {
+ if (!pcmk__mock_grent) {
+ __real_endgrent();
+ }
+}
+
+
+/* fopen()
+ *
+ * If pcmk__mock_fopen is set to true, later calls to fopen() must be
+ * preceded by:
+ *
+ * expect_*(__wrap_fopen, pathname[, ...]);
+ * expect_*(__wrap_fopen, mode[, ...]);
+ * will_return(__wrap_fopen, errno_to_set);
+ *
+ * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
+ */
+
+bool pcmk__mock_fopen = false;
+
+FILE *
+__wrap_fopen(const char *pathname, const char *mode)
+{
+ if (pcmk__mock_fopen) {
+ check_expected_ptr(pathname);
+ check_expected_ptr(mode);
+ errno = mock_type(int);
+
+ if (errno != 0) {
+ return NULL;
+ } else {
+ return __real_fopen(pathname, mode);
+ }
+
+ } else {
+ return __real_fopen(pathname, mode);
+ }
+}
+
+
+/* getpwnam_r()
+ *
+ * If pcmk__mock_getpwnam_r is set to true, later calls to getpwnam_r() must be
+ * preceded by:
+ *
+ * expect_*(__wrap_getpwnam_r, name[, ...]);
+ * expect_*(__wrap_getpwnam_r, pwd[, ...]);
+ * expect_*(__wrap_getpwnam_r, buf[, ...]);
+ * expect_*(__wrap_getpwnam_r, buflen[, ...]);
+ * expect_*(__wrap_getpwnam_r, result[, ...]);
+ * will_return(__wrap_getpwnam_r, return_value);
+ * will_return(__wrap_getpwnam_r, ptr_to_result_struct);
+ *
+ * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
+ */
+
+bool pcmk__mock_getpwnam_r = false;
+
+int
+__wrap_getpwnam_r(const char *name, struct passwd *pwd, char *buf,
+ size_t buflen, struct passwd **result)
+{
+ if (pcmk__mock_getpwnam_r) {
+ int retval = mock_type(int);
+
+ check_expected_ptr(name);
+ check_expected_ptr(pwd);
+ check_expected_ptr(buf);
+ check_expected(buflen);
+ check_expected_ptr(result);
+ *result = mock_ptr_type(struct passwd *);
+ return retval;
+
+ } else {
+ return __real_getpwnam_r(name, pwd, buf, buflen, result);
+ }
+}
+
+/*
+ * If pcmk__mock_readlink is set to true, later calls to readlink() must be
+ * preceded by:
+ *
+ * expect_*(__wrap_readlink, path[, ...]);
+ * expect_*(__wrap_readlink, buf[, ...]);
+ * expect_*(__wrap_readlink, bufsize[, ...]);
+ * will_return(__wrap_readlink, errno_to_set);
+ * will_return(__wrap_readlink, link_contents);
+ *
+ * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
+ *
+ * The mocked function will return 0 if errno_to_set is 0, and -1 otherwise.
+ */
+
+bool pcmk__mock_readlink = false;
+
+ssize_t
+__wrap_readlink(const char *restrict path, char *restrict buf,
+ size_t bufsize)
+{
+ if (pcmk__mock_readlink) {
+ const char *contents = NULL;
+
+ check_expected_ptr(path);
+ check_expected_ptr(buf);
+ check_expected(bufsize);
+ errno = mock_type(int);
+ contents = mock_ptr_type(const char *);
+
+ if (errno == 0) {
+ strncpy(buf, contents, bufsize - 1);
+ return strlen(contents);
+ }
+ return -1;
+
+ } else {
+ return __real_readlink(path, buf, bufsize);
+ }
+}
+
+
+/* strdup()
+ *
+ * If pcmk__mock_strdup is set to true, later calls to strdup() will return
+ * NULL and must be preceded by:
+ *
+ * expect_*(__wrap_strdup, s[, ...]);
+ *
+ * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
+ */
+
+bool pcmk__mock_strdup = false;
+
+char *
+__wrap_strdup(const char *s)
+{
+ if (!pcmk__mock_strdup) {
+ return __real_strdup(s);
+ }
+ check_expected_ptr(s);
+ return NULL;
+}
+
+
+/* uname()
+ *
+ * If pcmk__mock_uname is set to true, later calls to uname() must be preceded
+ * by:
+ *
+ * expect_*(__wrap_uname, buf[, ...]);
+ * will_return(__wrap_uname, return_value);
+ * will_return(__wrap_uname, node_name_for_buf_parameter_to_uname);
+ *
+ * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
+ */
+
+bool pcmk__mock_uname = false;
+
+int
+__wrap_uname(struct utsname *buf)
+{
+ if (pcmk__mock_uname) {
+ int retval = 0;
+ char *result = NULL;
+
+ check_expected_ptr(buf);
+ retval = mock_type(int);
+ result = mock_ptr_type(char *);
+
+ if (result != NULL) {
+ strcpy(buf->nodename, result);
+ }
+ return retval;
+
+ } else {
+ return __real_uname(buf);
+ }
+}
+
+// LCOV_EXCL_STOP