| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// Integration tests for unsetenv. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "src/__support/alloc-checker.h" |
| #include "src/stdlib/environ_internal.h" |
| #include "src/stdlib/getenv.h" |
| #include "src/stdlib/setenv.h" |
| #include "src/stdlib/unsetenv.h" |
| #include "src/string/memory_utils/inline_memcpy.h" |
| #include "src/unistd/environ.h" |
| |
| #include "test/IntegrationTest/test.h" |
| |
| #include <errno.h> |
| |
| namespace LIBC_NAMESPACE { |
| |
| TEST_MAIN([[maybe_unused]] int argc, [[maybe_unused]] char **argv, |
| [[maybe_unused]] char **envp) { |
| // Test: Remove a variable set by setenv |
| { |
| ASSERT_EQ(setenv("UNSET_VAR", "value", 1), 0); |
| ASSERT_TRUE(getenv("UNSET_VAR") != nullptr); |
| |
| ASSERT_EQ(unsetenv("UNSET_VAR"), 0); |
| ASSERT_TRUE(getenv("UNSET_VAR") == nullptr); |
| } |
| |
| // Test: Unset non-existent variable succeeds |
| { |
| ASSERT_EQ(unsetenv("DOES_NOT_EXIST"), 0); |
| } |
| |
| // Test: Empty name sets errno to EINVAL |
| { |
| errno = 0; |
| ASSERT_EQ(unsetenv(""), -1); |
| ASSERT_ERRNO_EQ(EINVAL); |
| } |
| |
| // Test: Name with '=' sets errno to EINVAL |
| { |
| errno = 0; |
| ASSERT_EQ(unsetenv("BAD=NAME"), -1); |
| ASSERT_ERRNO_EQ(EINVAL); |
| } |
| |
| // Test: Unset then re-set |
| { |
| ASSERT_EQ(setenv("REUSE_VAR", "first", 1), 0); |
| ASSERT_STREQ(getenv("REUSE_VAR"), "first"); |
| |
| ASSERT_EQ(unsetenv("REUSE_VAR"), 0); |
| ASSERT_TRUE(getenv("REUSE_VAR") == nullptr); |
| |
| ASSERT_EQ(setenv("REUSE_VAR", "second", 1), 0); |
| ASSERT_STREQ(getenv("REUSE_VAR"), "second"); |
| } |
| |
| // Test: Unset multiple variables |
| { |
| ASSERT_EQ(setenv("MULTI_A", "a", 1), 0); |
| ASSERT_EQ(setenv("MULTI_B", "b", 1), 0); |
| ASSERT_EQ(setenv("MULTI_C", "c", 1), 0); |
| |
| ASSERT_EQ(unsetenv("MULTI_B"), 0); |
| |
| ASSERT_TRUE(getenv("MULTI_A") != nullptr); |
| ASSERT_TRUE(getenv("MULTI_B") == nullptr); |
| ASSERT_TRUE(getenv("MULTI_C") != nullptr); |
| |
| ASSERT_STREQ(getenv("MULTI_A"), "a"); |
| ASSERT_STREQ(getenv("MULTI_C"), "c"); |
| } |
| |
| // Test: Unset same variable twice is harmless |
| { |
| ASSERT_EQ(setenv("DOUBLE_UNSET", "val", 1), 0); |
| ASSERT_EQ(unsetenv("DOUBLE_UNSET"), 0); |
| ASSERT_EQ(unsetenv("DOUBLE_UNSET"), 0); |
| ASSERT_TRUE(getenv("DOUBLE_UNSET") == nullptr); |
| } |
| |
| // Test: environ is updated and does NOT contain the variable |
| { |
| ASSERT_EQ(setenv("ENV_CHECK", "val", 1), 0); |
| // Verify it is in environ |
| bool found = false; |
| for (char **env = environ; *env != nullptr; ++env) { |
| if (cpp::string_view(*env) == "ENV_CHECK=val") { |
| found = true; |
| break; |
| } |
| } |
| ASSERT_TRUE(found); |
| |
| // Now unset it |
| ASSERT_EQ(unsetenv("ENV_CHECK"), 0); |
| |
| // Verify it is NOT in environ |
| found = false; |
| for (char **env = environ; *env != nullptr; ++env) { |
| bool match = true; |
| const char *prefix = "ENV_CHECK="; |
| for (size_t i = 0; i < 10; ++i) { |
| if ((*env)[i] == '\0' || (*env)[i] != prefix[i]) { |
| match = false; |
| break; |
| } |
| } |
| if (match) { |
| found = true; |
| break; |
| } |
| } |
| ASSERT_FALSE(found); |
| } |
| |
| // Test: Unset removes all duplicate instances of a variable |
| { |
| ASSERT_EQ(setenv("DUP_VAL1", "1", 1), 0); |
| ASSERT_EQ(setenv("DUP_VAL2", "2", 1), 0); |
| |
| AllocChecker ac; |
| char *d1 = new (ac) char[14]; |
| ASSERT_TRUE(ac); |
| const char *s1 = "DUP_VAR=first"; |
| inline_memcpy(d1, s1, 14); |
| |
| char *d2 = new (ac) char[15]; |
| ASSERT_TRUE(ac); |
| const char *s2 = "DUP_VAR=second"; |
| inline_memcpy(d2, s2, 15); |
| |
| internal::EnvironmentManager &mgr = |
| internal::EnvironmentManager::get_instance(); |
| char **env_array = mgr.begin(); |
| size_t count = mgr.size(); |
| |
| size_t idx1 = count; |
| size_t idx2 = count; |
| for (size_t i = 0; i < count; ++i) { |
| cpp::string_view curr(env_array[i]); |
| if (curr.starts_with("DUP_VAL1=")) |
| idx1 = i; |
| if (curr.starts_with("DUP_VAL2=")) |
| idx2 = i; |
| } |
| ASSERT_TRUE(idx1 < count); |
| ASSERT_TRUE(idx2 < count); |
| |
| delete[] env_array[idx1]; |
| delete[] env_array[idx2]; |
| |
| env_array[idx1] = d1; |
| env_array[idx2] = d2; |
| |
| ASSERT_EQ(unsetenv("DUP_VAR"), 0); |
| ASSERT_TRUE(getenv("DUP_VAR") == nullptr); |
| } |
| |
| return 0; |
| } |
| |
| } // namespace LIBC_NAMESPACE |