diff --git a/lib/common/mock.c b/lib/common/mock.c index 1cc2d6979c..32ddd14dd5 100644 --- a/lib/common/mock.c +++ b/lib/common/mock.c @@ -1,321 +1,330 @@ /* * Copyright 2021-2022 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mock_private.h" /* This file is only used when running "make check". It is built into * libcrmcommon_test.a, not into libcrmcommon.so. It is used to support * constructing mock versions of library functions for unit testing. * * HOW TO ADD A MOCKED FUNCTION: * * - In this file, declare a bool pcmk__mock_X variable, and define a __wrap_X * function with the same prototype as the actual function that performs the * desired behavior if pcmk__mock_X is true and calls __real_X otherwise. * You can use cmocka's mock_type() and mock_ptr_type() to pass extra * information to the mocked function (see existing examples for details). * * - In mock_private.h, add declarations for extern bool pcmk__mock_X and the * __real_X and __wrap_X function prototypes. * * - In mk/tap.mk, add the function name to the WRAPPED variable. * * HOW TO USE A MOCKED FUNCTION: * * - #include "mock_private.h" in your test file. * * - Write your test cases using pcmk__mock_X and cmocka's will_return() as * needed per the comments for the mocked function below. See existing test * cases for examples. */ // LCOV_EXCL_START - /* calloc() * - * If pcmk__mock_calloc is set to true, later calls to malloc() will return - * NULL. + * If pcmk__mock_calloc is set to true, later calls to calloc() will return + * NULL and must be preceded by: + * + * expect_*(__wrap_calloc, nmemb[, ...]); + * expect_*(__wrap_calloc, size[, ...]); + * + * expect_* functions: https://api.cmocka.org/group__cmocka__param.html */ bool pcmk__mock_calloc = false; void * __wrap_calloc(size_t nmemb, size_t size) { - return pcmk__mock_calloc? NULL : __real_calloc(nmemb, size); + if (!pcmk__mock_calloc) { + return __real_calloc(nmemb, size); + } + check_expected(nmemb); + check_expected(size); + return NULL; } /* getenv() * * If pcmk__mock_getenv is set to true, later calls to getenv() must be preceded * by: * * expect_*(__wrap_getenv, name[, ...]); * will_return(__wrap_getenv, return_value); * * expect_* functions: https://api.cmocka.org/group__cmocka__param.html */ bool pcmk__mock_getenv = false; char * __wrap_getenv(const char *name) { if (!pcmk__mock_getenv) { return __real_getenv(name); } check_expected_ptr(name); return mock_ptr_type(char *); } /* getpid() * * If pcmk__mock_getpid is set to true, later calls to getpid() must be preceded * by: * * will_return(__wrap_getpid, return_value); */ bool pcmk__mock_getpid = false; pid_t __wrap_getpid(void) { return pcmk__mock_getpid? mock_type(pid_t) : __real_getpid(); } /* setgrent(), getgrent() and endgrent() * * If pcmk__mock_grent is set to true, getgrent() will behave as if the only * groups on the system are: * * - grp0 (user0, user1) * - grp1 (user1) * - grp2 (user2, user1) */ bool pcmk__mock_grent = false; // Index of group that will be returned next from getgrent() static int group_idx = 0; // Data used for testing static const char* grp0_members[] = { "user0", "user1", NULL }; static const char* grp1_members[] = { "user1", NULL }; static const char* grp2_members[] = { "user2", "user1", NULL }; /* An array of "groups" (a struct from grp.h) * * The members of the groups are initalized here to some testing data, casting * away the consts to make the compiler happy and simplify initialization. We * never actually change these variables during the test! * * string literal = const char* (cannot be changed b/c ? ) * vs. char* (it's getting casted to this) */ static const int NUM_GROUPS = 3; static struct group groups[] = { {(char*)"grp0", (char*)"", 0, (char**)grp0_members}, {(char*)"grp1", (char*)"", 1, (char**)grp1_members}, {(char*)"grp2", (char*)"", 2, (char**)grp2_members}, }; // This function resets the group_idx to 0. void __wrap_setgrent(void) { if (pcmk__mock_grent) { group_idx = 0; } else { __real_setgrent(); } } /* This function returns the next group entry in the list of groups, or * NULL if there aren't any left. * group_idx is a global variable which keeps track of where you are in the list */ struct group * __wrap_getgrent(void) { if (pcmk__mock_grent) { if (group_idx >= NUM_GROUPS) { return NULL; } return &groups[group_idx++]; } else { return __real_getgrent(); } } void __wrap_endgrent(void) { if (!pcmk__mock_grent) { __real_endgrent(); } } /* fopen() * * If pcmk__mock_fopen is set to true, later calls to fopen() must be * preceded by: * * will_return(__wrap_fopen, errno_to_set); */ bool pcmk__mock_fopen = false; FILE * __wrap_fopen(const char *pathname, const char *mode) { if (pcmk__mock_fopen) { errno = mock_type(int); if (errno != 0) { return NULL; } else { return __real_fopen(pathname, mode); } } else { return __real_fopen(pathname, mode); } } /* getpwnam_r() * * If pcmk__mock_getpwnam_r is set to true, later calls to getpwnam_r() must be * preceded by: * * will_return(__wrap_getpwnam_r, return_value); * will_return(__wrap_getpwnam_r, ptr_to_result_struct); */ bool pcmk__mock_getpwnam_r = false; int __wrap_getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result) { if (pcmk__mock_getpwnam_r) { int retval = mock_type(int); *result = mock_ptr_type(struct passwd *); return retval; } else { return __real_getpwnam_r(name, pwd, buf, buflen, result); } } /* * If pcmk__mock_readlink is set to true, later calls to readlink() must be * preceded by: * * will_return(__wrap_readlink, errno_to_set); * will_return(__wrap_readlink, link_contents); * * The mocked function will return 0 if errno_to_set is 0, and -1 otherwise. */ bool pcmk__mock_readlink = false; ssize_t __wrap_readlink(const char *restrict path, char *restrict buf, size_t bufsize) { if (pcmk__mock_readlink) { const char *contents = NULL; errno = mock_type(int); contents = mock_ptr_type(const char *); if (errno == 0) { strncpy(buf, contents, bufsize - 1); return strlen(contents); } return -1; } else { return __real_readlink(path, buf, bufsize); } } /* strdup() * * If pcmk__mock_strdup is set to true, later calls to strdup() will return * NULL. */ bool pcmk__mock_strdup = false; char * __wrap_strdup(const char *s) { return pcmk__mock_strdup? NULL : __real_strdup(s); } /* uname() * * If pcmk__mock_uname is set to true, later calls to uname() must be preceded * by: * * will_return(__wrap_uname, return_value); * will_return(__wrap_uname, node_name_for_buf_parameter_to_uname); */ bool pcmk__mock_uname = false; int __wrap_uname(struct utsname *buf) { if (pcmk__mock_uname) { int retval = mock_type(int); char *result = mock_ptr_type(char *); if (result != NULL) { strcpy(buf->nodename, result); } return retval; } else { return __real_uname(buf); } } // LCOV_EXCL_STOP diff --git a/lib/common/tests/output/pcmk__output_new_test.c b/lib/common/tests/output/pcmk__output_new_test.c index 14410830d6..65f3b4d3ea 100644 --- a/lib/common/tests/output/pcmk__output_new_test.c +++ b/lib/common/tests/output/pcmk__output_new_test.c @@ -1,152 +1,154 @@ /* * Copyright 2022 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include "mock_private.h" static bool init_succeeds = true; static bool fake_text_init(pcmk__output_t *out) { return init_succeeds; } static void fake_text_free_priv(pcmk__output_t *out) { /* This function intentionally left blank */ } /* "text" is the default for pcmk__output_new. */ static pcmk__output_t * mk_fake_text_output(char **argv) { pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t)); if (retval == NULL) { return NULL; } retval->fmt_name = "text"; retval->init = fake_text_init; retval->free_priv = fake_text_free_priv; retval->register_message = pcmk__register_message; retval->message = pcmk__call_message; return retval; } static int setup(void **state) { pcmk__register_format(NULL, "text", mk_fake_text_output, NULL); return 0; } static int teardown(void **state) { pcmk__unregister_formats(); return 0; } static void empty_formatters(void **state) { pcmk__output_t *out = NULL; pcmk__assert_asserts(pcmk__output_new(&out, "fake", NULL, NULL)); } static void invalid_params(void **state) { /* This must be called with the setup/teardown functions so formatters is not NULL. */ pcmk__assert_asserts(pcmk__output_new(NULL, "fake", NULL, NULL)); } static void no_such_format(void **state) { pcmk__output_t *out = NULL; assert_int_equal(pcmk__output_new(&out, "fake", NULL, NULL), pcmk_rc_unknown_format); } static void create_fails(void **state) { pcmk__output_t *out = NULL; pcmk__mock_calloc = true; // calloc() will return NULL + expect_value(__wrap_calloc, nmemb, 1); + expect_value(__wrap_calloc, size, sizeof(pcmk__output_t)); assert_int_equal(pcmk__output_new(&out, "text", NULL, NULL), ENOMEM); pcmk__mock_calloc = false; // Use real calloc() } static void fopen_fails(void **state) { pcmk__output_t *out = NULL; pcmk__mock_fopen = true; will_return(__wrap_fopen, EPERM); assert_int_equal(pcmk__output_new(&out, "text", "destfile", NULL), EPERM); pcmk__mock_fopen = false; } static void init_fails(void **state) { pcmk__output_t *out = NULL; init_succeeds = false; assert_int_equal(pcmk__output_new(&out, "text", NULL, NULL), ENOMEM); init_succeeds = true; } static void everything_succeeds(void **state) { pcmk__output_t *out = NULL; assert_int_equal(pcmk__output_new(&out, "text", NULL, NULL), pcmk_rc_ok); assert_string_equal(out->fmt_name, "text"); assert_ptr_equal(out->dest, stdout); assert_false(out->quiet); assert_non_null(out->messages); assert_string_equal(getenv("OCF_OUTPUT_FORMAT"), "text"); pcmk__output_free(out); } static void no_fmt_name_given(void **state) { pcmk__output_t *out = NULL; assert_int_equal(pcmk__output_new(&out, NULL, NULL, NULL), pcmk_rc_ok); assert_string_equal(out->fmt_name, "text"); pcmk__output_free(out); } int main(int argc, char **argv) { const struct CMUnitTest tests[] = { cmocka_unit_test(empty_formatters), cmocka_unit_test_setup_teardown(invalid_params, setup, teardown), cmocka_unit_test_setup_teardown(no_such_format, setup, teardown), cmocka_unit_test_setup_teardown(create_fails, setup, teardown), cmocka_unit_test_setup_teardown(init_fails, setup, teardown), cmocka_unit_test_setup_teardown(fopen_fails, setup, teardown), cmocka_unit_test_setup_teardown(everything_succeeds, setup, teardown), cmocka_unit_test_setup_teardown(no_fmt_name_given, setup, teardown), }; cmocka_set_message_output(CM_OUTPUT_TAP); return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/lib/common/tests/strings/pcmk__compress_test.c b/lib/common/tests/strings/pcmk__compress_test.c index 5e131b768d..adf7e7a175 100644 --- a/lib/common/tests/strings/pcmk__compress_test.c +++ b/lib/common/tests/strings/pcmk__compress_test.c @@ -1,60 +1,66 @@ /* * Copyright 2022 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include "mock_private.h" #define SIMPLE_DATA "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" const char *SIMPLE_COMPRESSED = "BZh41AY&SYO\x1ai"; static void simple_compress(void **state) { char *result = calloc(1024, sizeof(char)); unsigned int len; assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 0, &result, &len), pcmk_rc_ok); assert_memory_equal(result, SIMPLE_COMPRESSED, 13); } static void max_too_small(void **state) { char *result = calloc(1024, sizeof(char)); unsigned int len; assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 10, &result, &len), pcmk_rc_error); } static void calloc_fails(void **state) { char *result = calloc(1024, sizeof(char)); unsigned int len; - pcmk__mock_calloc = true; // calloc() will return NULL - pcmk__assert_asserts(pcmk__compress(SIMPLE_DATA, 40, 0, &result, &len)); - pcmk__mock_calloc = false; // Use the real calloc() + pcmk__assert_asserts( + { + pcmk__mock_calloc = true; // calloc() will return NULL + expect_value(__wrap_calloc, nmemb, (size_t) ((40 * 1.01) + 601)); + expect_value(__wrap_calloc, size, sizeof(char)); + pcmk__compress(SIMPLE_DATA, 40, 0, &result, &len); + pcmk__mock_calloc = false; // Use the real calloc() + } + ); } int main(int argc, char **argv) { const struct CMUnitTest tests[] = { cmocka_unit_test(simple_compress), cmocka_unit_test(max_too_small), cmocka_unit_test(calloc_fails), }; cmocka_set_message_output(CM_OUTPUT_TAP); return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/lib/common/tests/utils/crm_user_lookup_test.c b/lib/common/tests/utils/crm_user_lookup_test.c index f40e318e25..e9099f6ad4 100644 --- a/lib/common/tests/utils/crm_user_lookup_test.c +++ b/lib/common/tests/utils/crm_user_lookup_test.c @@ -1,108 +1,110 @@ /* * Copyright 2022 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include "mock_private.h" #include #include static void calloc_fails(void **state) { uid_t uid; gid_t gid; pcmk__mock_calloc = true; // calloc() will return NULL + expect_value(__wrap_calloc, nmemb, 1); + expect_value(__wrap_calloc, size, PCMK__PW_BUFFER_LEN); assert_int_equal(crm_user_lookup("hauser", &uid, &gid), -ENOMEM); pcmk__mock_calloc = false; // Use real calloc() } static void getpwnam_r_fails(void **state) { uid_t uid; gid_t gid; // Set getpwnam_r() return value and result parameter pcmk__mock_getpwnam_r = true; will_return(__wrap_getpwnam_r, EIO); will_return(__wrap_getpwnam_r, NULL); assert_int_equal(crm_user_lookup("hauser", &uid, &gid), -EIO); pcmk__mock_getpwnam_r = false; } static void no_matching_pwent(void **state) { uid_t uid; gid_t gid; // Set getpwnam_r() return value and result parameter pcmk__mock_getpwnam_r = true; will_return(__wrap_getpwnam_r, 0); will_return(__wrap_getpwnam_r, NULL); assert_int_equal(crm_user_lookup("hauser", &uid, &gid), -EINVAL); pcmk__mock_getpwnam_r = false; } static void entry_found(void **state) { uid_t uid; gid_t gid; /* We don't care about any of the other fields of the password entry, so just * leave them blank. */ struct passwd returned_ent = { .pw_uid = 1000, .pw_gid = 1000 }; /* Test getpwnam_r returning a valid passwd entry, but we don't pass uid or gid. */ // Set getpwnam_r() return value and result parameter pcmk__mock_getpwnam_r = true; will_return(__wrap_getpwnam_r, 0); will_return(__wrap_getpwnam_r, &returned_ent); assert_int_equal(crm_user_lookup("hauser", NULL, NULL), 0); /* Test getpwnam_r returning a valid passwd entry, and we do pass uid and gid. */ // Set getpwnam_r() return value and result parameter will_return(__wrap_getpwnam_r, 0); will_return(__wrap_getpwnam_r, &returned_ent); assert_int_equal(crm_user_lookup("hauser", &uid, &gid), 0); assert_int_equal(uid, 1000); assert_int_equal(gid, 1000); pcmk__mock_getpwnam_r = false; } int main(int argc, char **argv) { const struct CMUnitTest tests[] = { cmocka_unit_test(calloc_fails), cmocka_unit_test(getpwnam_r_fails), cmocka_unit_test(no_matching_pwent), cmocka_unit_test(entry_found), }; cmocka_set_message_output(CM_OUTPUT_TAP); return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/lib/pengine/tests/status/pe_new_working_set_test.c b/lib/pengine/tests/status/pe_new_working_set_test.c index 888d5d9ed0..bf08bdf3d5 100644 --- a/lib/pengine/tests/status/pe_new_working_set_test.c +++ b/lib/pengine/tests/status/pe_new_working_set_test.c @@ -1,50 +1,52 @@ /* * Copyright 2022 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include "mock_private.h" static void calloc_fails(void **state) { pcmk__mock_calloc = true; // calloc() will return NULL + expect_value(__wrap_calloc, nmemb, 1); + expect_value(__wrap_calloc, size, sizeof(pe_working_set_t)); assert_null(pe_new_working_set()); pcmk__mock_calloc = false; // Use real calloc() } static void calloc_succeeds(void **state) { pe_working_set_t *data_set = pe_new_working_set(); /* Nothing else to test about this function, as all it does is call * set_working_set_defaults which is also a public function and should * get its own unit test. */ assert_non_null(data_set); /* Avoid calling pe_free_working_set here so we don't artificially * inflate the coverage numbers. */ free(data_set); } int main(int argc, char **argv) { const struct CMUnitTest tests[] = { cmocka_unit_test(calloc_fails), cmocka_unit_test(calloc_succeeds), }; cmocka_set_message_output(CM_OUTPUT_TAP); return cmocka_run_group_tests(tests, NULL, NULL); }