diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2033d8..b95959d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,10 +135,10 @@ jobs: # - name: Run tests (debug only) # run: swift test - android: - name: Android - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: "Test Swift Package on Android" - uses: skiptools/swift-android-action@v2 + # android: + # name: Android + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v4 + # - name: "Test Swift Package on Android" + # uses: skiptools/swift-android-action@v2 diff --git a/Examples/Examples.xcodeproj/project.pbxproj b/Examples/Examples.xcodeproj/project.pbxproj index 841dcda..b5d5d63 100644 --- a/Examples/Examples.xcodeproj/project.pbxproj +++ b/Examples/Examples.xcodeproj/project.pbxproj @@ -7,13 +7,14 @@ objects = { /* Begin PBXBuildFile section */ + CA1972D72DB1836900351823 /* TraitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA1972D62DB1836900351823 /* TraitTests.swift */; }; + CA1972D92DB1847200351823 /* ExampleTrait.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA1972D82DB1847200351823 /* ExampleTrait.swift */; }; CADE79AE2C4A9D2C005A85F7 /* ExamplesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CADE79AD2C4A9D2C005A85F7 /* ExamplesApp.swift */; }; CADE79B22C4A9D2D005A85F7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CADE79B12C4A9D2D005A85F7 /* Assets.xcassets */; }; CADE79DA2C4A9D3A005A85F7 /* IssueReporting in Frameworks */ = {isa = PBXBuildFile; productRef = CADE79D92C4A9D3A005A85F7 /* IssueReporting */; }; CADE79DD2C4A9E59005A85F7 /* SwiftTestingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CADE79DB2C4A9E59005A85F7 /* SwiftTestingTests.swift */; }; CADE79DE2C4A9E59005A85F7 /* XCTestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CADE79DC2C4A9E59005A85F7 /* XCTestTests.swift */; }; CADE79E02C4ABD94005A85F7 /* Examples.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = CADE79DF2C4ABD94005A85F7 /* Examples.xctestplan */; }; - CADE79E22C4ABE90005A85F7 /* IssueReporting in Frameworks */ = {isa = PBXBuildFile; productRef = CADE79E12C4ABE90005A85F7 /* IssueReporting */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -26,7 +27,32 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + CA1972DD2DB1854600351823 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + CA1972E12DB186BC00351823 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ + CA1972D62DB1836900351823 /* TraitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TraitTests.swift; sourceTree = ""; }; + CA1972D82DB1847200351823 /* ExampleTrait.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleTrait.swift; sourceTree = ""; }; CADE79AA2C4A9D2C005A85F7 /* Examples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Examples.app; sourceTree = BUILT_PRODUCTS_DIR; }; CADE79AD2C4A9D2C005A85F7 /* ExamplesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesApp.swift; sourceTree = ""; }; CADE79B12C4A9D2D005A85F7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -50,7 +76,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CADE79E22C4ABE90005A85F7 /* IssueReporting in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -80,6 +105,7 @@ isa = PBXGroup; children = ( CADE79AD2C4A9D2C005A85F7 /* ExamplesApp.swift */, + CA1972D82DB1847200351823 /* ExampleTrait.swift */, CADE79B12C4A9D2D005A85F7 /* Assets.xcassets */, CADE79B32C4A9D2D005A85F7 /* Examples.entitlements */, ); @@ -92,6 +118,7 @@ CADE79DF2C4ABD94005A85F7 /* Examples.xctestplan */, CADE79DB2C4A9E59005A85F7 /* SwiftTestingTests.swift */, CADE79DC2C4A9E59005A85F7 /* XCTestTests.swift */, + CA1972D62DB1836900351823 /* TraitTests.swift */, ); path = ExamplesTests; sourceTree = ""; @@ -113,6 +140,7 @@ CADE79A62C4A9D2C005A85F7 /* Sources */, CADE79A72C4A9D2C005A85F7 /* Frameworks */, CADE79A82C4A9D2C005A85F7 /* Resources */, + CA1972E12DB186BC00351823 /* Embed Frameworks */, ); buildRules = ( ); @@ -133,6 +161,7 @@ CADE79B72C4A9D2D005A85F7 /* Sources */, CADE79B82C4A9D2D005A85F7 /* Frameworks */, CADE79B92C4A9D2D005A85F7 /* Resources */, + CA1972DD2DB1854600351823 /* Embed Frameworks */, ); buildRules = ( ); @@ -141,7 +170,6 @@ ); name = ExamplesTests; packageProductDependencies = ( - CADE79E12C4ABE90005A85F7 /* IssueReporting */, ); productName = ExamplesTests; productReference = CADE79BB2C4A9D2D005A85F7 /* ExamplesTests.xctest */; @@ -210,6 +238,7 @@ buildActionMask = 2147483647; files = ( CADE79AE2C4A9D2C005A85F7 /* ExamplesApp.swift in Sources */, + CA1972D92DB1847200351823 /* ExampleTrait.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -219,6 +248,7 @@ files = ( CADE79DD2C4A9E59005A85F7 /* SwiftTestingTests.swift in Sources */, CADE79DE2C4A9E59005A85F7 /* XCTestTests.swift in Sources */, + CA1972D72DB1836900351823 /* TraitTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -504,10 +534,6 @@ isa = XCSwiftPackageProductDependency; productName = IssueReporting; }; - CADE79E12C4ABE90005A85F7 /* IssueReporting */ = { - isa = XCSwiftPackageProductDependency; - productName = IssueReporting; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = CADE79A22C4A9D2C005A85F7 /* Project object */; diff --git a/Examples/Examples/ExampleTrait.swift b/Examples/Examples/ExampleTrait.swift new file mode 100644 index 0000000..acc3b8b --- /dev/null +++ b/Examples/Examples/ExampleTrait.swift @@ -0,0 +1,14 @@ +import IssueReporting + +public struct ExampleTrait: Sendable { + public init() {} + + public static func hasTrait() -> Bool { + switch TestContext.current { + case .none, .some(.xcTest): + return false + case .some(.swiftTesting(let testing)): + return testing?.test.traits.contains { $0 is Self } ?? false + } + } +} diff --git a/Examples/ExamplesTests/TraitTests.swift b/Examples/ExamplesTests/TraitTests.swift new file mode 100644 index 0000000..3e13efe --- /dev/null +++ b/Examples/ExamplesTests/TraitTests.swift @@ -0,0 +1,21 @@ +import IssueReporting +import Testing +import Examples + +extension ExampleTrait: @retroactive SuiteTrait, @retroactive TestTrait { +} + +@Suite +struct TraitTests { + @Test(ExampleTrait()) func hasTrait() { + #if DEBUG + #expect(ExampleTrait.hasTrait()) + #else + #expect(ExampleTrait.hasTrait() == false) + #endif + } + + @Test func doesNotHaveTrait() { + #expect(ExampleTrait.hasTrait() == false) + } +} diff --git a/Package.swift b/Package.swift index f3a8668..1c50b17 100644 --- a/Package.swift +++ b/Package.swift @@ -22,7 +22,13 @@ let package = Package( ], targets: [ .target( - name: "IssueReporting" + name: "IssueReportingPackageSupport" + ), + .target( + name: "IssueReporting", + dependencies: [ + "IssueReportingPackageSupport" + ] ), .testTarget( name: "IssueReportingTests", @@ -32,7 +38,10 @@ let package = Package( ] ), .target( - name: "IssueReportingTestSupport" + name: "IssueReportingTestSupport", + dependencies: [ + "IssueReportingPackageSupport" + ] ), .target( name: "XCTestDynamicOverlay", diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index 22f30ee..308b8c2 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -21,7 +21,13 @@ let package = Package( ], targets: [ .target( - name: "IssueReporting" + name: "IssueReportingPackageSupport" + ), + .target( + name: "IssueReporting", + dependencies: [ + "IssueReportingPackageSupport", + ] ), .testTarget( name: "IssueReportingTests", @@ -31,7 +37,10 @@ let package = Package( ] ), .target( - name: "IssueReportingTestSupport" + name: "IssueReportingTestSupport", + dependencies: [ + "IssueReportingPackageSupport", + ] ), .target( name: "XCTestDynamicOverlay", diff --git a/Sources/IssueReporting/Internal/SwiftTesting.swift b/Sources/IssueReporting/Internal/SwiftTesting.swift index d31860c..ac8aa47 100644 --- a/Sources/IssueReporting/Internal/SwiftTesting.swift +++ b/Sources/IssueReporting/Internal/SwiftTesting.swift @@ -1,4 +1,5 @@ import Foundation +import IssueReportingPackageSupport #if canImport(WinSDK) import WinSDK @@ -313,17 +314,21 @@ func _withKnownIssue( #endif @usableFromInline -func _currentTestID() -> AnyHashable? { - guard let function = function(for: "$s25IssueReportingTestSupport08_currentC2IDypyF") +func _currentTest() -> _Test? { + guard let function = function(for: "$s25IssueReportingTestSupport08_currentC0ypyF") else { #if DEBUG - return Test.current?.id + return Test.current.map { _Test(id: $0.id, traits: $0.traits) } #else return nil #endif } - return (function as! @Sendable () -> AnyHashable?)() + return withUnsafePointer(to: function) { + $0.withMemoryRebound(to: (@Sendable () -> _Test?).self, capacity: 1) { + $0.pointee() + } + } } #if DEBUG @@ -446,7 +451,7 @@ func _currentTestID() -> AnyHashable? { struct Case {} private var name: String private var displayName: String? - private var traits: [any Trait] + fileprivate var traits: [any Trait] private var sourceLocation: SourceLocation private var containingTypeInfo: TypeInfo? private var xcTestCompatibleSelector: __XCTestCompatibleSelector? diff --git a/Sources/IssueReporting/TestContext.swift b/Sources/IssueReporting/TestContext.swift index 9d8c612..e6affc9 100644 --- a/Sources/IssueReporting/TestContext.swift +++ b/Sources/IssueReporting/TestContext.swift @@ -23,8 +23,8 @@ public enum TestContext: Equatable, Sendable { /// If executed outside of a test process, this will return `nil`. public static var current: Self? { guard isTesting else { return nil } - if let currentTestID = _currentTestID() { - return .swiftTesting(Testing(id: currentTestID)) + if let currentTest = _currentTest() { + return .swiftTesting(Testing(id: currentTest.id, traits: currentTest.traits)) } else { return .xcTest } @@ -42,10 +42,19 @@ public enum TestContext: Equatable, Sendable { public struct Test: Equatable, Hashable, Identifiable, Sendable { public let id: ID + public let traits: [any Sendable] public struct ID: Equatable, Hashable, @unchecked Sendable { public let rawValue: AnyHashable } + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.id == rhs.id + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + } } } @@ -73,7 +82,7 @@ public enum TestContext: Equatable, Sendable { } extension TestContext.Testing { - fileprivate init(id: AnyHashable) { - self.init(test: Test(id: Test.ID(rawValue: id))) + fileprivate init(id: AnyHashable, traits: [any Sendable]) { + self.init(test: Test(id: Test.ID(rawValue: id), traits: traits)) } } diff --git a/Sources/IssueReportingPackageSupport/_Test.swift b/Sources/IssueReportingPackageSupport/_Test.swift new file mode 100644 index 0000000..81819f5 --- /dev/null +++ b/Sources/IssueReportingPackageSupport/_Test.swift @@ -0,0 +1,14 @@ +@usableFromInline +package struct _Test { + @usableFromInline + package let id: AnyHashable + + @usableFromInline + package let traits: [any Sendable] + + @usableFromInline + package init(id: AnyHashable, traits: [any Sendable]) { + self.id = id + self.traits = traits + } +} diff --git a/Sources/IssueReportingTestSupport/SwiftTesting.swift b/Sources/IssueReportingTestSupport/SwiftTesting.swift index 030631b..0107ed4 100644 --- a/Sources/IssueReportingTestSupport/SwiftTesting.swift +++ b/Sources/IssueReportingTestSupport/SwiftTesting.swift @@ -1,3 +1,5 @@ +import IssueReportingPackageSupport + #if canImport(Testing) import Testing #endif @@ -134,11 +136,11 @@ private func __withKnownIssueAsync( } #endif -public func _currentTestID() -> Any { __currentTestID } +public func _currentTest() -> Any { __currentTest } @Sendable -private func __currentTestID() -> AnyHashable? { +private func __currentTest() -> _Test? { #if canImport(Testing) - return Test.current?.id + return Test.current.map { _Test(id: $0.id, traits: $0.traits) } #else return nil #endif