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)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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
171 __wrap_getpid(void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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
223 __wrap_setgrent(void) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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 *
236 __wrap_getgrent(void) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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
248 __wrap_endgrent(void) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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
305 bool pcmk__mock_getpwnam_r = false;
306
307 int
308 __wrap_getpwnam_r(const char *name, struct passwd *pwd, char *buf,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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