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