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