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