root/lib/common/mock.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __wrap_abort
  2. __wrap_calloc
  3. __wrap_getenv
  4. __wrap_realloc
  5. __wrap_setenv
  6. __wrap_unsetenv
  7. __wrap_getpid
  8. __wrap_setgrent
  9. __wrap_getgrent
  10. __wrap_endgrent
  11. __wrap_fopen
  12. __wrap_fopen64
  13. __wrap_getpwnam_r
  14. __wrap_readlink
  15. __wrap_strdup

   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>
  27 #include <crm/common/unittest_internal.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
  70 __wrap_abort(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  71 {
  72 #if (PCMK__WITH_COVERAGE == 1)
  73     __gcov_dump();
  74 #endif
  75     __real_abort();
  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 
  89 bool pcmk__mock_calloc = false;
  90 
  91 void *
  92 __wrap_calloc(size_t nmemb, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
  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 
 114 bool pcmk__mock_getenv = false;
 115 
 116 char *
 117 __wrap_getenv(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 138 bool pcmk__mock_realloc = false;
 139 
 140 void *
 141 __wrap_realloc(void *ptr, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  */
 166 bool pcmk__mock_setenv = false;
 167 
 168 int
 169 __wrap_setenv(const char *name, const char *value, int overwrite)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  */
 194 bool pcmk__mock_unsetenv = false;
 195 
 196 int
 197 __wrap_unsetenv(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 216 bool pcmk__mock_getpid = false;
 217 
 218 pid_t
 219 __wrap_getpid(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 235 bool pcmk__mock_grent = false;
 236 
 237 // Index of group that will be returned next from getgrent()
 238 static int group_idx = 0;
 239 
 240 // Data used for testing
 241 static const char* grp0_members[] = {
 242     "user0", "user1", NULL
 243 };
 244 
 245 static const char* grp1_members[] = {
 246     "user1", NULL
 247 };
 248 
 249 static 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  */
 262 static const int NUM_GROUPS = 3;
 263 static 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.
 270 void
 271 __wrap_setgrent(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 272     if (pcmk__mock_grent) {
 273         group_idx = 0;
 274     } else {
 275         __real_setgrent();
 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  */
 283 struct group *
 284 __wrap_getgrent(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 295 void
 296 __wrap_endgrent(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 297     if (!pcmk__mock_grent) {
 298         __real_endgrent();
 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 
 317 bool pcmk__mock_fopen = false;
 318 
 319 FILE *
 320 __wrap_fopen(const char *pathname, const char *mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 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
 339 FILE *
 340 __wrap_fopen64(const char *pathname, const char *mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 375 bool pcmk__mock_getpwnam_r = false;
 376 
 377 int
 378 __wrap_getpwnam_r(const char *name, struct passwd *pwd, char *buf,
     /* [previous][next][first][last][top][bottom][index][help] */
 379                   size_t buflen, struct passwd **result)
 380 {
 381     if (pcmk__mock_getpwnam_r) {
 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 
 412 bool pcmk__mock_readlink = false;
 413 
 414 ssize_t
 415 __wrap_readlink(const char *restrict path, char *restrict buf,
     /* [previous][next][first][last][top][bottom][index][help] */
 416                 size_t bufsize)
 417 {
 418     if (pcmk__mock_readlink) {
 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 
 449 bool pcmk__mock_strdup = false;
 450 
 451 char *
 452 __wrap_strdup(const char *s)
     /* [previous][next][first][last][top][bottom][index][help] */
 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

/* [previous][next][first][last][top][bottom][index][help] */