Skip to content

Commit 55ff51e

Browse files
authored
Capture the current working directory early on OpenBSD. (#1388)
This PR adds some OpenBSD-specific code to capture the current working directory as early as possible. We then use that directory when argv[0] appears to be a relative path so that the following incantation works correctly: ```sh ./.build/debug/myPackageTests.xctest --testing-library swift-testing ``` This logic is not necessary on other platforms because they all provide a way to get the path to the current executable. OpenBSD has no such API. Resolves #1348. ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent 38a7758 commit 55ff51e

File tree

5 files changed

+84
-3
lines changed

5 files changed

+84
-3
lines changed

Sources/Testing/Support/Additions/CommandLineAdditions.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,17 @@ extension CommandLine {
5757
}
5858
#elseif os(OpenBSD)
5959
// OpenBSD does not have API to get a path to the running executable. Use
60-
// arguments[0]. We do a basic sniff test for a path-like string, but
61-
// otherwise return argv[0] verbatim.
62-
guard let argv0 = arguments.first, argv0.contains("/") else {
60+
// arguments[0]. We do a basic sniff test for a path-like string, and
61+
// prepend the early CWD if it looks like a relative path, but otherwise
62+
// return argv[0] verbatim.
63+
guard var argv0 = arguments.first, argv0.contains("/") else {
6364
throw CError(rawValue: ENOEXEC)
6465
}
66+
if argv0.first != "/",
67+
let earlyCWD = swt_getEarlyCWD().flatMap(String.init(validatingCString:)),
68+
!earlyCWD.isEmpty {
69+
argv0 = "\(earlyCWD)/\(argv0)"
70+
}
6571
return argv0
6672
#elseif os(Windows)
6773
var result: String?

Sources/_TestingInternals/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ include(GitCommit)
1212
include(TargetTriple)
1313
add_library(_TestingInternals STATIC
1414
Discovery.cpp
15+
ExecutablePath.cpp
1516
Versions.cpp
1617
WillThrow.cpp)
1718
target_include_directories(_TestingInternals PUBLIC
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// This source file is part of the Swift.org open source project
3+
//
4+
// Copyright (c) 2024 Apple Inc. and the Swift project authors
5+
// Licensed under Apache License v2.0 with Runtime Library Exception
6+
//
7+
// See https://swift.org/LICENSE.txt for license information
8+
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
//
10+
11+
#include "ExecutablePath.h"
12+
13+
#include <atomic>
14+
15+
#if defined(__OpenBSD__)
16+
static std::atomic<const char *> earlyCWD { nullptr };
17+
18+
/// At process start (before `main()` is called), capture the current working
19+
/// directory.
20+
///
21+
/// This function is necessary on OpenBSD so that we can (as correctly as
22+
/// possible) resolve the executable path when the first argument is a relative
23+
/// path (which can occur when manually invoking the test executable.)
24+
__attribute__((__constructor__(101), __used__))
25+
static void swt_captureEarlyCWD(void) {
26+
static char buffer[PATH_MAX * 2];
27+
if (getcwd(buffer, sizeof(buffer))) {
28+
earlyCWD.store(buffer);
29+
}
30+
}
31+
32+
const char *swt_getEarlyCWD(void) {
33+
return earlyCWD.load();
34+
}
35+
#endif
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// This source file is part of the Swift.org open source project
3+
//
4+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
5+
// Licensed under Apache License v2.0 with Runtime Library Exception
6+
//
7+
// See https://swift.org/LICENSE.txt for license information
8+
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
//
10+
11+
#if !defined(SWT_EXECUTABLE_PATH_H)
12+
#define SWT_EXECUTABLE_PATH_H
13+
14+
#include "Defines.h"
15+
#include "Includes.h"
16+
17+
SWT_ASSUME_NONNULL_BEGIN
18+
19+
#if defined(__OpenBSD__)
20+
/// Get the current working directory as it was set shortly after the process
21+
/// started and before `main()` has been called.
22+
///
23+
/// This function is necessary on OpenBSD so that we can (as correctly as
24+
/// possible) resolve the executable path when the first argument is a relative
25+
/// path (which can occur when manually invoking the test executable.)
26+
SWT_EXTERN const char *_Nullable swt_getEarlyCWD(void);
27+
#endif
28+
29+
SWT_ASSUME_NONNULL_END
30+
31+
#endif

Tests/TestingTests/ExitTestTests.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,14 @@ private import _TestingInternals
625625
}
626626
}
627627
#endif
628+
629+
#if os(OpenBSD)
630+
@Test("Changing the CWD doesn't break exit tests")
631+
func changeCWD() async throws {
632+
try #require(0 == chdir("/"))
633+
await #expect(processExitsWith: .success) {}
634+
}
635+
#endif
628636
}
629637

630638
// MARK: - Fixtures

0 commit comments

Comments
 (0)