pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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)
75#endif
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
90bool pcmk__mock_calloc = false;
91
92void *
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
115bool pcmk__mock_getenv = false;
116
117char *
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
140
141void *
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 */
167bool pcmk__mock_setenv = false;
168
169int
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 */
196
197int
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
217bool pcmk__mock_getpid = false;
218
219pid_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
236bool pcmk__mock_grent = false;
237
238// Index of group that will be returned next from getgrent()
239static int group_idx = 0;
240
241// Data used for testing
242static const char* grp0_members[] = {
243 "user0", "user1", NULL
244};
245
246static const char* grp1_members[] = {
247 "user1", NULL
248};
249
250static 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 */
263static const int NUM_GROUPS = 3;
264static 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.
271void
273 if (pcmk__mock_grent) {
274 group_idx = 0;
275 } else {
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 */
284struct 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
296void
298 if (!pcmk__mock_grent) {
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
318bool pcmk__mock_fopen = false;
319
320FILE *
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
340FILE *
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
378int
379__wrap_getpwnam_r(const char *name, struct passwd *pwd, char *buf,
380 size_t buflen, struct passwd **result)
381{
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
414
415ssize_t
416__wrap_readlink(const char *restrict path, char *restrict buf,
417 size_t bufsize)
418{
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
450bool pcmk__mock_strdup = false;
451
452char *
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
const char * path
Definition cib.c:28
const char * name
Definition cib.c:26
#define restrict
Definition config.h:1041
#define _Noreturn
Definition config.h:660
uint32_t size
Definition cpg.c:4
int __wrap_setenv(const char *name, const char *value, int overwrite)
Definition mock.c:170
bool pcmk__mock_realloc
Definition mock.c:139
char * __wrap_strdup(const char *s)
Definition mock.c:453
bool pcmk__mock_getpid
Definition mock.c:217
pid_t __wrap_getpid(void)
Definition mock.c:220
bool pcmk__mock_getenv
Definition mock.c:115
bool pcmk__mock_calloc
Definition mock.c:90
bool pcmk__mock_getpwnam_r
Definition mock.c:376
bool pcmk__mock_fopen
Definition mock.c:318
bool pcmk__mock_unsetenv
Definition mock.c:195
char * __wrap_getenv(const char *name)
Definition mock.c:118
void __wrap_endgrent(void)
Definition mock.c:297
bool pcmk__mock_setenv
Definition mock.c:167
int __wrap_unsetenv(const char *name)
Definition mock.c:198
bool pcmk__mock_strdup
Definition mock.c:450
bool pcmk__mock_readlink
Definition mock.c:413
void * __wrap_calloc(size_t nmemb, size_t size)
Definition mock.c:93
bool pcmk__mock_grent
Definition mock.c:236
void __wrap_setgrent(void)
Definition mock.c:272
void * __wrap_realloc(void *ptr, size_t size)
Definition mock.c:142
int __wrap_getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result)
Definition mock.c:379
struct group * __wrap_getgrent(void)
Definition mock.c:285
ssize_t __wrap_readlink(const char *restrict path, char *restrict buf, size_t bufsize)
Definition mock.c:416
_Noreturn void __wrap_abort(void)
Definition mock.c:71
FILE * __wrap_fopen(const char *pathname, const char *mode)
Definition mock.c:321
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:35
#define __gcov_dump()