From 1340fd30010d489b822105110ea34f76643400f7 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Sun, 30 Mar 2025 17:47:00 -0400 Subject: [PATCH] Erase environment variables set by exit tests after reading them. The environment variables we currently use to pass information from the parent process to the child process are implementation details of exit tests and may change or be removed in a future update. To minimize the risk of code relying on these environment variables, and to avoid accidentally trying to open inherited file descriptors twice, clear the variables after reading them. --- Sources/Testing/ExitTests/ExitTest.swift | 8 ++++ Sources/Testing/Support/Environment.swift | 39 +++++++++++++++++++ .../Support/EnvironmentTests.swift | 39 ------------------- 3 files changed, 47 insertions(+), 39 deletions(-) diff --git a/Sources/Testing/ExitTests/ExitTest.swift b/Sources/Testing/ExitTests/ExitTest.swift index 69346b74e..bd5cb95b9 100644 --- a/Sources/Testing/ExitTests/ExitTest.swift +++ b/Sources/Testing/ExitTests/ExitTest.swift @@ -457,6 +457,10 @@ extension ExitTest { return nil } + // Erase the environment variable so that it cannot accidentally be opened + // twice (nor, in theory, affect the code of the exit test.) + Environment.setVariable(nil, named: "SWT_EXPERIMENTAL_BACKCHANNEL") + var fd: CInt? #if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) fd = CInt(backChannelEnvironmentVariable) @@ -487,6 +491,10 @@ extension ExitTest { // Find the ID of the exit test to run, if any, in the environment block. var id: ExitTest.ID? if var idString = Environment.variable(named: "SWT_EXPERIMENTAL_EXIT_TEST_ID") { + // Clear the environment variable. It's an implementation detail and exit + // test code shouldn't be dependent on it. Use ExitTest.current if needed! + Environment.setVariable(nil, named: "SWT_EXPERIMENTAL_EXIT_TEST_ID") + id = try? idString.withUTF8 { idBuffer in try JSON.decode(ExitTest.ID.self, from: UnsafeRawBufferPointer(idBuffer)) } diff --git a/Sources/Testing/Support/Environment.swift b/Sources/Testing/Support/Environment.swift index f09efc1ed..2ab3710a4 100644 --- a/Sources/Testing/Support/Environment.swift +++ b/Sources/Testing/Support/Environment.swift @@ -235,3 +235,42 @@ enum Environment { } } } + +// MARK: - Setting variables + +extension Environment { + /// Set the environment variable with the specified name. + /// + /// - Parameters: + /// - value: The new value for the specified environment variable. Pass + /// `nil` to remove the variable from the current process' environment. + /// - name: The name of the environment variable. + /// + /// - Returns: Whether or not the environment variable was successfully set. + @discardableResult + static func setVariable(_ value: String?, named name: String) -> Bool { +#if SWT_NO_ENVIRONMENT_VARIABLES + simulatedEnvironment.withLock { environment in + environment[name] = value + } + return true +#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) + if let value { + return 0 == setenv(name, value, 1) + } + return 0 == unsetenv(name) +#elseif os(Windows) + name.withCString(encodedAs: UTF16.self) { name in + if let value { + return value.withCString(encodedAs: UTF16.self) { value in + SetEnvironmentVariableW(name, value) + } + } + return SetEnvironmentVariableW(name, nil) + } +#else +#warning("Platform-specific implementation missing: environment variables unavailable") + return false +#endif + } +} diff --git a/Tests/TestingTests/Support/EnvironmentTests.swift b/Tests/TestingTests/Support/EnvironmentTests.swift index 512ebfe7b..43d8ea3f3 100644 --- a/Tests/TestingTests/Support/EnvironmentTests.swift +++ b/Tests/TestingTests/Support/EnvironmentTests.swift @@ -71,42 +71,3 @@ struct EnvironmentTests { #expect(Environment.flag(named: name) == false) } } - -// MARK: - Fixtures - -extension Environment { - /// Set the environment variable with the specified name. - /// - /// - Parameters: - /// - value: The new value for the specified environment variable. Pass - /// `nil` to remove the variable from the current process' environment. - /// - name: The name of the environment variable. - /// - /// - Returns: Whether or not the environment variable was successfully set. - @discardableResult - static func setVariable(_ value: String?, named name: String) -> Bool { -#if SWT_NO_ENVIRONMENT_VARIABLES - simulatedEnvironment.withLock { environment in - environment[name] = value - } - return true -#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) - if let value { - return 0 == setenv(name, value, 1) - } - return 0 == unsetenv(name) -#elseif os(Windows) - name.withCString(encodedAs: UTF16.self) { name in - if let value { - return value.withCString(encodedAs: UTF16.self) { value in - SetEnvironmentVariableW(name, value) - } - } - return SetEnvironmentVariableW(name, nil) - } -#else -#warning("Platform-specific implementation missing: environment variables unavailable") - return false -#endif - } -}