test: Restore config test environment variables with a guard (#8333)

Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bart
2026-10-02 18:24:41 +00:00
committed by GitHub
parent 6a05339c6c
commit 0a6da4de74

View File

@@ -122,6 +122,83 @@ backend=sqlite
return std::format(kConfigContentsTemplate, dbPathSection, valFileSection);
}
// setenv and unsetenv are POSIX only, and MSVC does not have them.
#ifndef _MSC_VER
/**
* Read an environment variable into a value the caller owns.
*
* The value is copied, because POSIX allows a later `setenv` to release the
* string `getenv` returned.
*
* @param name Name of the environment variable to read.
* @return The value, or std::nullopt when the variable is not set.
*/
[[nodiscard]] std::optional<std::string>
envVar(char const* name)
{
if (char const* const value = std::getenv(name); value != nullptr)
return std::string{value};
return std::nullopt;
}
/**
* Set an environment variable and restore its previous value when done.
*/
class EnvVarGuard
{
private:
std::string const name_;
std::optional<std::string> const saved_;
/**
* Give an environment variable a value, or remove it.
*
* @param name Name of the environment variable to write.
* @param value Value to set, or std::nullopt to remove the variable.
*/
static void
apply(char const* name, std::optional<std::string> const& value) noexcept
{
if (value)
{
setenv(name, value->c_str(), 1);
}
else
{
unsetenv(name);
}
}
public:
/**
* Save the variable's current value, then write the given one.
*
* @param name Name of the environment variable to set.
* @param value Value to set, or std::nullopt to remove the variable.
*/
[[nodiscard]] EnvVarGuard(char const* name, std::optional<std::string> const& value)
: name_(name), saved_(envVar(name))
{
apply(name_.c_str(), value);
}
EnvVarGuard(EnvVarGuard const&) = delete;
EnvVarGuard&
operator=(EnvVarGuard const&) = delete;
/**
* Restore the value the variable held before construction.
*/
~EnvVarGuard()
{
apply(name_.c_str(), saved_);
}
};
#endif // _MSC_VER
/**
* Write an xrpld config file and remove when done.
*/
@@ -335,7 +412,11 @@ port_wss_admin
}
// Config file in HOME or XDG_CONFIG_HOME directory.
#if BOOST_OS_LINUX || BOOST_OS_MACOS
#ifndef _MSC_VER
// Save the values the guards below must put back.
auto const home = detail::envVar("HOME");
auto const xdgConfigHome = detail::envVar("XDG_CONFIG_HOME");
for (auto const& configFile : configFiles)
{
// Point the current working directory to a temporary directory, so
@@ -348,13 +429,11 @@ port_wss_admin
{
TempDir const tc;
// Set the HOME and XDG_CONFIG_HOME environment variables. The
// HOME variable is not used when XDG_CONFIG_HOME is set, but
// must be set.
char const* h = getenv("HOME");
setenv("HOME", tc.path().c_str(), 1);
char const* x = getenv("XDG_CONFIG_HOME");
setenv("XDG_CONFIG_HOME", tc.path().c_str(), 1);
// The HOME variable is not used when XDG_CONFIG_HOME is set,
// but must be set. Both guards are declared after tc, so they
// restore before it is removed.
detail::EnvVarGuard const homeGuard("HOME", tc.path());
detail::EnvVarGuard const xdgGuard("XDG_CONFIG_HOME", tc.path());
// Create the config file in '${XDG_CONFIG_HOME}/[systemName]'.
Path p = tc.file(systemName());
@@ -371,22 +450,21 @@ port_wss_admin
BEAST_EXPECT(
c.section(Sections::kDebugLogfile).values()[0] ==
"/Users/dummy/xrpld/config/log/debug.log");
// Restore the environment variables.
(h != nullptr) ? setenv("HOME", h, 1) : unsetenv("HOME");
(x != nullptr) ? setenv("XDG_CONFIG_HOME", x, 1) : unsetenv("XDG_CONFIG_HOME");
}
// Both guards are gone: both variables must hold their old values.
BEAST_EXPECT(detail::envVar("HOME") == home);
BEAST_EXPECT(detail::envVar("XDG_CONFIG_HOME") == xdgConfigHome);
// The XDG config directory is not set: the config file must be in a
// subdirectory named .config followed by the system name.
{
TempDir const tc;
// Set only the HOME environment variable.
char const* h = getenv("HOME");
setenv("HOME", tc.path().c_str(), 1);
char const* x = getenv("XDG_CONFIG_HOME");
unsetenv("XDG_CONFIG_HOME");
// Both guards are declared after tc, so they restore before it
// is removed.
detail::EnvVarGuard const homeGuard("HOME", tc.path());
detail::EnvVarGuard const xdgGuard("XDG_CONFIG_HOME", std::nullopt);
// Create the config file in '${HOME}/.config/[systemName]'.
std::string s = ".config";
@@ -407,14 +485,13 @@ port_wss_admin
BEAST_EXPECT(
c.section(Sections::kDebugLogfile).values()[0] ==
"/Users/dummy/xrpld/config/log/debug.log");
// Restore the environment variables.
(h != nullptr) ? setenv("HOME", h, 1) : unsetenv("HOME");
if (x != nullptr)
setenv("XDG_CONFIG_HOME", x, 1);
}
// Both guards are gone: both variables must hold their old values.
BEAST_EXPECT(detail::envVar("HOME") == home);
BEAST_EXPECT(detail::envVar("XDG_CONFIG_HOME") == xdgConfigHome);
}
#endif
#endif // _MSC_VER
// Restore the current working directory.
current_path(cwd);