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

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