Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3687472
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/common/mock.c b/lib/common/mock.c
index 6f837adc0d..ba1e7c4414 100644
--- a/lib/common/mock.c
+++ b/lib/common/mock.c
@@ -1,451 +1,476 @@
/*
- * Copyright 2021-2023 the Pacemaker project contributors
+ * Copyright 2021-2024 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 <crm_internal.h>
#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 *);
}
+/* realloc()
+ *
+ * If pcmk__mock_realloc is set to true, later calls to realloc() will return
+ * NULL and must be preceded by:
+ *
+ * expect_*(__wrap_realloc, ptr[, ...]);
+ * expect_*(__wrap_realloc, size[, ...]);
+ *
+ * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
+ */
+
+bool pcmk__mock_realloc = false;
+
+void *
+__wrap_realloc(void *ptr, size_t size)
+{
+ if (!pcmk__mock_realloc) {
+ return __real_realloc(ptr, size);
+ }
+ check_expected_ptr(ptr);
+ check_expected(size);
+ return NULL;
+}
+
+
/* 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
*
* This has two mocked functions, since fopen() is sometimes actually fopen64().
*/
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);
}
}
#ifdef HAVE_FOPEN64
FILE *
__wrap_fopen64(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_fopen64(pathname, mode);
}
} else {
return __real_fopen64(pathname, mode);
}
}
#endif
/* 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
diff --git a/lib/common/mock_private.h b/lib/common/mock_private.h
index b0e0ed2769..61b2bb4850 100644
--- a/lib/common/mock_private.h
+++ b/lib/common/mock_private.h
@@ -1,81 +1,85 @@
/*
- * Copyright 2021-2023 the Pacemaker project contributors
+ * Copyright 2021-2024 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.
*/
#ifndef MOCK_PRIVATE__H
# define MOCK_PRIVATE__H
#include <pwd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <grp.h>
/* This header is for the sole use of libcrmcommon_test and unit tests */
extern bool pcmk__mock_calloc;
void *__real_calloc(size_t nmemb, size_t size);
void *__wrap_calloc(size_t nmemb, size_t size);
extern bool pcmk__mock_fopen;
FILE *__real_fopen(const char *pathname, const char *mode);
FILE *__wrap_fopen(const char *pathname, const char *mode);
#ifdef HAVE_FOPEN64
FILE *__real_fopen64(const char *pathname, const char *mode);
FILE *__wrap_fopen64(const char *pathname, const char *mode);
#endif
extern bool pcmk__mock_getenv;
char *__real_getenv(const char *name);
char *__wrap_getenv(const char *name);
+extern bool pcmk__mock_realloc;
+void *__real_realloc(void *ptr, size_t size);
+void *__wrap_realloc(void *ptr, size_t size);
+
extern bool pcmk__mock_setenv;
int __real_setenv(const char *name, const char *value, int overwrite);
int __wrap_setenv(const char *name, const char *value, int overwrite);
extern bool pcmk__mock_unsetenv;
int __real_unsetenv(const char *name);
int __wrap_unsetenv(const char *name);
extern bool pcmk__mock_getpid;
pid_t __real_getpid(void);
pid_t __wrap_getpid(void);
extern bool pcmk__mock_grent;
void __real_setgrent(void);
void __wrap_setgrent(void);
struct group * __wrap_getgrent(void);
struct group * __real_getgrent(void);
void __wrap_endgrent(void);
void __real_endgrent(void);
extern bool pcmk__mock_getpwnam_r;
int __real_getpwnam_r(const char *name, struct passwd *pwd,
char *buf, size_t buflen, struct passwd **result);
int __wrap_getpwnam_r(const char *name, struct passwd *pwd,
char *buf, size_t buflen, struct passwd **result);
extern bool pcmk__mock_readlink;
ssize_t __real_readlink(const char *restrict path, char *restrict buf,
size_t bufsize);
ssize_t __wrap_readlink(const char *restrict path, char *restrict buf,
size_t bufsize);
extern bool pcmk__mock_strdup;
char *__real_strdup(const char *s);
char *__wrap_strdup(const char *s);
extern bool pcmk__mock_uname;
int __real_uname(struct utsname *buf);
int __wrap_uname(struct utsname *buf);
#endif // MOCK_PRIVATE__H
diff --git a/mk/tap.mk b/mk/tap.mk
index fd6d4e2dfd..14a1cf1059 100644
--- a/mk/tap.mk
+++ b/mk/tap.mk
@@ -1,36 +1,37 @@
#
-# Copyright 2021-2023 the Pacemaker project contributors
+# Copyright 2021-2024 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
# This source code is licensed under the GNU General Public License version 2
# or later (GPLv2+) WITHOUT ANY WARRANTY.
#
AM_TESTS_ENVIRONMENT= \
G_DEBUG=gc-friendly \
MALLOC_CHECK_=2 \
MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256))
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tests/tap-driver.sh
LOG_COMPILER = $(top_srcdir)/tests/tap-test
CLEANFILES = *.log *.trs
WRAPPED = calloc \
endgrent \
fopen \
getenv \
getpid \
getgrent \
getpwnam_r \
readlink \
+ realloc \
setenv \
setgrent \
strdup \
uname \
unsetenv
if WRAPPABLE_FOPEN64
WRAPPED += fopen64
endif
LDFLAGS_WRAP = $(foreach fn,$(WRAPPED),-Wl,--wrap=$(fn))
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Apr 21, 7:29 PM (12 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1665563
Default Alt Text
(15 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment