diff --git a/.gitmodules b/.gitmodules index 1bc9b3fcd6..31f9a62215 100644 --- a/.gitmodules +++ b/.gitmodules @@ -95,3 +95,6 @@ [submodule "projects/rocprofiler-systems/external/googletest"] path = projects/rocprofiler-systems/external/googletest url = https://github.com/google/googletest.git +[submodule "projects/rocprofiler-systems/external/filesystem"] + path = projects/rocprofiler-systems/external/filesystem + url = https://github.com/gulrak/filesystem.git diff --git a/projects/rocprofiler-systems/cmake/GhcFilesystem.cmake b/projects/rocprofiler-systems/cmake/GhcFilesystem.cmake new file mode 100644 index 0000000000..47262eeae8 --- /dev/null +++ b/projects/rocprofiler-systems/cmake/GhcFilesystem.cmake @@ -0,0 +1,58 @@ +# MIT License +# +# Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +include_guard(GLOBAL) + +message(STATUS "Setting up ghc::filesystem for tests") + +include(FetchContent) + +rocprofiler_systems_checkout_git_submodule( + RELATIVE_PATH external/filesystem + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + TEST_FILE include/ghc/filesystem.hpp + REPO_URL https://github.com/gulrak/filesystem.git + REPO_BRANCH "v1.5.14" +) + +# Configure ghc/filesystem options before adding it +set(GHC_FILESYSTEM_BUILD_TESTING OFF CACHE BOOL "Disable ghc filesystem tests" FORCE) +set(GHC_FILESYSTEM_BUILD_EXAMPLES OFF CACHE BOOL "Disable ghc filesystem examples" FORCE) +set(GHC_FILESYSTEM_WITH_INSTALL OFF CACHE BOOL "Disable ghc filesystem install" FORCE) + +# Declare ghc/filesystem from the submodule +FetchContent_Declare(ghc_filesystem SOURCE_DIR ${PROJECT_SOURCE_DIR}/external/filesystem) + +# Make ghc/filesystem available +FetchContent_MakeAvailable(ghc_filesystem) + +# Create interface library that wraps ghc::filesystem target +add_library(rocprofiler-systems-ghc-filesystem INTERFACE) + +target_link_libraries(rocprofiler-systems-ghc-filesystem INTERFACE ghc_filesystem) + +target_compile_definitions( + rocprofiler-systems-ghc-filesystem + INTERFACE ROCPROFSYS_TESTS_HAS_GHC_LIB_FILESYSTEM=1 +) + +message(STATUS "ghc::filesystem configured successfully using FetchContent") diff --git a/projects/rocprofiler-systems/cmake/Packages.cmake b/projects/rocprofiler-systems/cmake/Packages.cmake index 18f9f6b412..48bbe4fffe 100644 --- a/projects/rocprofiler-systems/cmake/Packages.cmake +++ b/projects/rocprofiler-systems/cmake/Packages.cmake @@ -691,6 +691,7 @@ include(NlohmannJson) if(ROCPROFSYS_BUILD_TESTING) include(GTest) + include(GhcFilesystem) endif() # ----------------------------------------------------------------------------------------# diff --git a/projects/rocprofiler-systems/external/filesystem b/projects/rocprofiler-systems/external/filesystem new file mode 160000 index 0000000000..8a2edd6d92 --- /dev/null +++ b/projects/rocprofiler-systems/external/filesystem @@ -0,0 +1 @@ +Subproject commit 8a2edd6d92ed820521d42c94d179462bf06b5ed3 diff --git a/projects/rocprofiler-systems/source/lib/common/environment.hpp b/projects/rocprofiler-systems/source/lib/common/environment.hpp index ca7d37d09b..7d2f45eceb 100644 --- a/projects/rocprofiler-systems/source/lib/common/environment.hpp +++ b/projects/rocprofiler-systems/source/lib/common/environment.hpp @@ -203,7 +203,8 @@ remove_env(std::vector& _environ, std::string_view _env_var, } // Remove null entries - _environ.erase(std::remove_if(_environ.begin(), _environ.end(), match), + _environ.erase(std::remove_if(_environ.begin(), _environ.end(), + [](const char* ptr) { return ptr == nullptr; }), _environ.end()); // Restore from original_envs if previously existed diff --git a/projects/rocprofiler-systems/source/lib/common/tests/CMakeLists.txt b/projects/rocprofiler-systems/source/lib/common/tests/CMakeLists.txt index 1ea7c50f1e..79d3f4ad03 100644 --- a/projects/rocprofiler-systems/source/lib/common/tests/CMakeLists.txt +++ b/projects/rocprofiler-systems/source/lib/common/tests/CMakeLists.txt @@ -20,9 +20,19 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -add_library(lib-common-tests OBJECT test_update_env.cpp) +add_library( + lib-common-tests + OBJECT + test_discover_llvm_libdir.cpp + test_path.cpp + test_remove_env.cpp + test_update_env.cpp +) target_link_libraries( lib-common-tests - PUBLIC rocprofiler-systems-common-library rocprofiler-systems-googletest-library + PUBLIC + rocprofiler-systems-common-library + rocprofiler-systems-googletest-library + rocprofiler-systems-ghc-filesystem ) diff --git a/projects/rocprofiler-systems/source/lib/common/tests/filesystem.hpp b/projects/rocprofiler-systems/source/lib/common/tests/filesystem.hpp new file mode 100644 index 0000000000..5d877ccd36 --- /dev/null +++ b/projects/rocprofiler-systems/source/lib/common/tests/filesystem.hpp @@ -0,0 +1,76 @@ +// MIT License +// +// Copyright (c) 2023-2025 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#if !defined(ROCPROFSYS_TESTS_HAS_GHC_LIB_FILESYSTEM) +# if defined __has_include +# if __has_include() +# define ROCPROFSYS_TESTS_HAS_GHC_LIB_FILESYSTEM 1 +# else +# define ROCPROFSYS_TESTS_HAS_GHC_LIB_FILESYSTEM 0 +# endif +# else +# define ROCPROFSYS_TESTS_HAS_GHC_LIB_FILESYSTEM 0 +# endif +#endif + +#if ROCPROFSYS_TESTS_HAS_GHC_LIB_FILESYSTEM == 0 +# if defined __has_include +# if __has_include() +# include +# endif +# endif + +# if defined(__cpp_lib_filesystem) +# define ROCPROFSYS_TESTS_HAS_CPP_LIB_FILESYSTEM 1 +# else +# if defined __has_include +# if __has_include() +# define ROCPROFSYS_TESTS_HAS_CPP_LIB_FILESYSTEM 1 +# endif +# endif +# endif +#endif + +#if defined(ROCPROFSYS_TESTS_HAS_GHC_LIB_FILESYSTEM) && \ + ROCPROFSYS_TESTS_HAS_GHC_LIB_FILESYSTEM > 0 +# include +#elif defined(ROCPROFSYS_TESTS_HAS_CPP_LIB_FILESYSTEM) && \ + ROCPROFSYS_TESTS_HAS_CPP_LIB_FILESYSTEM > 0 +# include +#else +# include +#endif + +namespace test_common +{ +#if defined(ROCPROFSYS_TESTS_HAS_GHC_LIB_FILESYSTEM) && \ + ROCPROFSYS_TESTS_HAS_GHC_LIB_FILESYSTEM > 0 +namespace fs = ::ghc::filesystem; // NOLINT(misc-unused-alias-decls) +#elif defined(ROCPROFSYS_TESTS_HAS_CPP_LIB_FILESYSTEM) && \ + ROCPROFSYS_TESTS_HAS_CPP_LIB_FILESYSTEM > 0 +namespace fs = ::std::filesystem; // NOLINT(misc-unused-alias-decls) +#else +namespace fs = ::std::experimental::filesystem; // NOLINT(misc-unused-alias-decls) +#endif +} // namespace test_common \ No newline at end of file diff --git a/projects/rocprofiler-systems/source/lib/common/tests/test_discover_llvm_libdir.cpp b/projects/rocprofiler-systems/source/lib/common/tests/test_discover_llvm_libdir.cpp new file mode 100644 index 0000000000..bf5678fb94 --- /dev/null +++ b/projects/rocprofiler-systems/source/lib/common/tests/test_discover_llvm_libdir.cpp @@ -0,0 +1,399 @@ +// MIT License +// +// Copyright (c) 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "common/environment.hpp" +#include "filesystem.hpp" + +#include +#include +#include +#include +#include + +using namespace rocprofsys::common; + +class DiscoverLlvmLibdirTest : public ::testing::Test +{ +protected: + void SetUp() override + { + m_test_dir = create_temp_dir(); + save_env_vars(); + } + + void TearDown() override + { + restore_env_vars(); + cleanup_temp_dir(m_test_dir); + } + + std::string create_temp_dir() + { + char tmpl[] = "/tmp/rocprofsys_llvm_test_XXXXXX"; + char* dir = mkdtemp(tmpl); + if(!dir) + { + throw std::runtime_error("Failed to create temp directory"); + } + return std::string{ dir }; + } + + void cleanup_temp_dir(const std::string& dir) + { + if(dir.empty()) return; + std::error_code ec; + test_common::fs::remove_all(dir, ec); + } + + void create_directory(const std::string& path) + { + std::error_code ec; + test_common::fs::create_directories(path, ec); + } + + void create_libomptarget(const std::string& dir) + { + create_directory(dir); + std::string lib_path = dir + "/libomptarget.so"; + std::ofstream ofs(lib_path); + ofs << "fake library content"; + } + + void save_env_vars() + { + char* rocm_path = getenv("ROCM_PATH"); + char* rocmv_dir = getenv("ROCmVersion_DIR"); + m_saved_rocm_path = + rocm_path ? std::optional{ rocm_path } : std::nullopt; + m_saved_rocmv_dir = + rocmv_dir ? std::optional{ rocmv_dir } : std::nullopt; + } + + void restore_env_vars() + { + if(m_saved_rocm_path.has_value()) + setenv("ROCM_PATH", m_saved_rocm_path->c_str(), 1); + else + unsetenv("ROCM_PATH"); + + if(m_saved_rocmv_dir.has_value()) + setenv("ROCmVersion_DIR", m_saved_rocmv_dir->c_str(), 1); + else + unsetenv("ROCmVersion_DIR"); + } + + void set_rocm_path(const std::string& path) { setenv("ROCM_PATH", path.c_str(), 1); } + + void set_rocmv_dir(const std::string& path) + { + setenv("ROCmVersion_DIR", path.c_str(), 1); + } + + void clear_rocm_path() { unsetenv("ROCM_PATH"); } + + void clear_rocmv_dir() { unsetenv("ROCmVersion_DIR"); } + + std::string m_test_dir; + std::optional m_saved_rocm_path; + std::optional m_saved_rocmv_dir; +}; + +TEST_F(DiscoverLlvmLibdirTest, FindsLibInRocmPathLlvmLib) +{ + std::string rocm_path = m_test_dir + "/rocm"; + std::string llvm_lib = rocm_path + "/llvm/lib"; + create_libomptarget(llvm_lib); + + set_rocm_path(rocm_path); + clear_rocmv_dir(); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, llvm_lib); +} + +TEST_F(DiscoverLlvmLibdirTest, FindsLibInRocmPathLibLlvmLib) +{ + std::string rocm_path = m_test_dir + "/rocm"; + std::string lib_llvm_lib = rocm_path + "/lib/llvm/lib"; + create_libomptarget(lib_llvm_lib); + + set_rocm_path(rocm_path); + clear_rocmv_dir(); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, lib_llvm_lib); +} + +TEST_F(DiscoverLlvmLibdirTest, PrefersLlvmLibOverLibLlvmLib) +{ + std::string rocm_path = m_test_dir + "/rocm"; + std::string llvm_lib = rocm_path + "/llvm/lib"; + std::string lib_llvm_lib = rocm_path + "/lib/llvm/lib"; + create_libomptarget(llvm_lib); + create_libomptarget(lib_llvm_lib); + + set_rocm_path(rocm_path); + clear_rocmv_dir(); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, llvm_lib); +} + +TEST_F(DiscoverLlvmLibdirTest, RocmVersionDirLlvmLib) +{ + std::string rocmv_dir = m_test_dir + "/rocm-version"; + std::string llvm_lib = rocmv_dir + "/llvm/lib"; + create_libomptarget(llvm_lib); + + clear_rocm_path(); + set_rocmv_dir(rocmv_dir); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, llvm_lib); +} + +TEST_F(DiscoverLlvmLibdirTest, RocmVersionDirLib) +{ + std::string rocmv_dir = m_test_dir + "/rocm-version"; + std::string lib_dir = rocmv_dir + "/lib"; + create_libomptarget(lib_dir); + + clear_rocm_path(); + set_rocmv_dir(rocmv_dir); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, lib_dir); +} + +TEST_F(DiscoverLlvmLibdirTest, RocmVersionDirTakesPrecedence) +{ + std::string rocm_path = m_test_dir + "/rocm"; + std::string rocmv_dir = m_test_dir + "/rocm-version"; + std::string rocm_llvm_lib = rocm_path + "/llvm/lib"; + std::string rocmv_lib = rocmv_dir + "/llvm/lib"; + create_libomptarget(rocm_llvm_lib); + create_libomptarget(rocmv_lib); + + set_rocm_path(rocm_path); + set_rocmv_dir(rocmv_dir); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, rocmv_lib); +} + +TEST_F(DiscoverLlvmLibdirTest, NoLibomptargetFound_InCustomPath) +{ + std::string rocm_path = m_test_dir + "/rocm"; + create_directory(rocm_path + "/llvm/lib"); + + set_rocm_path(rocm_path); + clear_rocmv_dir(); + + std::string result = discover_llvm_libdir_for_ompt(); + + // Result should either be empty (no ROCm installed) or one of the default + // fallback paths if ROCm is installed on the system + if(!result.empty()) + { + bool is_default_path = + (result == "/opt/rocm/llvm/lib" || result == "/opt/rocm/lib/llvm/lib"); + EXPECT_TRUE(is_default_path) + << "Expected empty or default ROCm path, got: " << result; + } +} + +TEST_F(DiscoverLlvmLibdirTest, TrailingSlashInRocmPath) +{ + std::string rocm_path = m_test_dir + "/rocm"; + std::string llvm_lib = rocm_path + "/llvm/lib"; + create_libomptarget(llvm_lib); + + set_rocm_path(rocm_path + "/"); + clear_rocmv_dir(); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, llvm_lib); +} + +TEST_F(DiscoverLlvmLibdirTest, TrailingSlashInRocmVersionDir) +{ + std::string rocmv_dir = m_test_dir + "/rocm-version"; + std::string llvm_lib = rocmv_dir + "/llvm/lib"; + create_libomptarget(llvm_lib); + + clear_rocm_path(); + set_rocmv_dir(rocmv_dir + "/"); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, llvm_lib); +} + +TEST_F(DiscoverLlvmLibdirTest, EmptyRocmVersionDir) +{ + std::string rocm_path = m_test_dir + "/rocm"; + std::string llvm_lib = rocm_path + "/llvm/lib"; + create_libomptarget(llvm_lib); + + set_rocm_path(rocm_path); + setenv("ROCmVersion_DIR", "", 1); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, llvm_lib); +} + +TEST_F(DiscoverLlvmLibdirTest, VerboseModeDoesNotCrash) +{ + std::string rocm_path = m_test_dir + "/rocm"; + std::string llvm_lib = rocm_path + "/llvm/lib"; + create_libomptarget(llvm_lib); + + set_rocm_path(rocm_path); + clear_rocmv_dir(); + + EXPECT_NO_THROW({ discover_llvm_libdir_for_ompt(true); }); +} + +TEST_F(DiscoverLlvmLibdirTest, VerboseModeNoLibFound) +{ + std::string rocm_path = m_test_dir + "/rocm"; + create_directory(rocm_path); + + set_rocm_path(rocm_path); + clear_rocmv_dir(); + + EXPECT_NO_THROW({ discover_llvm_libdir_for_ompt(true); }); +} + +TEST_F(DiscoverLlvmLibdirTest, RocmVersionDirLlvmLibPreferredOverLib) +{ + std::string rocmv_dir = m_test_dir + "/rocm-version"; + std::string llvm_lib = rocmv_dir + "/llvm/lib"; + std::string lib_dir = rocmv_dir + "/lib"; + create_libomptarget(llvm_lib); + create_libomptarget(lib_dir); + + clear_rocm_path(); + set_rocmv_dir(rocmv_dir); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, llvm_lib); +} + +TEST_F(DiscoverLlvmLibdirTest, FallsBackToRocmPathWhenRocmVersionDirHasNoLib) +{ + std::string rocm_path = m_test_dir + "/rocm"; + std::string rocmv_dir = m_test_dir + "/rocm-version"; + std::string llvm_lib = rocm_path + "/llvm/lib"; + create_directory(rocmv_dir + "/llvm/lib"); + create_libomptarget(llvm_lib); + + set_rocm_path(rocm_path); + set_rocmv_dir(rocmv_dir); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, llvm_lib); +} + +TEST_F(DiscoverLlvmLibdirTest, MultipleTrailingSlashes) +{ + std::string rocm_path = m_test_dir + "/rocm"; + std::string llvm_lib = rocm_path + "/llvm/lib"; + create_libomptarget(llvm_lib); + + set_rocm_path(rocm_path + "/"); + clear_rocmv_dir(); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_FALSE(result.empty()); + EXPECT_NE(result.find("llvm/lib"), std::string::npos); +} + +TEST_F(DiscoverLlvmLibdirTest, PathWithSpaces) +{ + std::string rocm_path = m_test_dir + "/rocm with spaces"; + std::string llvm_lib = rocm_path + "/llvm/lib"; + + std::error_code ec; + test_common::fs::create_directories(llvm_lib, ec); + + std::string lib_path = llvm_lib + "/libomptarget.so"; + std::ofstream ofs(lib_path); + ofs << "fake library content"; + ofs.close(); + + set_rocm_path(rocm_path); + clear_rocmv_dir(); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, llvm_lib); +} + +TEST_F(DiscoverLlvmLibdirTest, BothEnvVarsUnset_UsesDefaultRocmPath) +{ + clear_rocm_path(); + clear_rocmv_dir(); + + std::string result = discover_llvm_libdir_for_ompt(); + + if(!result.empty()) + { + EXPECT_TRUE(result.find("/opt/rocm") != std::string::npos || + result.find("llvm/lib") != std::string::npos); + } +} + +TEST_F(DiscoverLlvmLibdirTest, SymlinkToLibomptarget) +{ + std::string rocm_path = m_test_dir + "/rocm"; + std::string llvm_lib = rocm_path + "/llvm/lib"; + std::string actual_dir = m_test_dir + "/actual"; + std::string actual_file = actual_dir + "/libomptarget.so"; + + create_directory(actual_dir); + std::ofstream ofs(actual_file); + ofs << "fake library content"; + ofs.close(); + + create_directory(llvm_lib); + std::string link_path = llvm_lib + "/libomptarget.so"; + symlink(actual_file.c_str(), link_path.c_str()); + + set_rocm_path(rocm_path); + clear_rocmv_dir(); + + std::string result = discover_llvm_libdir_for_ompt(); + + EXPECT_EQ(result, llvm_lib); +} diff --git a/projects/rocprofiler-systems/source/lib/common/tests/test_path.cpp b/projects/rocprofiler-systems/source/lib/common/tests/test_path.cpp new file mode 100644 index 0000000000..4e91873be7 --- /dev/null +++ b/projects/rocprofiler-systems/source/lib/common/tests/test_path.cpp @@ -0,0 +1,393 @@ +// MIT License +// +// Copyright (c) 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "common/path.hpp" +#include "filesystem.hpp" + +#include +#include +#include +#include +#include + +using namespace rocprofsys::common::path; + +class PathTest : public ::testing::Test +{ +protected: + void SetUp() override { m_test_dir = create_temp_dir(); } + + void TearDown() override { cleanup_temp_dir(m_test_dir); } + + std::string create_temp_dir() + { + char tmpl[] = "/tmp/rocprofsys_path_test_XXXXXX"; + char* dir = mkdtemp(tmpl); + if(!dir) + { + throw std::runtime_error("Failed to create temp directory"); + } + return std::string{ dir }; + } + + void cleanup_temp_dir(const std::string& dir) + { + if(dir.empty()) return; + std::error_code ec; + test_common::fs::remove_all(dir, ec); + } + + std::string create_file(const std::string& name, const std::string& content = "test") + { + std::string path = m_test_dir + "/" + name; + std::ofstream ofs(path); + ofs << content; + return path; + } + + std::string create_symlink(const std::string& target, const std::string& link_name) + { + std::string link_path = m_test_dir + "/" + link_name; + symlink(target.c_str(), link_path.c_str()); + return link_path; + } + + std::string create_subdir(const std::string& name) + { + std::string path = m_test_dir + "/" + name; + mkdir(path.c_str(), 0755); + return path; + } + + std::string m_test_dir; +}; + +TEST_F(PathTest, Dirname_StandardPath) +{ + EXPECT_EQ(dirname("/usr/local/bin/program"), "/usr/local/bin"); +} + +TEST_F(PathTest, Dirname_SingleLevel) { EXPECT_EQ(dirname("/usr/file"), "/usr"); } + +TEST_F(PathTest, Dirname_RootFile) { EXPECT_EQ(dirname("/file"), ""); } + +TEST_F(PathTest, Dirname_NoSlash) { EXPECT_EQ(dirname("filename"), ""); } + +TEST_F(PathTest, Dirname_EmptyString) { EXPECT_EQ(dirname(""), ""); } + +TEST_F(PathTest, Dirname_TrailingSlash) +{ + EXPECT_EQ(dirname("/usr/local/"), "/usr/local"); +} + +TEST_F(PathTest, Dirname_MultipleSlashes) +{ + EXPECT_EQ(dirname("/a/b/c/d/e"), "/a/b/c/d"); +} + +TEST_F(PathTest, Exists_ExistingFile) +{ + std::string file_path = create_file("existing_file.txt"); + EXPECT_TRUE(exists(file_path)); +} + +TEST_F(PathTest, Exists_NonexistentFile) +{ + EXPECT_FALSE(exists(m_test_dir + "/nonexistent_file.txt")); +} + +TEST_F(PathTest, Exists_ExistingDirectory) { EXPECT_TRUE(exists(m_test_dir)); } + +TEST_F(PathTest, Exists_NonexistentDirectory) +{ + EXPECT_FALSE(exists("/nonexistent/path/to/dir")); +} + +TEST_F(PathTest, Exists_SymbolicLink) +{ + std::string target = create_file("target.txt"); + std::string link_path = create_symlink(target, "link_to_target"); + EXPECT_TRUE(exists(link_path)); +} + +TEST_F(PathTest, Exists_BrokenSymlink) +{ + std::string link_path = create_symlink("/nonexistent/target", "broken_link"); + EXPECT_TRUE(exists(link_path)); +} + +TEST_F(PathTest, Exists_EmptyPath) { EXPECT_FALSE(exists("")); } + +TEST_F(PathTest, IsLink_RegularFile) +{ + std::string file_path = create_file("regular.txt"); + EXPECT_FALSE(is_link(file_path)); +} + +TEST_F(PathTest, IsLink_Directory) { EXPECT_FALSE(is_link(m_test_dir)); } + +TEST_F(PathTest, IsLink_SymbolicLink) +{ + std::string target = create_file("target.txt"); + std::string link_path = create_symlink(target, "symbolic_link"); + EXPECT_TRUE(is_link(link_path)); +} + +TEST_F(PathTest, IsLink_NonexistentPath) { EXPECT_FALSE(is_link("/nonexistent/path")); } + +TEST_F(PathTest, Readlink_SymbolicLink) +{ + std::string target = create_file("readlink_target.txt"); + std::string link_path = create_symlink(target, "readlink_link"); + EXPECT_EQ(readlink(link_path), target); +} + +TEST_F(PathTest, Readlink_NotALink) +{ + std::string file_path = create_file("not_a_link.txt"); + EXPECT_EQ(readlink(file_path), file_path); +} + +TEST_F(PathTest, Readlink_NonexistentPath) +{ + std::string path = "/nonexistent/path"; + EXPECT_EQ(readlink(path), path); +} + +TEST_F(PathTest, Realpath_RelativePath) +{ + std::string file_path = create_file("realpath_test.txt"); + + char cwd[PATH_MAX]; + char* cwd_result = getcwd(cwd, PATH_MAX); + ASSERT_NE(cwd_result, nullptr); + + if(chdir(m_test_dir.c_str()) == 0) + { + std::string resolved = realpath("realpath_test.txt"); + EXPECT_EQ(resolved, file_path); + ASSERT_EQ(chdir(cwd), 0); + } +} + +TEST_F(PathTest, Realpath_AbsolutePath) +{ + std::string file_path = create_file("absolute_test.txt"); + std::string resolved = realpath(file_path); + EXPECT_EQ(resolved, file_path); +} + +TEST_F(PathTest, Realpath_WithSymlink) +{ + std::string target = create_file("realpath_target.txt"); + std::string link_path = create_symlink(target, "realpath_link"); + std::string resolved = realpath(link_path); + EXPECT_EQ(resolved, target); +} + +TEST_F(PathTest, Realpath_NonexistentPath) +{ + std::string nonexistent = "/nonexistent/path/to/file"; + std::string resolved = realpath(nonexistent); + EXPECT_EQ(resolved, nonexistent); +} + +TEST_F(PathTest, Realpath_WithResolvedOutput) +{ + std::string file_path = create_file("resolved_output_test.txt"); + std::string resolved_output; + std::string result = realpath(file_path, &resolved_output); + EXPECT_EQ(result, file_path); + EXPECT_EQ(resolved_output, file_path); +} + +TEST_F(PathTest, IsTextFile_TextFile) +{ + std::string text_content = "This is a text file\nwith multiple lines\n"; + std::string file_path = create_file("text_file.txt", text_content); + EXPECT_TRUE(is_text_file(file_path)); +} + +TEST_F(PathTest, IsTextFile_BinaryFile) +{ + std::string file_path = m_test_dir + "/binary_file.bin"; + std::ofstream ofs(file_path, std::ios::binary); + char binary_data[] = { 'H', 'e', 'l', 'l', 'o', '\0', 'W', 'o', 'r', 'l', 'd' }; + ofs.write(binary_data, sizeof(binary_data)); + ofs.close(); + EXPECT_FALSE(is_text_file(file_path)); +} + +TEST_F(PathTest, IsTextFile_EmptyFile) +{ + std::string file_path = create_file("empty_file.txt", ""); + EXPECT_TRUE(is_text_file(file_path)); +} + +TEST_F(PathTest, PathType_Directory) +{ + path_type pt(m_test_dir); + EXPECT_TRUE(pt.exists()); + EXPECT_TRUE(static_cast(pt)); +} + +TEST_F(PathTest, PathType_RegularFile) +{ + std::string file_path = create_file("pathtype_file.txt"); + path_type pt(file_path); + EXPECT_TRUE(pt.exists()); +} + +TEST_F(PathTest, PathType_SymbolicLink) +{ + std::string target = create_file("pathtype_target.txt"); + std::string link_path = create_symlink(target, "pathtype_link"); + path_type pt(link_path); + EXPECT_TRUE(pt.exists()); +} + +TEST_F(PathTest, PathType_Nonexistent) +{ + path_type pt("/nonexistent/path"); + EXPECT_FALSE(pt.exists()); + EXPECT_FALSE(static_cast(pt)); +} + +TEST_F(PathTest, GetRocprofsysRoot_ReturnsNonEmpty) +{ + std::string root = get_rocprofsys_root(); + EXPECT_FALSE(root.empty()); +} + +TEST_F(PathTest, GetRocprofsysRoot_EndsWithParentDir) +{ + std::string root = get_rocprofsys_root(); + EXPECT_TRUE(root.length() >= 2); + EXPECT_EQ(root.substr(root.length() - 2), ".."); +} + +TEST_F(PathTest, GetInternalLibdir_ContainsLib) +{ + std::string libdir = get_internal_libdir(); + EXPECT_NE(libdir.find("lib"), std::string::npos); +} + +TEST_F(PathTest, GetInternalScriptPath_ContainsLibexec) +{ + std::string script_path = get_internal_script_path(); + EXPECT_NE(script_path.find("libexec"), std::string::npos); + EXPECT_NE(script_path.find("rocprofiler-systems"), std::string::npos); +} + +TEST_F(PathTest, GetInternalLibpath_ContainsLibName) +{ + std::string libpath = get_internal_libpath("librocprof-sys.so"); + EXPECT_NE(libpath.find("librocprof-sys.so"), std::string::npos); +} + +TEST_F(PathTest, GetInternalLibpath_ContainsLib) +{ + std::string libpath = get_internal_libpath("test.so"); + EXPECT_NE(libpath.find("lib"), std::string::npos); +} + +TEST_F(PathTest, GetDefaultLibSearchPaths_ReturnsNonEmpty) +{ + auto paths = get_default_lib_search_paths(); + EXPECT_FALSE(paths.empty()); +} + +TEST_F(PathTest, GetDefaultLibSearchPaths_AsVector) +{ + auto paths = get_default_lib_search_paths>(); + EXPECT_FALSE(paths.empty()); +} + +TEST_F(PathTest, FindPath_AbsoluteExisting) +{ + std::string file_path = create_file("findpath_test.txt"); + std::string result = find_path(file_path, 0); + EXPECT_EQ(result, file_path); +} + +TEST_F(PathTest, FindPath_NonexistentReturnsOriginal) +{ + std::string nonexistent = "nonexistent_file_xyz.txt"; + std::string result = find_path(nonexistent, 0); + EXPECT_EQ(result, nonexistent); +} + +TEST_F(PathTest, FindPath_InSearchPath) +{ + std::string file_path = create_file("searchable.txt"); + std::string result = find_path("searchable.txt", 0, m_test_dir); + EXPECT_EQ(result, file_path); +} + +TEST_F(PathTest, Dirname_ComplexPath) +{ + EXPECT_EQ(dirname("/opt/rocm/lib/rocprofiler-systems/librocprof-sys.so"), + "/opt/rocm/lib/rocprofiler-systems"); +} + +TEST_F(PathTest, ChainedSymlinks) +{ + std::string target = create_file("chain_target.txt"); + std::string link1 = create_symlink(target, "chain_link1"); + std::string link2_path = m_test_dir + "/chain_link2"; + symlink("chain_link1", link2_path.c_str()); + + EXPECT_TRUE(is_link(link1)); + EXPECT_TRUE(is_link(link2_path)); + + std::string resolved = realpath(link2_path); + EXPECT_EQ(resolved, target); +} + +TEST_F(PathTest, Exists_SpecialCharactersInPath) +{ + std::string file_path = create_file("file with spaces.txt"); + EXPECT_TRUE(exists(file_path)); +} + +TEST_F(PathTest, Dirname_RocprofsysTypicalPath) +{ + std::string path = "/opt/rocm-6.0.0/lib/rocprofiler-systems/librocprof-sys-dl.so"; + std::string result = dirname(path); + EXPECT_EQ(result, "/opt/rocm-6.0.0/lib/rocprofiler-systems"); +} + +TEST_F(PathTest, NestedDirectories) +{ + std::string subdir1 = create_subdir("level1"); + std::string subdir2 = subdir1 + "/level2"; + mkdir(subdir2.c_str(), 0755); + std::string subdir3 = subdir2 + "/level3"; + mkdir(subdir3.c_str(), 0755); + + EXPECT_TRUE(exists(subdir1)); + EXPECT_TRUE(exists(subdir2)); + EXPECT_TRUE(exists(subdir3)); + + EXPECT_EQ(dirname(subdir3), subdir2); + EXPECT_EQ(dirname(subdir2), subdir1); +} diff --git a/projects/rocprofiler-systems/source/lib/common/tests/test_remove_env.cpp b/projects/rocprofiler-systems/source/lib/common/tests/test_remove_env.cpp new file mode 100644 index 0000000000..bd7c47a2fd --- /dev/null +++ b/projects/rocprofiler-systems/source/lib/common/tests/test_remove_env.cpp @@ -0,0 +1,308 @@ +// MIT License +// +// Copyright (c) 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "common/environment.hpp" + +#include +#include +#include +#include +#include +#include + +using namespace rocprofsys::common; + +namespace +{ +std::string +find_env_var(const std::vector& env, std::string_view var_name) +{ + std::string prefix = std::string(var_name) + "="; + for(auto* itr : env) + { + if(!itr) continue; + if(std::string_view{ itr }.find(prefix) == 0) + { + return std::string{ itr }; + } + } + return ""; +} +} // namespace + +class RemoveEnvTest : public ::testing::Test +{ +protected: + void SetUp() override + { + m_env_vars.clear(); + m_original_envs.clear(); + } + + void TearDown() override + { + for(auto* ptr : m_env_vars) + { + if(ptr) free(ptr); + } + } + + std::vector m_env_vars; + std::unordered_set m_original_envs; +}; + +TEST_F(RemoveEnvTest, RemoveSingleVariable) +{ + m_env_vars.push_back(strdup("VAR1=value1")); + m_env_vars.push_back(strdup("VAR2=value2")); + m_env_vars.push_back(strdup("VAR3=value3")); + + remove_env(m_env_vars, "VAR2", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 2); + EXPECT_STREQ(m_env_vars[0], "VAR1=value1"); + EXPECT_STREQ(m_env_vars[1], "VAR3=value3"); +} + +TEST_F(RemoveEnvTest, RemoveFirstVariable) +{ + m_env_vars.push_back(strdup("FIRST_VAR=first")); + m_env_vars.push_back(strdup("SECOND_VAR=second")); + + remove_env(m_env_vars, "FIRST_VAR", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "SECOND_VAR=second"); +} + +TEST_F(RemoveEnvTest, RemoveLastVariable) +{ + m_env_vars.push_back(strdup("FIRST_VAR=first")); + m_env_vars.push_back(strdup("LAST_VAR=last")); + + remove_env(m_env_vars, "LAST_VAR", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "FIRST_VAR=first"); +} + +TEST_F(RemoveEnvTest, RemoveNonexistentVariable) +{ + m_env_vars.push_back(strdup("EXISTING_VAR=value")); + + remove_env(m_env_vars, "NONEXISTENT_VAR", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "EXISTING_VAR=value"); +} + +TEST_F(RemoveEnvTest, RemoveFromEmptyVector) +{ + remove_env(m_env_vars, "ANY_VAR", m_original_envs); + + EXPECT_TRUE(m_env_vars.empty()); +} + +TEST_F(RemoveEnvTest, RemoveOnlyVariable) +{ + m_env_vars.push_back(strdup("ONLY_VAR=only_value")); + + remove_env(m_env_vars, "ONLY_VAR", m_original_envs); + + EXPECT_TRUE(m_env_vars.empty()); +} + +TEST_F(RemoveEnvTest, RestoreFromOriginalEnvs) +{ + m_original_envs.insert("RESTORE_VAR=original_value"); + + m_env_vars.push_back(strdup("RESTORE_VAR=modified_value")); + m_env_vars.push_back(strdup("OTHER_VAR=other_value")); + + remove_env(m_env_vars, "RESTORE_VAR", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 2); + EXPECT_STREQ(find_env_var(m_env_vars, "OTHER_VAR").c_str(), "OTHER_VAR=other_value"); + EXPECT_STREQ(find_env_var(m_env_vars, "RESTORE_VAR").c_str(), + "RESTORE_VAR=original_value"); +} + +TEST_F(RemoveEnvTest, RemoveVariableNotInOriginal_NoRestore) +{ + m_env_vars.push_back(strdup("NEW_VAR=new_value")); + + remove_env(m_env_vars, "NEW_VAR", m_original_envs); + + EXPECT_TRUE(m_env_vars.empty()); +} + +TEST_F(RemoveEnvTest, RemoveWithSimilarPrefixes) +{ + m_env_vars.push_back(strdup("PATH=/usr/bin")); + m_env_vars.push_back(strdup("PATH_EXTRA=/extra")); + m_env_vars.push_back(strdup("MYPATH=/my")); + + remove_env(m_env_vars, "PATH", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 2); + EXPECT_STREQ(m_env_vars[0], "PATH_EXTRA=/extra"); + EXPECT_STREQ(m_env_vars[1], "MYPATH=/my"); +} + +TEST_F(RemoveEnvTest, RemoveWithNullEntries) +{ + m_env_vars.push_back(strdup("VAR1=value1")); + m_env_vars.push_back(nullptr); + m_env_vars.push_back(strdup("VAR2=value2")); + m_env_vars.push_back(nullptr); + m_env_vars.push_back(strdup("VAR3=value3")); + + remove_env(m_env_vars, "VAR2", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 2); + EXPECT_STREQ(m_env_vars[0], "VAR1=value1"); + EXPECT_STREQ(m_env_vars[1], "VAR3=value3"); +} + +TEST_F(RemoveEnvTest, RemoveMultipleTimes) +{ + m_env_vars.push_back(strdup("A=1")); + m_env_vars.push_back(strdup("B=2")); + m_env_vars.push_back(strdup("C=3")); + m_env_vars.push_back(strdup("D=4")); + + remove_env(m_env_vars, "B", m_original_envs); + ASSERT_EQ(m_env_vars.size(), 3); + + remove_env(m_env_vars, "D", m_original_envs); + ASSERT_EQ(m_env_vars.size(), 2); + + EXPECT_STREQ(m_env_vars[0], "A=1"); + EXPECT_STREQ(m_env_vars[1], "C=3"); +} + +TEST_F(RemoveEnvTest, RealWorld_LD_PRELOAD) +{ + m_env_vars.push_back(strdup("LD_LIBRARY_PATH=/usr/lib")); + m_env_vars.push_back(strdup("LD_PRELOAD=/lib/inject.so")); + m_env_vars.push_back(strdup("PATH=/usr/bin")); + + remove_env(m_env_vars, "LD_PRELOAD", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 2); + EXPECT_FALSE(find_env_var(m_env_vars, "LD_PRELOAD").length() > 0); + EXPECT_STREQ(find_env_var(m_env_vars, "LD_LIBRARY_PATH").c_str(), + "LD_LIBRARY_PATH=/usr/lib"); + EXPECT_STREQ(find_env_var(m_env_vars, "PATH").c_str(), "PATH=/usr/bin"); +} + +TEST_F(RemoveEnvTest, RealWorld_RestoreROCPROFSYS_Variable) +{ + m_original_envs.insert("ROCPROFSYS_TRACE=false"); + + m_env_vars.push_back(strdup("ROCPROFSYS_TRACE=true")); + m_env_vars.push_back(strdup("ROCPROFSYS_PROFILE=true")); + + remove_env(m_env_vars, "ROCPROFSYS_TRACE", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 2); + EXPECT_STREQ(find_env_var(m_env_vars, "ROCPROFSYS_TRACE").c_str(), + "ROCPROFSYS_TRACE=false"); + EXPECT_STREQ(find_env_var(m_env_vars, "ROCPROFSYS_PROFILE").c_str(), + "ROCPROFSYS_PROFILE=true"); +} + +TEST_F(RemoveEnvTest, EmptyVariableName) +{ + m_env_vars.push_back(strdup("VAR=value")); + + remove_env(m_env_vars, "", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "VAR=value"); +} + +TEST_F(RemoveEnvTest, VariableWithEmptyValue) +{ + m_env_vars.push_back(strdup("EMPTY_VALUE=")); + m_env_vars.push_back(strdup("NORMAL_VAR=value")); + + remove_env(m_env_vars, "EMPTY_VALUE", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "NORMAL_VAR=value"); +} + +TEST_F(RemoveEnvTest, VariableWithSpecialCharactersInValue) +{ + m_env_vars.push_back(strdup("SPECIAL=a:b:c:/path/with spaces")); + m_env_vars.push_back(strdup("OTHER=value")); + + remove_env(m_env_vars, "SPECIAL", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "OTHER=value"); +} + +TEST_F(RemoveEnvTest, LongVariableName) +{ + std::string long_var_name = "VERY_LONG_ENVIRONMENT_VARIABLE_NAME_FOR_TESTING"; + std::string entry = long_var_name + "=some_value"; + m_env_vars.push_back(strdup(entry.c_str())); + m_env_vars.push_back(strdup("SHORT=val")); + + remove_env(m_env_vars, long_var_name, m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "SHORT=val"); +} + +TEST_F(RemoveEnvTest, RestoreMultipleOriginalValues) +{ + m_original_envs.insert("VAR1=orig1"); + m_original_envs.insert("VAR2=orig2"); + m_original_envs.insert("VAR3=orig3"); + + m_env_vars.push_back(strdup("VAR1=modified1")); + m_env_vars.push_back(strdup("VAR2=modified2")); + m_env_vars.push_back(strdup("VAR3=modified3")); + + remove_env(m_env_vars, "VAR2", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 3); + EXPECT_STREQ(find_env_var(m_env_vars, "VAR1").c_str(), "VAR1=modified1"); + EXPECT_STREQ(find_env_var(m_env_vars, "VAR2").c_str(), "VAR2=orig2"); + EXPECT_STREQ(find_env_var(m_env_vars, "VAR3").c_str(), "VAR3=modified3"); +} + +TEST_F(RemoveEnvTest, CaseSensitiveRemoval) +{ + m_env_vars.push_back(strdup("MyVar=value1")); + m_env_vars.push_back(strdup("MYVAR=value2")); + m_env_vars.push_back(strdup("myvar=value3")); + + remove_env(m_env_vars, "MYVAR", m_original_envs); + + ASSERT_EQ(m_env_vars.size(), 2); + EXPECT_STREQ(m_env_vars[0], "MyVar=value1"); + EXPECT_STREQ(m_env_vars[1], "myvar=value3"); +} diff --git a/projects/rocprofiler-systems/source/lib/common/tests/test_update_env.cpp b/projects/rocprofiler-systems/source/lib/common/tests/test_update_env.cpp index 521b22a0a3..4489a9e069 100644 --- a/projects/rocprofiler-systems/source/lib/common/tests/test_update_env.cpp +++ b/projects/rocprofiler-systems/source/lib/common/tests/test_update_env.cpp @@ -51,212 +51,212 @@ class UpdateEnvTest : public ::testing::Test protected: void SetUp() override { - env_vars.clear(); - updated_envs.clear(); - original_envs.clear(); + m_env_vars.clear(); + m_updated_envs.clear(); + m_original_envs.clear(); } void TearDown() override { - for(auto* ptr : env_vars) + for(auto* ptr : m_env_vars) { if(ptr) free(ptr); } } - std::vector env_vars; - std::unordered_set updated_envs; - std::unordered_set original_envs; + std::vector m_env_vars; + std::unordered_set m_updated_envs; + std::unordered_set m_original_envs; }; TEST_F(UpdateEnvTest, ReplaceMode_NewVariable) { - update_env(env_vars, "TEST_VAR", "test_value", update_mode::REPLACE, ":", - updated_envs, original_envs); + update_env(m_env_vars, "TEST_VAR", "test_value", update_mode::REPLACE, ":", + m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "TEST_VAR=test_value"); - EXPECT_EQ(updated_envs.count("TEST_VAR"), 1); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "TEST_VAR=test_value"); + EXPECT_EQ(m_updated_envs.count("TEST_VAR"), 1); } TEST_F(UpdateEnvTest, ReplaceMode_ExistingVariable) { - env_vars.push_back(strdup("TEST_VAR=old_value")); - original_envs.insert("TEST_VAR=old_value"); + m_env_vars.push_back(strdup("TEST_VAR=old_value")); + m_original_envs.insert("TEST_VAR=old_value"); - update_env(env_vars, "TEST_VAR", "new_value", update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "TEST_VAR", "new_value", update_mode::REPLACE, ":", + m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "TEST_VAR=new_value"); - EXPECT_EQ(updated_envs.count("TEST_VAR"), 1); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "TEST_VAR=new_value"); + EXPECT_EQ(m_updated_envs.count("TEST_VAR"), 1); } TEST_F(UpdateEnvTest, AppendMode_NewVariable) { - update_env(env_vars, "PATH", "/new/path", update_mode::APPEND, ":", updated_envs, - original_envs); + update_env(m_env_vars, "PATH", "/new/path", update_mode::APPEND, ":", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "PATH=/new/path"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "PATH=/new/path"); } TEST_F(UpdateEnvTest, AppendMode_ExistingVariable) { - env_vars.push_back(strdup("PATH=/old/path")); - original_envs.insert("PATH=/old/path"); + m_env_vars.push_back(strdup("PATH=/old/path")); + m_original_envs.insert("PATH=/old/path"); - update_env(env_vars, "PATH", "/new/path", update_mode::APPEND, ":", updated_envs, - original_envs); + update_env(m_env_vars, "PATH", "/new/path", update_mode::APPEND, ":", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "PATH=/old/path:/new/path"); - EXPECT_EQ(updated_envs.count("PATH"), 1); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "PATH=/old/path:/new/path"); + EXPECT_EQ(m_updated_envs.count("PATH"), 1); } TEST_F(UpdateEnvTest, PrependMode_ExistingVariable) { - env_vars.push_back(strdup("LD_LIBRARY_PATH=/old/lib")); - original_envs.insert("LD_LIBRARY_PATH=/old/lib"); + m_env_vars.push_back(strdup("LD_LIBRARY_PATH=/old/lib")); + m_original_envs.insert("LD_LIBRARY_PATH=/old/lib"); - update_env(env_vars, "LD_LIBRARY_PATH", "/new/lib", update_mode::PREPEND, ":", - updated_envs, original_envs); + update_env(m_env_vars, "LD_LIBRARY_PATH", "/new/lib", update_mode::PREPEND, ":", + m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "LD_LIBRARY_PATH=/new/lib:/old/lib"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "LD_LIBRARY_PATH=/new/lib:/old/lib"); } TEST_F(UpdateEnvTest, WeakMode_OriginalValue) { - env_vars.push_back(strdup("WEAK_VAR=original")); - original_envs.insert("WEAK_VAR=original"); + m_env_vars.push_back(strdup("WEAK_VAR=original")); + m_original_envs.insert("WEAK_VAR=original"); - update_env(env_vars, "WEAK_VAR", "new_value", update_mode::WEAK, ":", updated_envs, - original_envs); + update_env(m_env_vars, "WEAK_VAR", "new_value", update_mode::WEAK, ":", + m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "WEAK_VAR=new_value"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "WEAK_VAR=new_value"); } TEST_F(UpdateEnvTest, WeakMode_ModifiedValue) { - env_vars.push_back(strdup("WEAK_VAR=original")); - original_envs.insert("WEAK_VAR=original"); + m_env_vars.push_back(strdup("WEAK_VAR=original")); + m_original_envs.insert("WEAK_VAR=original"); - free(env_vars[0]); - env_vars[0] = strdup("WEAK_VAR=modified"); + free(m_env_vars[0]); + m_env_vars[0] = strdup("WEAK_VAR=modified"); - update_env(env_vars, "WEAK_VAR", "new_value", update_mode::WEAK, ":", updated_envs, - original_envs); + update_env(m_env_vars, "WEAK_VAR", "new_value", update_mode::WEAK, ":", + m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "WEAK_VAR=modified"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "WEAK_VAR=modified"); } TEST_F(UpdateEnvTest, BooleanValue_True) { - update_env(env_vars, "BOOL_VAR", true, update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "BOOL_VAR", true, update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "BOOL_VAR=true"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "BOOL_VAR=true"); } TEST_F(UpdateEnvTest, BooleanValue_False) { - update_env(env_vars, "BOOL_VAR", false, update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "BOOL_VAR", false, update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "BOOL_VAR=false"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "BOOL_VAR=false"); } TEST_F(UpdateEnvTest, NumericValue) { - update_env(env_vars, "NUM_VAR", 42, update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "NUM_VAR", 42, update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "NUM_VAR=42"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "NUM_VAR=42"); } TEST_F(UpdateEnvTest, AppendMode_AvoidsDuplicates) { - env_vars.push_back(strdup("PATH=/existing/path")); - original_envs.insert("PATH=/existing/path"); + m_env_vars.push_back(strdup("PATH=/existing/path")); + m_original_envs.insert("PATH=/existing/path"); - update_env(env_vars, "PATH", "/existing/path", update_mode::APPEND, ":", updated_envs, - original_envs); + update_env(m_env_vars, "PATH", "/existing/path", update_mode::APPEND, ":", + m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "PATH=/existing/path"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "PATH=/existing/path"); } TEST_F(UpdateEnvTest, CustomDelimiter) { - env_vars.push_back(strdup("VAR=a")); - original_envs.insert("VAR=a"); + m_env_vars.push_back(strdup("VAR=a")); + m_original_envs.insert("VAR=a"); - update_env(env_vars, "VAR", "b", update_mode::APPEND, ",", updated_envs, - original_envs); + update_env(m_env_vars, "VAR", "b", update_mode::APPEND, ",", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "VAR=a,b"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "VAR=a,b"); } TEST_F(UpdateEnvTest, RealWorld_LD_LIBRARY_PATH_Append) { - env_vars.push_back(strdup("LD_LIBRARY_PATH=/usr/lib:/usr/local/lib")); - original_envs.insert("LD_LIBRARY_PATH=/usr/lib:/usr/local/lib"); + m_env_vars.push_back(strdup("LD_LIBRARY_PATH=/usr/lib:/usr/local/lib")); + m_original_envs.insert("LD_LIBRARY_PATH=/usr/lib:/usr/local/lib"); - update_env(env_vars, "LD_LIBRARY_PATH", "/opt/rocm/lib", update_mode::APPEND, ":", - updated_envs, original_envs); + update_env(m_env_vars, "LD_LIBRARY_PATH", "/opt/rocm/lib", update_mode::APPEND, ":", + m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "LD_LIBRARY_PATH=/usr/lib:/usr/local/lib:/opt/rocm/lib"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "LD_LIBRARY_PATH=/usr/lib:/usr/local/lib:/opt/rocm/lib"); } TEST_F(UpdateEnvTest, RealWorld_LD_PRELOAD_Prepend) { - env_vars.push_back(strdup("LD_PRELOAD=/lib/existing.so")); - original_envs.insert("LD_PRELOAD=/lib/existing.so"); + m_env_vars.push_back(strdup("LD_PRELOAD=/lib/existing.so")); + m_original_envs.insert("LD_PRELOAD=/lib/existing.so"); - update_env(env_vars, "LD_PRELOAD", "/opt/rocm/librocprof-sys-dl.so", - update_mode::PREPEND, ":", updated_envs, original_envs); + update_env(m_env_vars, "LD_PRELOAD", "/opt/rocm/librocprof-sys-dl.so", + update_mode::PREPEND, ":", m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "LD_PRELOAD=/opt/rocm/librocprof-sys-dl.so:/lib/existing.so"); } TEST_F(UpdateEnvTest, RealWorld_ROCPROFSYS_Environment_Variables) { - update_env(env_vars, "ROCPROFSYS_TRACE", true, update_mode::REPLACE, ":", - updated_envs, original_envs); - update_env(env_vars, "ROCPROFSYS_PROFILE", false, update_mode::REPLACE, ":", - updated_envs, original_envs); - update_env(env_vars, "ROCPROFSYS_USE_SAMPLING", true, update_mode::REPLACE, ":", - updated_envs, original_envs); + update_env(m_env_vars, "ROCPROFSYS_TRACE", true, update_mode::REPLACE, ":", + m_updated_envs, m_original_envs); + update_env(m_env_vars, "ROCPROFSYS_PROFILE", false, update_mode::REPLACE, ":", + m_updated_envs, m_original_envs); + update_env(m_env_vars, "ROCPROFSYS_USE_SAMPLING", true, update_mode::REPLACE, ":", + m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 3); - EXPECT_STREQ(find_env_var(env_vars, "ROCPROFSYS_TRACE").c_str(), + ASSERT_EQ(m_env_vars.size(), 3); + EXPECT_STREQ(find_env_var(m_env_vars, "ROCPROFSYS_TRACE").c_str(), "ROCPROFSYS_TRACE=true"); - EXPECT_STREQ(find_env_var(env_vars, "ROCPROFSYS_PROFILE").c_str(), + EXPECT_STREQ(find_env_var(m_env_vars, "ROCPROFSYS_PROFILE").c_str(), "ROCPROFSYS_PROFILE=false"); - EXPECT_STREQ(find_env_var(env_vars, "ROCPROFSYS_USE_SAMPLING").c_str(), + EXPECT_STREQ(find_env_var(m_env_vars, "ROCPROFSYS_USE_SAMPLING").c_str(), "ROCPROFSYS_USE_SAMPLING=true"); } TEST_F(UpdateEnvTest, RealWorld_Timing_DoubleValues) { - update_env(env_vars, "ROCPROFSYS_TRACE_DELAY", 1.5, update_mode::REPLACE, ":", - updated_envs, original_envs); - update_env(env_vars, "ROCPROFSYS_SAMPLING_FREQ", 100.0, update_mode::REPLACE, ":", - updated_envs, original_envs); + update_env(m_env_vars, "ROCPROFSYS_TRACE_DELAY", 1.5, update_mode::REPLACE, ":", + m_updated_envs, m_original_envs); + update_env(m_env_vars, "ROCPROFSYS_SAMPLING_FREQ", 100.0, update_mode::REPLACE, ":", + m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 2); - std::string delay_var = find_env_var(env_vars, "ROCPROFSYS_TRACE_DELAY"); - std::string freq_var = find_env_var(env_vars, "ROCPROFSYS_SAMPLING_FREQ"); + ASSERT_EQ(m_env_vars.size(), 2); + std::string delay_var = find_env_var(m_env_vars, "ROCPROFSYS_TRACE_DELAY"); + std::string freq_var = find_env_var(m_env_vars, "ROCPROFSYS_SAMPLING_FREQ"); EXPECT_TRUE(delay_var.find("ROCPROFSYS_TRACE_DELAY=") == 0); EXPECT_TRUE(freq_var.find("ROCPROFSYS_SAMPLING_FREQ=") == 0); @@ -265,156 +265,156 @@ TEST_F(UpdateEnvTest, RealWorld_Timing_DoubleValues) TEST_F(UpdateEnvTest, StringTypes_StdString) { std::string value = "test_string_value"; - update_env(env_vars, "STRING_VAR", value, update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "STRING_VAR", value, update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "STRING_VAR=test_string_value"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "STRING_VAR=test_string_value"); } TEST_F(UpdateEnvTest, StringTypes_ConstCharPtr) { const char* value = "const_char_value"; - update_env(env_vars, "CHAR_VAR", value, update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "CHAR_VAR", value, update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "CHAR_VAR=const_char_value"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "CHAR_VAR=const_char_value"); } TEST_F(UpdateEnvTest, EmptyStringValue) { - update_env(env_vars, "EMPTY_VAR", "", update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "EMPTY_VAR", "", update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "EMPTY_VAR="); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "EMPTY_VAR="); } TEST_F(UpdateEnvTest, MultipleVariables_DifferentNames) { - update_env(env_vars, "VAR1", "value1", update_mode::REPLACE, ":", updated_envs, - original_envs); - update_env(env_vars, "VAR2", "value2", update_mode::REPLACE, ":", updated_envs, - original_envs); - update_env(env_vars, "VAR3", "value3", update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "VAR1", "value1", update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); + update_env(m_env_vars, "VAR2", "value2", update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); + update_env(m_env_vars, "VAR3", "value3", update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 3); - EXPECT_EQ(updated_envs.size(), 3); - EXPECT_STREQ(find_env_var(env_vars, "VAR1").c_str(), "VAR1=value1"); - EXPECT_STREQ(find_env_var(env_vars, "VAR2").c_str(), "VAR2=value2"); - EXPECT_STREQ(find_env_var(env_vars, "VAR3").c_str(), "VAR3=value3"); + ASSERT_EQ(m_env_vars.size(), 3); + EXPECT_EQ(m_updated_envs.size(), 3); + EXPECT_STREQ(find_env_var(m_env_vars, "VAR1").c_str(), "VAR1=value1"); + EXPECT_STREQ(find_env_var(m_env_vars, "VAR2").c_str(), "VAR2=value2"); + EXPECT_STREQ(find_env_var(m_env_vars, "VAR3").c_str(), "VAR3=value3"); } TEST_F(UpdateEnvTest, NullPointer_InEnvironmentVector) { - env_vars.push_back(strdup("VAR1=value1")); - env_vars.push_back(nullptr); - env_vars.push_back(strdup("VAR2=value2")); - original_envs.insert("VAR1=value1"); - original_envs.insert("VAR2=value2"); + m_env_vars.push_back(strdup("VAR1=value1")); + m_env_vars.push_back(nullptr); + m_env_vars.push_back(strdup("VAR2=value2")); + m_original_envs.insert("VAR1=value1"); + m_original_envs.insert("VAR2=value2"); - update_env(env_vars, "VAR2", "new_value2", update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "VAR2", "new_value2", update_mode::REPLACE, ":", + m_updated_envs, m_original_envs); - EXPECT_STREQ(find_env_var(env_vars, "VAR2").c_str(), "VAR2=new_value2"); + EXPECT_STREQ(find_env_var(m_env_vars, "VAR2").c_str(), "VAR2=new_value2"); } TEST_F(UpdateEnvTest, LongPath_Append) { std::string long_path = "/very/long/path/to/some/directory/with/many/subdirectories/" "that/might/be/used/in/real/world"; - env_vars.push_back(strdup("PATH=/usr/bin:/bin")); - original_envs.insert("PATH=/usr/bin:/bin"); + m_env_vars.push_back(strdup("PATH=/usr/bin:/bin")); + m_original_envs.insert("PATH=/usr/bin:/bin"); - update_env(env_vars, "PATH", long_path, update_mode::APPEND, ":", updated_envs, - original_envs); + update_env(m_env_vars, "PATH", long_path, update_mode::APPEND, ":", m_updated_envs, + m_original_envs); std::string expected = "PATH=/usr/bin:/bin:" + long_path; - EXPECT_STREQ(env_vars[0], expected.c_str()); + EXPECT_STREQ(m_env_vars[0], expected.c_str()); } TEST_F(UpdateEnvTest, SpecialCharacters_InValue) { - update_env(env_vars, "SPECIAL_VAR", "value-with_special.chars:123", - update_mode::REPLACE, ":", updated_envs, original_envs); + update_env(m_env_vars, "SPECIAL_VAR", "value-with_special.chars:123", + update_mode::REPLACE, ":", m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "SPECIAL_VAR=value-with_special.chars:123"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "SPECIAL_VAR=value-with_special.chars:123"); } TEST_F(UpdateEnvTest, IntegerValues_Positive) { - update_env(env_vars, "INT_VAR", 12345, update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "INT_VAR", 12345, update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "INT_VAR=12345"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "INT_VAR=12345"); } TEST_F(UpdateEnvTest, IntegerValues_Negative) { - update_env(env_vars, "NEGATIVE_VAR", -999, update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "NEGATIVE_VAR", -999, update_mode::REPLACE, ":", + m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "NEGATIVE_VAR=-999"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "NEGATIVE_VAR=-999"); } TEST_F(UpdateEnvTest, IntegerValues_Zero) { - update_env(env_vars, "ZERO_VAR", 0, update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "ZERO_VAR", 0, update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "ZERO_VAR=0"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "ZERO_VAR=0"); } TEST_F(UpdateEnvTest, UpdateTracking_MultipleUpdates) { - update_env(env_vars, "VAR1", "val1", update_mode::REPLACE, ":", updated_envs, - original_envs); - update_env(env_vars, "VAR2", "val2", update_mode::REPLACE, ":", updated_envs, - original_envs); - update_env(env_vars, "VAR1", "val1_updated", update_mode::REPLACE, ":", updated_envs, - original_envs); + update_env(m_env_vars, "VAR1", "val1", update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); + update_env(m_env_vars, "VAR2", "val2", update_mode::REPLACE, ":", m_updated_envs, + m_original_envs); + update_env(m_env_vars, "VAR1", "val1_updated", update_mode::REPLACE, ":", + m_updated_envs, m_original_envs); - EXPECT_EQ(updated_envs.count("VAR1"), 1); - EXPECT_EQ(updated_envs.count("VAR2"), 1); - EXPECT_EQ(updated_envs.size(), 2); + EXPECT_EQ(m_updated_envs.count("VAR1"), 1); + EXPECT_EQ(m_updated_envs.count("VAR2"), 1); + EXPECT_EQ(m_updated_envs.size(), 2); } TEST_F(UpdateEnvTest, WeakMode_SequentialUpdates) { - env_vars.push_back(strdup("CONFIG_VAR=initial")); - original_envs.insert("CONFIG_VAR=initial"); + m_env_vars.push_back(strdup("CONFIG_VAR=initial")); + m_original_envs.insert("CONFIG_VAR=initial"); - update_env(env_vars, "CONFIG_VAR", "weak_update", update_mode::WEAK, ":", - updated_envs, original_envs); + update_env(m_env_vars, "CONFIG_VAR", "weak_update", update_mode::WEAK, ":", + m_updated_envs, m_original_envs); - EXPECT_STREQ(env_vars[0], "CONFIG_VAR=weak_update"); + EXPECT_STREQ(m_env_vars[0], "CONFIG_VAR=weak_update"); - free(env_vars[0]); - env_vars[0] = strdup("CONFIG_VAR=user_modified"); + free(m_env_vars[0]); + m_env_vars[0] = strdup("CONFIG_VAR=user_modified"); - update_env(env_vars, "CONFIG_VAR", "another_weak_update", update_mode::WEAK, ":", - updated_envs, original_envs); + update_env(m_env_vars, "CONFIG_VAR", "another_weak_update", update_mode::WEAK, ":", + m_updated_envs, m_original_envs); - EXPECT_STREQ(env_vars[0], "CONFIG_VAR=user_modified"); + EXPECT_STREQ(m_env_vars[0], "CONFIG_VAR=user_modified"); } TEST_F(UpdateEnvTest, Append_MultiplePathsInSequence) { - update_env(env_vars, "BUILD_PATH", "/path1", update_mode::REPLACE, ":", updated_envs, - original_envs); - update_env(env_vars, "BUILD_PATH", "/path2", update_mode::APPEND, ":", updated_envs, - original_envs); - update_env(env_vars, "BUILD_PATH", "/path3", update_mode::APPEND, ":", updated_envs, - original_envs); - update_env(env_vars, "BUILD_PATH", "/path4", update_mode::APPEND, ":", updated_envs, - original_envs); + update_env(m_env_vars, "BUILD_PATH", "/path1", update_mode::REPLACE, ":", + m_updated_envs, m_original_envs); + update_env(m_env_vars, "BUILD_PATH", "/path2", update_mode::APPEND, ":", + m_updated_envs, m_original_envs); + update_env(m_env_vars, "BUILD_PATH", "/path3", update_mode::APPEND, ":", + m_updated_envs, m_original_envs); + update_env(m_env_vars, "BUILD_PATH", "/path4", update_mode::APPEND, ":", + m_updated_envs, m_original_envs); - ASSERT_EQ(env_vars.size(), 1); - EXPECT_STREQ(env_vars[0], "BUILD_PATH=/path1:/path2:/path3:/path4"); + ASSERT_EQ(m_env_vars.size(), 1); + EXPECT_STREQ(m_env_vars[0], "BUILD_PATH=/path1:/path2:/path3:/path4"); } \ No newline at end of file