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_getpwnam_r
  11. __wrap_readlink
  12. __wrap_strdup
  13. __wrap_uname

   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][next][first][last][top][bottom][index][help] */
  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][next][first][last][top][bottom][index][help] */
  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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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

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