diff --git a/Sources/Testing/Support/Additions/CommandLineAdditions.swift b/Sources/Testing/Support/Additions/CommandLineAdditions.swift index 57d9851a8..63decd1ad 100644 --- a/Sources/Testing/Support/Additions/CommandLineAdditions.swift +++ b/Sources/Testing/Support/Additions/CommandLineAdditions.swift @@ -57,11 +57,17 @@ extension CommandLine { } #elseif os(OpenBSD) // OpenBSD does not have API to get a path to the running executable. Use - // arguments[0]. We do a basic sniff test for a path-like string, but - // otherwise return argv[0] verbatim. - guard let argv0 = arguments.first, argv0.contains("/") else { + // arguments[0]. We do a basic sniff test for a path-like string, and + // prepend the early CWD if it looks like a relative path, but otherwise + // return argv[0] verbatim. + guard var argv0 = arguments.first, argv0.contains("/") else { throw CError(rawValue: ENOEXEC) } + if argv0.first != "/", + let earlyCWD = swt_getEarlyCWD().flatMap(String.init(validatingCString:)), + !earlyCWD.isEmpty { + argv0 = "\(earlyCWD)/\(argv0)" + } return argv0 #elseif os(Windows) var result: String? diff --git a/Sources/_TestingInternals/CMakeLists.txt b/Sources/_TestingInternals/CMakeLists.txt index a951c7d4b..b2bc5b6b1 100644 --- a/Sources/_TestingInternals/CMakeLists.txt +++ b/Sources/_TestingInternals/CMakeLists.txt @@ -12,6 +12,7 @@ include(GitCommit) include(TargetTriple) add_library(_TestingInternals STATIC Discovery.cpp + ExecutablePath.cpp Versions.cpp WillThrow.cpp) target_include_directories(_TestingInternals PUBLIC diff --git a/Sources/_TestingInternals/ExecutablePath.cpp b/Sources/_TestingInternals/ExecutablePath.cpp new file mode 100644 index 000000000..0d23653f6 --- /dev/null +++ b/Sources/_TestingInternals/ExecutablePath.cpp @@ -0,0 +1,35 @@ +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for Swift project authors +// + +#include "ExecutablePath.h" + +#include + +#if defined(__OpenBSD__) +static std::atomic earlyCWD { nullptr }; + +/// At process start (before `main()` is called), capture the current working +/// directory. +/// +/// This function is necessary on OpenBSD so that we can (as correctly as +/// possible) resolve the executable path when the first argument is a relative +/// path (which can occur when manually invoking the test executable.) +__attribute__((__constructor__(101), __used__)) +static void swt_captureEarlyCWD(void) { + static char buffer[PATH_MAX * 2]; + if (getcwd(buffer, sizeof(buffer))) { + earlyCWD.store(buffer); + } +} + +const char *swt_getEarlyCWD(void) { + return earlyCWD.load(); +} +#endif diff --git a/Sources/_TestingInternals/include/ExecutablePath.h b/Sources/_TestingInternals/include/ExecutablePath.h new file mode 100644 index 000000000..22f3acc62 --- /dev/null +++ b/Sources/_TestingInternals/include/ExecutablePath.h @@ -0,0 +1,31 @@ +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for Swift project authors +// + +#if !defined(SWT_EXECUTABLE_PATH_H) +#define SWT_EXECUTABLE_PATH_H + +#include "Defines.h" +#include "Includes.h" + +SWT_ASSUME_NONNULL_BEGIN + +#if defined(__OpenBSD__) +/// Get the current working directory as it was set shortly after the process +/// started and before `main()` has been called. +/// +/// This function is necessary on OpenBSD so that we can (as correctly as +/// possible) resolve the executable path when the first argument is a relative +/// path (which can occur when manually invoking the test executable.) +SWT_EXTERN const char *_Nullable swt_getEarlyCWD(void); +#endif + +SWT_ASSUME_NONNULL_END + +#endif diff --git a/Tests/TestingTests/ExitTestTests.swift b/Tests/TestingTests/ExitTestTests.swift index 5be229266..6edabc305 100644 --- a/Tests/TestingTests/ExitTestTests.swift +++ b/Tests/TestingTests/ExitTestTests.swift @@ -625,6 +625,14 @@ private import _TestingInternals } } #endif + +#if os(OpenBSD) + @Test("Changing the CWD doesn't break exit tests") + func changeCWD() async throws { + try #require(0 == chdir("/")) + await #expect(processExitsWith: .success) {} + } +#endif } // MARK: - Fixtures