Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
342a2ef
Add LLDB debugger support to swift-test command
plemarquand Aug 21, 2025
c5043d1
Update argument help string
plemarquand Sep 3, 2025
8aa9e52
Fixup tests on Linux/Windows
plemarquand Sep 3, 2025
93162eb
Refactor validation tests to avoid command invocation
plemarquand Sep 3, 2025
ffce7b1
Mark some tests with known issues
plemarquand Sep 4, 2025
8b1225e
Test command output can be written asynchronously
plemarquand Sep 4, 2025
dc26db6
Revert now unnecessary swift testing argument handling
plemarquand Sep 5, 2025
f525c05
Disable some tests on Windows
plemarquand Sep 9, 2025
9583701
Consolidate BuildConfiguration.buildArgs into one spot
plemarquand Sep 10, 2025
7a6678b
Remove clear alias to be added later to both test and run cmds
plemarquand Sep 12, 2025
b5cc42c
Swift-build can now link Testing properly
plemarquand Sep 18, 2025
5f48a4e
Cleanup unnecessary runLLDBForTesting method
plemarquand Jan 5, 2026
524cadb
Fix compile issues
plemarquand Mar 24, 2026
f7db781
General cleanup
plemarquand Mar 24, 2026
5dc28c2
Use same exec implementation for SwiftRunCommand and DebugTestRunner
plemarquand Mar 24, 2026
fab9ea3
Inject quit to LLDB setup commands when running tests
plemarquand Mar 30, 2026
9d51f72
Remove now unnecessary withKnownIssues from tests for Windows
plemarquand Mar 30, 2026
491693b
Fixup after rebase
plemarquand Apr 16, 2026
41247ac
Support multiple test executables as produced by swift-build
plemarquand Apr 21, 2026
dccd4fe
Cleanup and a few more tests
plemarquand Apr 21, 2026
4c738a3
Add test that runs tests through lldb
plemarquand Apr 21, 2026
f7ce77f
Dont use -l in ci with older lldb
plemarquand Apr 21, 2026
adc19e2
Mark assertions in smoke tests withKnownIssue
plemarquand Apr 21, 2026
e98c4f8
Narrow withKnownIssue on some tests
plemarquand Apr 22, 2026
90f6ab0
Work around multi-test target issue by marking tests as withKnownIssue
plemarquand Apr 22, 2026
715cd48
Cleanup comments
plemarquand Apr 23, 2026
2b0f196
Better error handling in LLDB python script
plemarquand Apr 23, 2026
832130d
Surface safeExec signal/sigprocmask/fcntl failures via ObservabilityS…
plemarquand Apr 23, 2026
71d8c18
Ensure tmp command files have unique names
plemarquand Apr 23, 2026
06d4c57
If building the modulePath fails, throw, don't continue
plemarquand Apr 23, 2026
3ae035e
Cleaner DebuggableTestSession.Target struct
plemarquand Apr 23, 2026
6c9cc88
Faster --debugger argument validation tests
plemarquand Apr 23, 2026
3dfd097
More cleanup
plemarquand Apr 23, 2026
f480823
Add new file to CMakeLists
plemarquand Apr 23, 2026
f60e1d1
Skip smoke tests that fail without python bindings
plemarquand Apr 24, 2026
838bc93
If exec in safeExec fails, exit immediately
plemarquand Apr 24, 2026
761d507
Escape more strings that go into LLDB commands
plemarquand Apr 24, 2026
02056af
Clean up tmp directory on error
plemarquand Apr 24, 2026
f012b50
Add dedicated DebuggerError type
plemarquand Apr 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Fixtures/Miscellaneous/TestDebugging/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// swift-tools-version: 6.0
import PackageDescription

let package = Package(
name: "TestDebugging",
targets: [
.target(name: "TestDebugging"),
.testTarget(name: "TestDebuggingTests", dependencies: ["TestDebugging"]),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
public struct Calculator {
public init() {}

public func add(_ a: Int, _ b: Int) -> Int {
return a + b
}

public func subtract(_ a: Int, _ b: Int) -> Int {
return a - b
}

public func multiply(_ a: Int, _ b: Int) -> Int {
return a * b
}

public func divide(_ a: Int, _ b: Int) -> Int {
return a / b
}

public func purposelyFail() -> Bool {
return false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import XCTest
import Testing
@testable import TestDebugging

// MARK: - XCTest Suite
final class XCTestCalculatorTests: XCTestCase {

func testAdditionPasses() {
let calculator = Calculator()
let result = calculator.add(2, 3)
XCTAssertEqual(result, 5, "Addition should return 5 for 2 + 3")
}

func testSubtractionFails() {
let calculator = Calculator()
let result = calculator.subtract(5, 3)
XCTAssertEqual(result, 3, "This test is designed to fail - subtraction 5 - 3 should equal 2, not 3")
}
}

// MARK: - Swift Testing Suite
@Test("Calculator Addition Works Correctly")
func calculatorAdditionPasses() {
let calculator = Calculator()
let result = calculator.add(4, 6)
#expect(result == 10, "Addition should return 10 for 4 + 6")
}

@Test("Calculator Boolean Check Fails")
func calculatorBooleanFails() {
let calculator = Calculator()
let result = calculator.purposelyFail()
#expect(result == true, "This test is designed to fail - purposelyFail() should return false, not true")
}
12 changes: 12 additions & 0 deletions Fixtures/Miscellaneous/TestDebuggingMultiProduct/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// swift-tools-version: 6.0
import PackageDescription

let package = Package(
name: "TestDebuggingMultiProduct",
targets: [
.target(name: "LibA"),
.target(name: "LibB"),
.testTarget(name: "LibATests", dependencies: ["LibA"]),
.testTarget(name: "LibBTests", dependencies: ["LibB"]),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public struct LibA {
public init() {}

public func greet() -> String {
return "Hello from LibA"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public struct LibB {
public init() {}

public func greet() -> String {
return "Hello from LibB"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import XCTest
import Testing
@testable import LibA

final class LibAXCTests: XCTestCase {
func testGreet() {
let lib = LibA()
XCTAssertEqual(lib.greet(), "Hello from LibA")
}
}

@Test("LibA greeting works")
func libAGreeting() {
let lib = LibA()
#expect(lib.greet() == "Hello from LibA")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import XCTest
import Testing
@testable import LibB

final class LibBXCTests: XCTestCase {
func testGreet() {
let lib = LibB()
XCTAssertEqual(lib.greet(), "Hello from LibB")
}
}

@Test("LibB greeting works")
func libBGreeting() {
let lib = LibB()
#expect(lib.greet() == "Hello from LibB")
}
1 change: 1 addition & 0 deletions Sources/Commands/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ add_library(Commands
Utilities/DOTManifestSerializer.swift
Utilities/MermaidPackageSerializer.swift
Utilities/MultiRootSupport.swift
Utilities/NonEmpty.swift
Utilities/PlainTextEncoder.swift
Utilities/PluginDelegate.swift
Utilities/RefactoringSupport.swift
Expand Down
59 changes: 16 additions & 43 deletions Sources/Commands/SwiftRunCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import PackageModel
import SPMBuildCore

import enum TSCBasic.ProcessEnv
import func TSCBasic.exec

import enum TSCUtility.Diagnostics

Expand Down Expand Up @@ -153,7 +152,8 @@ public struct SwiftRunCommand: AsyncSwiftCommand {
fileSystem: swiftCommandState.fileSystem,
executablePath: interpreterPath,
originalWorkingDirectory: swiftCommandState.originalWorkingDirectory,
arguments: arguments
arguments: arguments,
observabilityScope: swiftCommandState.observabilityScope
)

case .debugger:
Expand Down Expand Up @@ -183,12 +183,17 @@ public struct SwiftRunCommand: AsyncSwiftCommand {
fileSystem: swiftCommandState.fileSystem,
executablePath: debuggerPath,
originalWorkingDirectory: swiftCommandState.originalWorkingDirectory,
arguments: debugger.extraCLIOptions + [productAbsolutePath.pathString] + options.arguments
arguments: debugger.extraCLIOptions + [productAbsolutePath.pathString] + options.arguments,
observabilityScope: swiftCommandState.observabilityScope
)
} else {
let pathRelativeToWorkingDirectory = productAbsolutePath.relative(to: swiftCommandState.originalWorkingDirectory)
let lldbPath = try swiftCommandState.getTargetToolchain().getLLDB()
try exec(path: lldbPath.pathString, args: ["--", pathRelativeToWorkingDirectory.pathString] + options.arguments)
try safeExec(
path: lldbPath.pathString,
args: ["--", pathRelativeToWorkingDirectory.pathString] + options.arguments,
observabilityScope: swiftCommandState.observabilityScope
)
}
} catch let error as RunError {
swiftCommandState.observabilityScope.emit(error)
Expand All @@ -207,7 +212,8 @@ public struct SwiftRunCommand: AsyncSwiftCommand {
fileSystem: swiftCommandState.fileSystem,
executablePath: swiftInterpreterPath,
originalWorkingDirectory: swiftCommandState.originalWorkingDirectory,
arguments: arguments
arguments: arguments,
observabilityScope: swiftCommandState.observabilityScope
)
return
}
Expand Down Expand Up @@ -245,7 +251,8 @@ public struct SwiftRunCommand: AsyncSwiftCommand {
fileSystem: swiftCommandState.fileSystem,
executablePath: runnerPath,
originalWorkingDirectory: swiftCommandState.originalWorkingDirectory,
arguments: arguments
arguments: arguments,
observabilityScope: swiftCommandState.observabilityScope
)
} catch Diagnostics.fatalError {
throw ExitCode.failure
Expand Down Expand Up @@ -294,7 +301,8 @@ public struct SwiftRunCommand: AsyncSwiftCommand {
fileSystem: FileSystem,
executablePath: AbsolutePath,
originalWorkingDirectory: AbsolutePath,
arguments: [String]
arguments: [String],
observabilityScope: ObservabilityScope
) throws {
// Make sure we are running from the original working directory.
let cwd: AbsolutePath? = fileSystem.currentWorkingDirectory
Expand All @@ -303,7 +311,7 @@ public struct SwiftRunCommand: AsyncSwiftCommand {
}

let pathRelativeToWorkingDirectory = executablePath.relative(to: originalWorkingDirectory)
try execute(path: executablePath.pathString, args: [pathRelativeToWorkingDirectory.pathString] + arguments)
try safeExec(path: executablePath.pathString, args: [pathRelativeToWorkingDirectory.pathString] + arguments, observabilityScope: observabilityScope)
}

/// Determines if a path points to a valid swift file.
Expand All @@ -326,41 +334,6 @@ public struct SwiftRunCommand: AsyncSwiftCommand {
return fileSystem.isFile(absolutePath)
}

/// A safe wrapper of TSCBasic.exec.
private func execute(path: String, args: [String]) throws -> Never {
#if !os(Windows)
// Dispatch will disable almost all asynchronous signals on its worker threads, and this is called from `async`
// context. To correctly `exec` a freshly built binary, we will need to:
// 1. reset the signal masks
for i in 1..<NSIG {
signal(i, SIG_DFL)
}
var sig_set_all = sigset_t()
sigfillset(&sig_set_all)
sigprocmask(SIG_UNBLOCK, &sig_set_all, nil)

#if os(FreeBSD) || os(OpenBSD)
#if os(FreeBSD)
pthread_suspend_all_np()
#endif
closefrom(3)
#else
#if os(Android)
let number_fds = Int32(sysconf(_SC_OPEN_MAX))
#else
let number_fds = getdtablesize()
#endif /* os(Android) */

// 2. set to close all file descriptors on exec
for i in 3..<number_fds {
_ = fcntl(i, F_SETFD, FD_CLOEXEC)
}
#endif /* os(FreeBSD) || os(OpenBSD) */
#endif

try TSCBasic.exec(path: path, args: args)
}

public init() {}
}

Expand Down
Loading
Loading