diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index afcbba43..6fbff14e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: name: MacOS runs-on: macOS-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run tests run: make test @@ -25,7 +25,7 @@ jobs: name: Ubuntu runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run tests run: make test-linux @@ -57,7 +57,7 @@ jobs: with: branch: swift-5.8.1-release tag: 5.8.1-RELEASE - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build run: swift build -c ${{ matrix.config }} - name: Run tests (debug only) @@ -67,3 +67,19 @@ jobs: # that is generally available. if: ${{ matrix.config == 'debug' }} run: swift test + + static-stdlib: + name: Static standard library + strategy: + matrix: + os: [ubuntu-20.04] + runs-on: ${{ matrix.os }} + steps: + - uses: swift-actions/setup-swift@v1 + with: + swift-version: '5.8.0' + - name: Install dependencies + run: sudo apt-get install -y libcurl4-openssl-dev + - uses: actions/checkout@v4 + - name: Build for static-stdlib + run: make build-for-static-stdlib diff --git a/Makefile b/Makefile index c8166e6e..397a32ae 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,8 @@ test-debug: test: test-debug @swift test -c release +test-linux: test-debug + test-linux: test test-linux-docker: @@ -36,6 +38,10 @@ test-linux-static-stdlib: swift:5.6.2-focal \ bash -c "swift build -c release -Xswiftc -static-stdlib" +build-for-static-stdlib: + @swift build -c debug --static-swift-stdlib + @swift build -c release --static-swift-stdlib + format: @swift format \ --ignore-unparsable-files \ diff --git a/README.md b/README.md index 31cedad6..59815c9f 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,6 @@ import XCTestDynamicOverlay // ✅ …and your application or library will continue to compile just fine. -> ⚠️ Important: The dynamically loaded `XCTFail` is only available in `DEBUG` builds in order -to prevent App Store rejections due to runtime loading of symbols. - ## Example diff --git a/Sources/XCTestDynamicOverlay/Internal/XCTCurrentTestCase.swift b/Sources/XCTestDynamicOverlay/Internal/XCTCurrentTestCase.swift index b884f06c..ab725a77 100644 --- a/Sources/XCTestDynamicOverlay/Internal/XCTCurrentTestCase.swift +++ b/Sources/XCTestDynamicOverlay/Internal/XCTCurrentTestCase.swift @@ -1,28 +1,22 @@ -#if DEBUG - #if canImport(ObjectiveC) - import Foundation +#if canImport(ObjectiveC) + import Foundation - @_spi(CurrentTestCase) public var XCTCurrentTestCase: AnyObject? { - guard - let XCTestObservationCenter = NSClassFromString("XCTestObservationCenter"), - let XCTestObservationCenter = XCTestObservationCenter as Any as? NSObjectProtocol, - let shared = XCTestObservationCenter.perform(Selector(("sharedTestObservationCenter")))? - .takeUnretainedValue(), - let observers = shared.perform(Selector(("observers")))? - .takeUnretainedValue() as? [AnyObject], - let observer = - observers - .first(where: { NSStringFromClass(type(of: $0)) == "XCTestMisuseObserver" }), - let currentTestCase = observer.perform(Selector(("currentTestCase")))? - .takeUnretainedValue() - else { return nil } - return currentTestCase - } - #else - @_spi(CurrentTestCase) public var XCTCurrentTestCase: AnyObject? { - nil - } - #endif + @_spi(CurrentTestCase) public var XCTCurrentTestCase: AnyObject? { + guard + let XCTestObservationCenter = NSClassFromString("XCTestObservationCenter"), + let XCTestObservationCenter = XCTestObservationCenter as Any as? NSObjectProtocol, + let shared = XCTestObservationCenter.perform(Selector(("sharedTestObservationCenter")))? + .takeUnretainedValue(), + let observers = shared.perform(Selector(("observers")))? + .takeUnretainedValue() as? [AnyObject], + let observer = + observers + .first(where: { NSStringFromClass(type(of: $0)) == "XCTestMisuseObserver" }), + let currentTestCase = observer.perform(Selector(("currentTestCase")))? + .takeUnretainedValue() + else { return nil } + return currentTestCase + } #else @_spi(CurrentTestCase) public var XCTCurrentTestCase: AnyObject? { nil diff --git a/Sources/XCTestDynamicOverlay/XCTExpectFailure.swift b/Sources/XCTestDynamicOverlay/XCTExpectFailure.swift index ad32816e..8dd8cc87 100644 --- a/Sources/XCTestDynamicOverlay/XCTExpectFailure.swift +++ b/Sources/XCTestDynamicOverlay/XCTExpectFailure.swift @@ -1,6 +1,6 @@ import Foundation -#if DEBUG && canImport(ObjectiveC) +#if canImport(ObjectiveC) /// Instructs the test to expect a failure in an upcoming assertion, with options to customize /// expected failure checking and handling. /// diff --git a/Sources/XCTestDynamicOverlay/XCTFail.swift b/Sources/XCTestDynamicOverlay/XCTFail.swift index fd60b09a..e771b265 100644 --- a/Sources/XCTestDynamicOverlay/XCTFail.swift +++ b/Sources/XCTestDynamicOverlay/XCTFail.swift @@ -12,255 +12,204 @@ public struct XCTFailContext: Sendable { } } -#if DEBUG - #if canImport(ObjectiveC) - /// This function generates a failure immediately and unconditionally. - /// - /// Dynamically creates and records an `XCTIssue` under the hood that captures the source code - /// context of the caller. Useful for defining assertion helpers that fail in indirect code - /// paths, where the `file` and `line` of the failure have not been realized. - /// - /// - Parameter message: An optional description of the assertion, for inclusion in test - /// results. - @_disfavoredOverload - public func XCTFail(_ message: String = "") { - if let context = XCTFailContext.current { - XCTFail(message, file: context.file, line: context.line) - return - } - var message = message - attachHostApplicationWarningIfNeeded(&message) - guard - let currentTestCase = XCTCurrentTestCase, - let issue = (NSClassFromString("XCTIssue") as Any as? NSObjectProtocol)? - .perform(NSSelectorFromString("alloc"))?.takeUnretainedValue() - .perform( - Selector(("initWithType:compactDescription:")), - with: 0, - with: message.isEmpty ? "failed" : message - )? - .takeUnretainedValue() - else { - if !_XCTIsTesting { - runtimeWarn(message) - } - return - } - if let testFrame = Thread.callStackSymbols.enumerated().first(where: { isTestFrame($1) }), - let sourceCodeContext = - (NSClassFromString("XCTSourceCodeContext") as Any as? NSObjectProtocol)? - .perform(NSSelectorFromString("alloc"))?.takeUnretainedValue() - .perform( - Selector(("initWithCallStackAddresses:location:")), - with: Array(Thread.callStackReturnAddresses[testFrame.offset...]), - with: nil - )? - .takeUnretainedValue() - { - _ = issue.perform(Selector(("setSourceCodeContext:")), with: sourceCodeContext) +#if canImport(ObjectiveC) + /// This function generates a failure immediately and unconditionally. + /// + /// Dynamically creates and records an `XCTIssue` under the hood that captures the source code + /// context of the caller. Useful for defining assertion helpers that fail in indirect code paths, + /// where the `file` and `line` of the failure have not been realized. + /// + /// - Parameter message: An optional description of the assertion, for inclusion in test results. + @_disfavoredOverload + public func XCTFail(_ message: String = "") { + if let context = XCTFailContext.current { + XCTFail(message, file: context.file, line: context.line) + return + } + var message = message + attachHostApplicationWarningIfNeeded(&message) + guard + let currentTestCase = XCTCurrentTestCase, + let issue = (NSClassFromString("XCTIssue") as Any as? NSObjectProtocol)? + .perform(NSSelectorFromString("alloc"))?.takeUnretainedValue() + .perform( + Selector(("initWithType:compactDescription:")), + with: 0, + with: message.isEmpty ? "failed" : message + )? + .takeUnretainedValue() + else { + if !_XCTIsTesting { + runtimeWarn(message) } - _ = currentTestCase.perform(Selector(("recordIssue:")), with: issue) + return } - - /// This function generates a failure immediately and unconditionally. - /// - /// Dynamically calls `XCTFail` with the given file and line. Useful for defining assertion - /// helpers that have the source code context at hand and want to highlight the direct caller - /// of the helper. - /// - /// - Parameter message: An optional description of the assertion, for inclusion in test - /// results. - @_disfavoredOverload - public func XCTFail(_ message: String = "", file: StaticString, line: UInt) { - var message = message - attachHostApplicationWarningIfNeeded(&message) - _XCTFailureHandler(nil, true, "\(file)", line, "\(message.isEmpty ? "failed" : message)", nil) + if let testFrame = Thread.callStackSymbols.enumerated().first(where: { isTestFrame($1) }), + let sourceCodeContext = + (NSClassFromString("XCTSourceCodeContext") as Any as? NSObjectProtocol)? + .perform(NSSelectorFromString("alloc"))?.takeUnretainedValue() + .perform( + Selector(("initWithCallStackAddresses:location:")), + with: Array(Thread.callStackReturnAddresses[testFrame.offset...]), + with: nil + )? + .takeUnretainedValue() + { + _ = issue.perform(Selector(("setSourceCodeContext:")), with: sourceCodeContext) } + _ = currentTestCase.perform(Selector(("recordIssue:")), with: issue) + } - private typealias XCTFailureHandler = @convention(c) ( - AnyObject?, Bool, UnsafePointer, UInt, String, String? - ) -> Void - private let _XCTFailureHandler = unsafeBitCast( - dlsym(dlopen(nil, RTLD_LAZY), "_XCTFailureHandler"), - to: XCTFailureHandler.self - ) - - private func attachHostApplicationWarningIfNeeded(_ message: inout String) { - guard - _XCTIsTesting, - Bundle.main.bundleIdentifier != "com.apple.dt.xctest.tool" - else { return } + /// This function generates a failure immediately and unconditionally. + /// + /// Dynamically calls `XCTFail` with the given file and line. Useful for defining assertion + /// helpers that have the source code context at hand and want to highlight the direct caller of + /// the helper. + /// + /// - Parameter message: An optional description of the assertion, for inclusion in test results. + @_disfavoredOverload + public func XCTFail(_ message: String = "", file: StaticString, line: UInt) { + var message = message + attachHostApplicationWarningIfNeeded(&message) + _XCTFailureHandler(nil, true, "\(file)", line, "\(message.isEmpty ? "failed" : message)", nil) + } - let callStack = Thread.callStackSymbols + private typealias XCTFailureHandler = @convention(c) ( + AnyObject?, Bool, UnsafePointer, UInt, String, String? + ) -> Void + private let _XCTFailureHandler = unsafeBitCast( + dlsym(dlopen(nil, RTLD_LAZY), "_XCTFailureHandler"), + to: XCTFailureHandler.self + ) - // Detect when synchronous test exists in stack. - guard callStack.allSatisfy({ frame in !frame.contains(" XCTestCore ") }) - else { return } + private func attachHostApplicationWarningIfNeeded(_ message: inout String) { + guard + _XCTIsTesting, + Bundle.main.bundleIdentifier != "com.apple.dt.xctest.tool" + else { return } - // Detect when asynchronous test exists in stack. - guard callStack.allSatisfy({ frame in !isTestFrame(frame) }) - else { return } + let callStack = Thread.callStackSymbols - if !message.contains(where: \.isNewline) { - message.append(" …") - } + // Detect when synchronous test exists in stack. + guard callStack.allSatisfy({ frame in !frame.contains(" XCTestCore ") }) + else { return } - message.append( - """ + // Detect when asynchronous test exists in stack. + guard callStack.allSatisfy({ frame in !isTestFrame(frame) }) + else { return } + if !message.contains(where: \.isNewline) { + message.append(" …") + } - ━━┉┅ - Note: This failure was emitted from tests running in a host application\ - \(Bundle.main.bundleIdentifier.map { " (\($0))" } ?? ""). + message.append( + """ - This can lead to false positives, where failures could have emitted from live application \ - code at launch time, and not from the current test. - For more information (and workarounds), see "Testing gotchas": + ━━┉┅ + Note: This failure was emitted from tests running in a host application\ + \(Bundle.main.bundleIdentifier.map { " (\($0))" } ?? ""). - https://pointfreeco.github.io/swift-dependencies/main/documentation/dependencies/testing#Testing-gotchas - """ - ) - } + This can lead to false positives, where failures could have emitted from live application \ + code at launch time, and not from the current test. - func isTestFrame(_ frame: String) -> Bool { - // Regular expression to detect and demangle an XCTest case frame: - // - // 1. `(?<=\$s)`: Starts with "$s" (stable mangling) - // 2. `\d{1,3}`: Some numbers (the class name length or the module name length) - // 3. `.*`: The class name, or module name + class name length + class name - // 4. `C`: The class type identifier - // 5. `(?=\d{1,3}test.*yy(Ya)?K?F)`: The function name length, a function that starts with - // `test`, has no arguments (`y`), returns Void (`y`), and is a function (`F`), potentially - // async (`Ya`), throwing (`K`), or both. - let mangledTestFrame = #"(?<=\$s)\d{1,3}.*C(?=\d{1,3}test.*yy(Ya)?K?F)"# + For more information (and workarounds), see "Testing gotchas": - guard let XCTestCase = NSClassFromString("XCTestCase") - else { return false } + https://pointfreeco.github.io/swift-dependencies/main/documentation/dependencies/testing#Testing-gotchas + """ + ) + } - return frame.range(of: mangledTestFrame, options: .regularExpression) - .map { - (_typeByName(String(frame[$0])) as? NSObject.Type)?.isSubclass(of: XCTestCase) ?? false - } - ?? false - } - #else - private typealias XCTFailType = (_: String, _ file: StaticString, _ line: UInt) -> Void - private func unsafeCastToXCTFailType(_ pXCTFail: UnsafeRawPointer) -> XCTFailType { - // The function itself is a Swift function and must be marked as - // `__attribute__((__swiftcall__))`. However, translating the Swift - // signature `(_:file:line:) -> ()` to C is slightly tricky as we cannot - // guarantee the formal parameter set matches the actual ABI of the - // function. Work around this by exploiting some undefined behaviour. Take - // a pointer to the raw pointer, cast the pointee to the appropriate Swift - // signature, and then return the pointee. Given that the pointer itself - // is to a `.text` location which should not be unmapped, we should be - // able to deal with the escaping pointer remaining valid for the lifetime - // of the application. Unloading dynamically linked libraries is fraught - // with peril, and is generally unsupported. - withUnsafePointer(to: pXCTFail) { - UnsafeRawPointer($0).assumingMemoryBound(to: (@convention(thin) (_: String, _: StaticString, _: UInt) -> Void).self).pointee + func isTestFrame(_ frame: String) -> Bool { + // Regular expression to detect and demangle an XCTest case frame: + // + // 1. `(?<=\$s)`: Starts with "$s" (stable mangling) + // 2. `\d{1,3}`: Some numbers (the class name length or the module name length) + // 3. `.*`: The class name, or module name + class name length + class name + // 4. `C`: The class type identifier + // 5. `(?=\d{1,3}test.*yy(Ya)?K?F)`: The function name length, a function that starts with + // `test`, has no arguments (`y`), returns Void (`y`), and is a function (`F`), potentially + // async (`Ya`), throwing (`K`), or both. + let mangledTestFrame = #"(?<=\$s)\d{1,3}.*C(?=\d{1,3}test.*yy(Ya)?K?F)"# + + guard let XCTestCase = NSClassFromString("XCTestCase") + else { return false } + + return frame.range(of: mangledTestFrame, options: .regularExpression) + .map { + (_typeByName(String(frame[$0])) as? NSObject.Type)?.isSubclass(of: XCTestCase) ?? false } + ?? false + } +#else + private typealias XCTFailType = (_: String, _ file: StaticString, _ line: UInt) -> Void + private func unsafeCastToXCTFailType(_ pXCTFail: UnsafeRawPointer) -> XCTFailType { + // NB: The function itself is a Swift function and must be marked as + // `__attribute__((__swiftcall__))`. However, translating the Swift signature + // `(_:file:line:) -> ()` to C is slightly tricky as we cannot guarantee the formal parameter + // set matches the actual ABI of the function. Work around this by exploiting some undefined + // behavior. Take a pointer to the raw pointer, cast the pointee to the appropriate Swift + // signature, and then return the pointee. Given that the pointer itself is to a `.text` + // location which should not be unmapped, we should be able to deal with the escaping pointer + // remaining valid for the lifetime of the application. Unloading dynamically linked libraries + // is fraught with peril, and is generally unsupported. + withUnsafePointer(to: pXCTFail) { + UnsafeRawPointer($0).assumingMemoryBound( + to: (@convention(thin) ( + _: String, + _: StaticString, + _: UInt + ) -> Void).self + ).pointee } + } - #if os(Windows) - import WinSDK - - private func ResolveXCTFail() -> XCTFailType? { - let hXCTest = LoadLibraryA("XCTest.dll") - guard let hXCTest else { return nil } + #if os(Windows) + import WinSDK - if let pXCTFail = GetProcAddress(hXCTest, "$s6XCTest7XCTFail_4file4lineySS_s12StaticStringVSutF") { - return unsafeCastToXCTFailType(unsafeBitCast(pXCTFail, to: UnsafeRawPointer.self)) - } + private func ResolveXCTFail() -> XCTFailType? { + let hXCTest = LoadLibraryA("XCTest.dll") + guard let hXCTest else { return nil } - return nil + if let pXCTFail = GetProcAddress( + hXCTest, + "$s6XCTest7XCTFail_4file4lineySS_s12StaticStringVSutF" + ) { + return unsafeCastToXCTFailType(unsafeBitCast(pXCTFail, to: UnsafeRawPointer.self)) } - #else - import Glibc - private func ResolveXCTFail() -> XCTFailType? { - var hXCTest = dlopen("libXCTest.so", RTLD_NOW) - if hXCTest == nil { hXCTest = dlopen(nil, RTLD_NOW) } + return nil + } + #else + import Glibc - if let pXCTFail = dlsym(hXCTest, "$s6XCTest7XCTFail_4file4lineySS_s12StaticStringVSutF") { - return unsafeCastToXCTFailType(pXCTFail) - } + private func ResolveXCTFail() -> XCTFailType? { + var hXCTest = dlopen("libXCTest.so", RTLD_NOW) + if hXCTest == nil { hXCTest = dlopen(nil, RTLD_NOW) } - return nil + if let pXCTFail = dlsym(hXCTest, "$s6XCTest7XCTFail_4file4lineySS_s12StaticStringVSutF") { + return unsafeCastToXCTFailType(pXCTFail) } - #endif - - enum DynamicallyResolved { - static let XCTFail = { - if let XCTFail = ResolveXCTFail() { - return { (message: String, file: StaticString, line: UInt) in - XCTFail(message, file, line) - } - } - return { (message: String, _ file: StaticString, _ line: UInt) in - print(noop(message: message)) - } - }() - } - @_disfavoredOverload - public func XCTFail(_ message: String = "", file: StaticString = #file, line: UInt = #line) { - DynamicallyResolved.XCTFail(message, file, line) + return nil } #endif -#else - /// This function generates a failure immediately and unconditionally. - /// - /// Dynamically creates and records an `XCTIssue` under the hood that captures the source code - /// context of the caller. Useful for defining assertion helpers that fail in indirect code - /// paths, where the `file` and `line` of the failure have not been realized. - /// - /// - Parameter message: An optional description of the assertion, for inclusion in test - /// results. - @_disfavoredOverload - public func XCTFail(_ message: String = "") { - print(noop(message: message)) + + enum DynamicallyResolved { + static let XCTFail = { + if let XCTFail = ResolveXCTFail() { + return { (message: String, file: StaticString, line: UInt) in + XCTFail(message, file, line) + } + } + return { (message: String, _ file: StaticString, _ line: UInt) in + print("XCTFail: \(message)") + } + }() } - /// This function generates a failure immediately and unconditionally. - /// - /// Dynamically creates and records an `XCTIssue` under the hood that captures the source code - /// context of the caller. Useful for defining assertion helpers that fail in indirect code - /// paths, where the `file` and `line` of the failure have not been realized. - /// - /// - Parameter message: An optional description of the assertion, for inclusion in test - /// results. @_disfavoredOverload - public func XCTFail(_ message: String = "", file: StaticString, line: UInt) { - print(noop(message: message, file: file, line: line)) + public func XCTFail(_ message: String = "", file: StaticString = #file, line: UInt = #line) { + DynamicallyResolved.XCTFail(message, file, line) } #endif - -private func noop(message: String, file: StaticString? = nil, line: UInt? = nil) -> String { - let fileAndLine: String - if let file = file, let line = line { - fileAndLine = """ - : - ┃ - ┃ \(file):\(line) - ┃ - ┃ … - """ - } else { - fileAndLine = "\n┃ " - } - - return """ - XCTFail: \(message) - - ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┉┅ - ┃ ⚠︎ Warning: This XCTFail was ignored - ┃ - ┃ XCTFail was invoked in a non-DEBUG environment\(fileAndLine)and so was ignored. Be sure to run tests with - ┃ the DEBUG=1 flag set in order to dynamically - ┃ load XCTFail. - ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┉┅ - ▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄ - """ -} diff --git a/Tests/XCTestDynamicOverlayTests/GeneratePlaceholderTests.swift b/Tests/XCTestDynamicOverlayTests/GeneratePlaceholderTests.swift index 819bf8d4..c42f373a 100644 --- a/Tests/XCTestDynamicOverlayTests/GeneratePlaceholderTests.swift +++ b/Tests/XCTestDynamicOverlayTests/GeneratePlaceholderTests.swift @@ -1,4 +1,4 @@ -#if DEBUG && !os(Linux) && !os(Windows) +#if !os(Linux) && !os(Windows) import Foundation import XCTest import XCTestDynamicOverlay diff --git a/Tests/XCTestDynamicOverlayTests/HostAppDetectionTests.swift b/Tests/XCTestDynamicOverlayTests/HostAppDetectionTests.swift index eb2de8d7..01e7fc1a 100644 --- a/Tests/XCTestDynamicOverlayTests/HostAppDetectionTests.swift +++ b/Tests/XCTestDynamicOverlayTests/HostAppDetectionTests.swift @@ -1,4 +1,4 @@ -#if DEBUG && canImport(ObjectiveC) +#if canImport(ObjectiveC) import XCTest @testable import XCTestDynamicOverlay diff --git a/Tests/XCTestDynamicOverlayTests/TestHelpers.swift b/Tests/XCTestDynamicOverlayTests/TestHelpers.swift index 7ca2c0b9..2d6df0d1 100644 --- a/Tests/XCTestDynamicOverlayTests/TestHelpers.swift +++ b/Tests/XCTestDynamicOverlayTests/TestHelpers.swift @@ -5,7 +5,7 @@ func MyXCTFail(_ message: String) { XCTFail(message) } -#if DEBUG && canImport(ObjectiveC) +#if canImport(ObjectiveC) func MyXCTExpectFailure( _ failureReason: String, enabled: Bool = true, diff --git a/Tests/XCTestDynamicOverlayTests/UnimplementedTests.swift b/Tests/XCTestDynamicOverlayTests/UnimplementedTests.swift index 4a16cbfa..546dfcfc 100644 --- a/Tests/XCTestDynamicOverlayTests/UnimplementedTests.swift +++ b/Tests/XCTestDynamicOverlayTests/UnimplementedTests.swift @@ -1,4 +1,4 @@ -#if DEBUG && !os(Linux) && !os(Windows) +#if !os(Linux) && !os(Windows) import XCTest final class UnimplementedTests: XCTestCase { @@ -10,7 +10,7 @@ Unimplemented: f00 … Defined at: - XCTestDynamicOverlayTests/TestHelpers.swift:70 + XCTestDynamicOverlayTests/TestHelpers.swift:98 """ } @@ -21,7 +21,7 @@ Unimplemented: f01 … Defined at: - XCTestDynamicOverlayTests/TestHelpers.swift:71 + XCTestDynamicOverlayTests/TestHelpers.swift:99 Invoked with: "" @@ -35,7 +35,7 @@ Unimplemented: f02 … Defined at: - XCTestDynamicOverlayTests/TestHelpers.swift:72 + XCTestDynamicOverlayTests/TestHelpers.swift:100 Invoked with: ("", 42) @@ -49,7 +49,7 @@ Unimplemented: f03 … Defined at: - XCTestDynamicOverlayTests/TestHelpers.swift:73 + XCTestDynamicOverlayTests/TestHelpers.swift:101 Invoked with: ("", 42, 1.2) @@ -63,7 +63,7 @@ Unimplemented: f04 … Defined at: - XCTestDynamicOverlayTests/TestHelpers.swift:74 + XCTestDynamicOverlayTests/TestHelpers.swift:102 Invoked with: ("", 42, 1.2, [1, 2]) @@ -79,7 +79,7 @@ Unimplemented: f05 … Defined at: - XCTestDynamicOverlayTests/TestHelpers.swift:75 + XCTestDynamicOverlayTests/TestHelpers.swift:103 Invoked with: ("", 42, 1.2, [1, 2], XCTestDynamicOverlayTests.User(id: DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEF)) diff --git a/Tests/XCTestDynamicOverlayTests/XCTContextTests.swift b/Tests/XCTestDynamicOverlayTests/XCTContextTests.swift index 9d982407..610f200b 100644 --- a/Tests/XCTestDynamicOverlayTests/XCTContextTests.swift +++ b/Tests/XCTestDynamicOverlayTests/XCTContextTests.swift @@ -1,4 +1,4 @@ -#if DEBUG && !os(Linux) && !os(Windows) +#if !os(Linux) && !os(Windows) import XCTest import XCTestDynamicOverlay diff --git a/Tests/XCTestDynamicOverlayTests/XCTExpectFailureTests.swift b/Tests/XCTestDynamicOverlayTests/XCTExpectFailureTests.swift index 914441e8..d6c8487c 100644 --- a/Tests/XCTestDynamicOverlayTests/XCTExpectFailureTests.swift +++ b/Tests/XCTestDynamicOverlayTests/XCTExpectFailureTests.swift @@ -1,6 +1,6 @@ import XCTest -#if DEBUG && canImport(ObjectiveC) +#if canImport(ObjectiveC) final class XCTExpectFailureTests: XCTestCase { func testXCTDynamicOverlayWithBlockShouldFail() async throws { MyXCTExpectFailure("This is expected to pass.", strict: false) {