pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
mock.c
Go to the documentation of this file.
1 /*
2  * Copyright 2021-2023 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 <crm_internal.h>
11 
12 #include <errno.h>
13 #include <pwd.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <stddef.h>
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <setjmp.h>
22 #include <sys/types.h>
23 #include <sys/utsname.h>
24 #include <unistd.h>
25 #include <grp.h>
26 
27 #include <cmocka.h>
28 #include "mock_private.h"
29 
30 /* This file is only used when running "make check". It is built into
31  * libcrmcommon_test.a, not into libcrmcommon.so. It is used to support
32  * constructing mock versions of library functions for unit testing.
33  *
34  * HOW TO ADD A MOCKED FUNCTION:
35  *
36  * - In this file, declare a bool pcmk__mock_X variable, and define a __wrap_X
37  * function with the same prototype as the actual function that performs the
38  * desired behavior if pcmk__mock_X is true and calls __real_X otherwise.
39  * You can use cmocka's mock_type() and mock_ptr_type() to pass extra
40  * information to the mocked function (see existing examples for details).
41  *
42  * - In mock_private.h, add declarations for extern bool pcmk__mock_X and the
43  * __real_X and __wrap_X function prototypes.
44  *
45  * - In mk/tap.mk, add the function name to the WRAPPED variable.
46  *
47  * HOW TO USE A MOCKED FUNCTION:
48  *
49  * - #include "mock_private.h" in your test file.
50  *
51  * - Write your test cases using pcmk__mock_X and cmocka's will_return() as
52  * needed per the comments for the mocked function below. See existing test
53  * cases for examples.
54  */
55 
56 // LCOV_EXCL_START
57 /* calloc()
58  *
59  * If pcmk__mock_calloc is set to true, later calls to calloc() will return
60  * NULL and must be preceded by:
61  *
62  * expect_*(__wrap_calloc, nmemb[, ...]);
63  * expect_*(__wrap_calloc, size[, ...]);
64  *
65  * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
66  */
67 
68 bool pcmk__mock_calloc = false;
69 
70 void *
71 __wrap_calloc(size_t nmemb, size_t size)
72 {
73  if (!pcmk__mock_calloc) {
74  return __real_calloc(nmemb, size);
75  }
76  check_expected(nmemb);
77  check_expected(size);
78  return NULL;
79 }
80 
81 
82 /* getenv()
83  *
84  * If pcmk__mock_getenv is set to true, later calls to getenv() must be preceded
85  * by:
86  *
87  * expect_*(__wrap_getenv, name[, ...]);
88  * will_return(__wrap_getenv, return_value);
89  *
90  * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
91  */
92 
93 bool pcmk__mock_getenv = false;
94 
95 char *
96 __wrap_getenv(const char *name)
97 {
98  if (!pcmk__mock_getenv) {
99  return __real_getenv(name);
100  }
101  check_expected_ptr(name);
102  return mock_ptr_type(char *);
103 }
104 
105 
106 /* setenv()
107  *
108  * If pcmk__mock_setenv is set to true, later calls to setenv() must be preceded
109  * by:
110  *
111  * expect_*(__wrap_setenv, name[, ...]);
112  * expect_*(__wrap_setenv, value[, ...]);
113  * expect_*(__wrap_setenv, overwrite[, ...]);
114  * will_return(__wrap_setenv, errno_to_set);
115  *
116  * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
117  *
118  * The mocked function will return 0 if errno_to_set is 0, and -1 otherwise.
119  */
120 bool pcmk__mock_setenv = false;
121 
122 int
123 __wrap_setenv(const char *name, const char *value, int overwrite)
124 {
125  if (!pcmk__mock_setenv) {
126  return __real_setenv(name, value, overwrite);
127  }
128  check_expected_ptr(name);
129  check_expected_ptr(value);
130  check_expected(overwrite);
131  errno = mock_type(int);
132  return (errno == 0)? 0 : -1;
133 }
134 
135 
136 /* unsetenv()
137  *
138  * If pcmk__mock_unsetenv is set to true, later calls to unsetenv() must be
139  * preceded by:
140  *
141  * expect_*(__wrap_unsetenv, name[, ...]);
142  * will_return(__wrap_setenv, errno_to_set);
143  *
144  * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
145  *
146  * The mocked function will return 0 if errno_to_set is 0, and -1 otherwise.
147  */
148 bool pcmk__mock_unsetenv = false;
149 
150 int
151 __wrap_unsetenv(const char *name)
152 {
153  if (!pcmk__mock_unsetenv) {
154  return __real_unsetenv(name);
155  }
156  check_expected_ptr(name);
157  errno = mock_type(int);
158  return (errno == 0)? 0 : -1;
159 }
160 
161 
162 /* getpid()
163  *
164  * If pcmk__mock_getpid is set to true, later calls to getpid() must be preceded
165  * by:
166  *
167  * will_return(__wrap_getpid, return_value);
168  */
169 
170 bool pcmk__mock_getpid = false;
171 
172 pid_t
174 {
175  return pcmk__mock_getpid? mock_type(pid_t) : __real_getpid();
176 }
177 
178 
179 /* setgrent(), getgrent() and endgrent()
180  *
181  * If pcmk__mock_grent is set to true, getgrent() will behave as if the only
182  * groups on the system are:
183  *
184  * - grp0 (user0, user1)
185  * - grp1 (user1)
186  * - grp2 (user2, user1)
187  */
188 
189 bool pcmk__mock_grent = false;
190 
191 // Index of group that will be returned next from getgrent()
192 static int group_idx = 0;
193 
194 // Data used for testing
195 static const char* grp0_members[] = {
196  "user0", "user1", NULL
197 };
198 
199 static const char* grp1_members[] = {
200  "user1", NULL
201 };
202 
203 static const char* grp2_members[] = {
204  "user2", "user1", NULL
205 };
206 
207 /* An array of "groups" (a struct from grp.h)
208  *
209  * The members of the groups are initalized here to some testing data, casting
210  * away the consts to make the compiler happy and simplify initialization. We
211  * never actually change these variables during the test!
212  *
213  * string literal = const char* (cannot be changed b/c ? )
214  * vs. char* (it's getting casted to this)
215  */
216 static const int NUM_GROUPS = 3;
217 static struct group groups[] = {
218  {(char*)"grp0", (char*)"", 0, (char**)grp0_members},
219  {(char*)"grp1", (char*)"", 1, (char**)grp1_members},
220  {(char*)"grp2", (char*)"", 2, (char**)grp2_members},
221 };
222 
223 // This function resets the group_idx to 0.
224 void
226  if (pcmk__mock_grent) {
227  group_idx = 0;
228  } else {
229  __real_setgrent();
230  }
231 }
232 
233 /* This function returns the next group entry in the list of groups, or
234  * NULL if there aren't any left.
235  * group_idx is a global variable which keeps track of where you are in the list
236  */
237 struct group *
239  if (pcmk__mock_grent) {
240  if (group_idx >= NUM_GROUPS) {
241  return NULL;
242  }
243  return &groups[group_idx++];
244  } else {
245  return __real_getgrent();
246  }
247 }
248 
249 void
251  if (!pcmk__mock_grent) {
252  __real_endgrent();
253  }
254 }
255 
256 
257 /* fopen()
258  *
259  * If pcmk__mock_fopen is set to true, later calls to fopen() must be
260  * preceded by:
261  *
262  * expect_*(__wrap_fopen, pathname[, ...]);
263  * expect_*(__wrap_fopen, mode[, ...]);
264  * will_return(__wrap_fopen, errno_to_set);
265  *
266  * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
267  *
268  * This has two mocked functions, since fopen() is sometimes actually fopen64().
269  */
270 
271 bool pcmk__mock_fopen = false;
272 
273 FILE *
274 __wrap_fopen(const char *pathname, const char *mode)
275 {
276  if (pcmk__mock_fopen) {
277  check_expected_ptr(pathname);
278  check_expected_ptr(mode);
279  errno = mock_type(int);
280 
281  if (errno != 0) {
282  return NULL;
283  } else {
284  return __real_fopen(pathname, mode);
285  }
286 
287  } else {
288  return __real_fopen(pathname, mode);
289  }
290 }
291 
292 #ifdef HAVE_FOPEN64
293 FILE *
294 __wrap_fopen64(const char *pathname, const char *mode)
295 {
296  if (pcmk__mock_fopen) {
297  check_expected_ptr(pathname);
298  check_expected_ptr(mode);
299  errno = mock_type(int);
300 
301  if (errno != 0) {
302  return NULL;
303  } else {
304  return __real_fopen64(pathname, mode);
305  }
306 
307  } else {
308  return __real_fopen64(pathname, mode);
309  }
310 }
311 #endif
312 
313 /* getpwnam_r()
314  *
315  * If pcmk__mock_getpwnam_r is set to true, later calls to getpwnam_r() must be
316  * preceded by:
317  *
318  * expect_*(__wrap_getpwnam_r, name[, ...]);
319  * expect_*(__wrap_getpwnam_r, pwd[, ...]);
320  * expect_*(__wrap_getpwnam_r, buf[, ...]);
321  * expect_*(__wrap_getpwnam_r, buflen[, ...]);
322  * expect_*(__wrap_getpwnam_r, result[, ...]);
323  * will_return(__wrap_getpwnam_r, return_value);
324  * will_return(__wrap_getpwnam_r, ptr_to_result_struct);
325  *
326  * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
327  */
328 
330 
331 int
332 __wrap_getpwnam_r(const char *name, struct passwd *pwd, char *buf,
333  size_t buflen, struct passwd **result)
334 {
335  if (pcmk__mock_getpwnam_r) {
336  int retval = mock_type(int);
337 
338  check_expected_ptr(name);
339  check_expected_ptr(pwd);
340  check_expected_ptr(buf);
341  check_expected(buflen);
342  check_expected_ptr(result);
343  *result = mock_ptr_type(struct passwd *);
344  return retval;
345 
346  } else {
347  return __real_getpwnam_r(name, pwd, buf, buflen, result);
348  }
349 }
350 
351 /*
352  * If pcmk__mock_readlink is set to true, later calls to readlink() must be
353  * preceded by:
354  *
355  * expect_*(__wrap_readlink, path[, ...]);
356  * expect_*(__wrap_readlink, buf[, ...]);
357  * expect_*(__wrap_readlink, bufsize[, ...]);
358  * will_return(__wrap_readlink, errno_to_set);
359  * will_return(__wrap_readlink, link_contents);
360  *
361  * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
362  *
363  * The mocked function will return 0 if errno_to_set is 0, and -1 otherwise.
364  */
365 
366 bool pcmk__mock_readlink = false;
367 
368 ssize_t
369 __wrap_readlink(const char *restrict path, char *restrict buf,
370  size_t bufsize)
371 {
372  if (pcmk__mock_readlink) {
373  const char *contents = NULL;
374 
375  check_expected_ptr(path);
376  check_expected_ptr(buf);
377  check_expected(bufsize);
378  errno = mock_type(int);
379  contents = mock_ptr_type(const char *);
380 
381  if (errno == 0) {
382  strncpy(buf, contents, bufsize - 1);
383  return strlen(contents);
384  }
385  return -1;
386 
387  } else {
388  return __real_readlink(path, buf, bufsize);
389  }
390 }
391 
392 
393 /* strdup()
394  *
395  * If pcmk__mock_strdup is set to true, later calls to strdup() will return
396  * NULL and must be preceded by:
397  *
398  * expect_*(__wrap_strdup, s[, ...]);
399  *
400  * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
401  */
402 
403 bool pcmk__mock_strdup = false;
404 
405 char *
406 __wrap_strdup(const char *s)
407 {
408  if (!pcmk__mock_strdup) {
409  return __real_strdup(s);
410  }
411  check_expected_ptr(s);
412  return NULL;
413 }
414 
415 
416 /* uname()
417  *
418  * If pcmk__mock_uname is set to true, later calls to uname() must be preceded
419  * by:
420  *
421  * expect_*(__wrap_uname, buf[, ...]);
422  * will_return(__wrap_uname, return_value);
423  * will_return(__wrap_uname, node_name_for_buf_parameter_to_uname);
424  *
425  * expect_* functions: https://api.cmocka.org/group__cmocka__param.html
426  */
427 
428 bool pcmk__mock_uname = false;
429 
430 int
431 __wrap_uname(struct utsname *buf)
432 {
433  if (pcmk__mock_uname) {
434  int retval = 0;
435  char *result = NULL;
436 
437  check_expected_ptr(buf);
438  retval = mock_type(int);
439  result = mock_ptr_type(char *);
440 
441  if (result != NULL) {
442  strcpy(buf->nodename, result);
443  }
444  return retval;
445 
446  } else {
447  return __real_uname(buf);
448  }
449 }
450 
451 // LCOV_EXCL_STOP
bool pcmk__mock_getpwnam_r
Definition: mock.c:329
uint32_t size
Definition: cpg.c:49
bool pcmk__mock_uname
Definition: mock.c:428
bool pcmk__mock_fopen
Definition: mock.c:271
const char * name
Definition: cib.c:26
bool pcmk__mock_grent
Definition: mock.c:189
char * __real_getenv(const char *name)
pid_t __wrap_getpid(void)
Definition: mock.c:173
int __wrap_getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result)
Definition: mock.c:332
int __real_setenv(const char *name, const char *value, int overwrite)
pid_t __real_getpid(void)
bool pcmk__mock_readlink
Definition: mock.c:366
void __wrap_setgrent(void)
Definition: mock.c:225
int __real_unsetenv(const char *name)
int __real_uname(struct utsname *buf)
void __real_setgrent(void)
bool pcmk__mock_setenv
Definition: mock.c:120
void * __real_calloc(size_t nmemb, size_t size)
pcmk__action_result_t result
Definition: pcmk_fence.c:35
struct group * __wrap_getgrent(void)
Definition: mock.c:238
const char * path
Definition: cib.c:28
bool pcmk__mock_getpid
Definition: mock.c:170
struct group * __real_getgrent(void)
FILE * __wrap_fopen(const char *pathname, const char *mode)
Definition: mock.c:274
bool pcmk__mock_calloc
Definition: mock.c:68
void __real_endgrent(void)
ssize_t __real_readlink(const char *restrict path, char *restrict buf, size_t bufsize)
char * __real_strdup(const char *s)
bool pcmk__mock_strdup
Definition: mock.c:403
ssize_t __wrap_readlink(const char *restrict path, char *restrict buf, size_t bufsize)
Definition: mock.c:369
FILE * __real_fopen(const char *pathname, const char *mode)
void __wrap_endgrent(void)
Definition: mock.c:250
int __real_getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result)
int __wrap_setenv(const char *name, const char *value, int overwrite)
Definition: mock.c:123
void * __wrap_calloc(size_t nmemb, size_t size)
Definition: mock.c:71
#define restrict
Definition: config.h:1036
char * __wrap_strdup(const char *s)
Definition: mock.c:406
bool pcmk__mock_unsetenv
Definition: mock.c:148
int __wrap_uname(struct utsname *buf)
Definition: mock.c:431
char * __wrap_getenv(const char *name)
Definition: mock.c:96
int __wrap_unsetenv(const char *name)
Definition: mock.c:151
bool pcmk__mock_getenv
Definition: mock.c:93