#include "../../unity/unity.h" #include #include #include #include #include #include /* Note: - This test file is included at the end of env.c, so it shares the same translation unit and can access the static symbols: - usvars, usvars_used, usvars_alloc - unset_envvars() - We only manipulate usvars/usvars_used/usvars_alloc; we do not redefine any symbol from the program. */ static char const **allocated_usvars = NULL; /* Track array we allocate for cleanup */ /* Helper: set the list of variables to unset. Copies the pointer list, not the strings. */ static void prepare_usvars_list(char const *const *list, size_t count) { /* Free any prior allocation from a previous test run */ if (allocated_usvars) { free((void *)allocated_usvars); allocated_usvars = NULL; } if (count == 0 || list == NULL) { usvars = NULL; usvars_used = 0; usvars_alloc = 0; return; } char const **arr = (char const **)malloc(sizeof(char const *) * count); TEST_ASSERT_NOT_NULL(arr); for (size_t i = 0; i < count; i++) arr[i] = list[i]; usvars = arr; usvars_used = (typeof(usvars_used))count; usvars_alloc = (typeof(usvars_alloc))count; allocated_usvars = arr; } /* Helper: make a unique environment variable name. Caller frees the returned string. */ static char *make_unique_name(const char *prefix) { char buf[128]; static unsigned counter = 0; snprintf(buf, sizeof buf, "%s_%ld_%u", prefix, (long)getpid(), ++counter); return strdup(buf); } void setUp(void) { /* Reset our managed allocation; program globals will be set per-test */ if (allocated_usvars) { free((void *)allocated_usvars); allocated_usvars = NULL; } usvars = NULL; usvars_used = 0; usvars_alloc = 0; } void tearDown(void) { if (allocated_usvars) { free((void *)allocated_usvars); allocated_usvars = NULL; } /* Leave environment as modified by the test; each test sets/clears variables it needs */ } /* Test: unsetting a single existing variable removes it from the environment. */ static void test_unset_envvars_single_existing(void) { char *name = make_unique_name("ENV_TEST_ONE"); TEST_ASSERT_NOT_NULL(name); /* Ensure the variable exists */ TEST_ASSERT_EQUAL_INT(0, setenv(name, "value", 1)); TEST_ASSERT_NOT_NULL(getenv(name)); char const *list[] = { name }; prepare_usvars_list(list, 1); unset_envvars(); TEST_ASSERT_NULL(getenv(name)); free(name); } /* Test: unsetting multiple variables including one that does not exist. */ static void test_unset_envvars_multiple_existing_and_nonexistent(void) { char *n1 = make_unique_name("ENV_TEST_MULTI1"); char *n2 = make_unique_name("ENV_TEST_MULTI2"); char *n3 = make_unique_name("ENV_TEST_MULTI3_NOEXIST"); TEST_ASSERT_NOT_NULL(n1); TEST_ASSERT_NOT_NULL(n2); TEST_ASSERT_NOT_NULL(n3); /* Create two variables, leave third non-existent */ TEST_ASSERT_EQUAL_INT(0, setenv(n1, "v1", 1)); TEST_ASSERT_EQUAL_INT(0, setenv(n2, "v2", 1)); TEST_ASSERT_NOT_NULL(getenv(n1)); TEST_ASSERT_NOT_NULL(getenv(n2)); TEST_ASSERT_NULL(getenv(n3)); char const *list[] = { n1, n2, n3 }; prepare_usvars_list(list, 3); unset_envvars(); TEST_ASSERT_NULL(getenv(n1)); TEST_ASSERT_NULL(getenv(n2)); TEST_ASSERT_NULL(getenv(n3)); free(n1); free(n2); free(n3); } /* Test: unsetting a non-existent variable should not error and should remain non-existent. */ static void test_unset_envvars_nonexistent_only(void) { char *name = make_unique_name("ENV_TEST_NOEXIST"); TEST_ASSERT_NOT_NULL(name); /* Ensure it does not exist */ unsetenv(name); TEST_ASSERT_NULL(getenv(name)); char const *list[] = { name }; prepare_usvars_list(list, 1); unset_envvars(); TEST_ASSERT_NULL(getenv(name)); free(name); } /* Test: empty unset list should be a no-op (does not unset other variables). */ static void test_unset_envvars_empty_list_noop(void) { char *persist = make_unique_name("ENV_TEST_PERSIST"); TEST_ASSERT_NOT_NULL(persist); TEST_ASSERT_EQUAL_INT(0, setenv(persist, "keep", 1)); TEST_ASSERT_NOT_NULL(getenv(persist)); prepare_usvars_list(NULL, 0); /* No variables to unset */ unset_envvars(); TEST_ASSERT_NOT_NULL(getenv(persist)); /* Cleanup: remove the variable we created */ TEST_ASSERT_EQUAL_INT(0, unsetenv(persist)); TEST_ASSERT_NULL(getenv(persist)); free(persist); } /* Test: invalid variable name (contains '=') should cause error() to exit with EXIT_CANCELED. Run in a child process to avoid terminating the whole test runner. */ static void test_unset_envvars_invalid_name_triggers_error(void) { fflush(stdout); fflush(stderr); pid_t pid = fork(); TEST_ASSERT_MESSAGE(pid >= 0, "fork() failed"); if (pid == 0) { /* Child: set invalid name and call function; should exit via error(). */ char const *list[] = { "BAD=NAME" }; prepare_usvars_list(list, 1); /* Calling unset_envvars should invoke error(EXIT_CANCELED, ...) and exit. */ unset_envvars(); /* If we get here, it did not exit as expected. Use a distinct exit code. */ _exit(0xEE); } int status = 0; pid_t w = waitpid(pid, &status, 0); TEST_ASSERT_EQUAL_INT(pid, w); TEST_ASSERT_TRUE(WIFEXITED(status)); int es = WEXITSTATUS(status); /* EXIT_CANCELED is available from the program's headers included earlier. */ TEST_ASSERT_EQUAL_INT(EXIT_CANCELED, es); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_unset_envvars_single_existing); RUN_TEST(test_unset_envvars_multiple_existing_and_nonexistent); RUN_TEST(test_unset_envvars_nonexistent_only); RUN_TEST(test_unset_envvars_empty_list_noop); RUN_TEST(test_unset_envvars_invalid_name_triggers_error); return UNITY_END(); }