root/lib/common/mock.c

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

DEFINITIONS

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

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

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