diff --git a/Sources/Testing/ExitTests/ExitTest.swift b/Sources/Testing/ExitTests/ExitTest.swift index a38c7592e..13e72b691 100644 --- a/Sources/Testing/ExitTests/ExitTest.swift +++ b/Sources/Testing/ExitTests/ExitTest.swift @@ -48,13 +48,17 @@ public struct ExitTest: Sendable, ~Copyable { /// Storage for the underlying bits of the ID. /// /// - Note: On Apple platforms, we deploy to OS versions that do not include - /// support for `UInt128`, so we use two `UInt64`s for storage instead. - private var _lo: UInt64 - private var _hi: UInt64 - - init(_ uuid: (UInt64, UInt64)) { - self._lo = uuid.0 - self._hi = uuid.1 + /// support for `UInt128`, so we use four `UInt64`s for storage instead. + private var _0: UInt64 + private var _1: UInt64 + private var _2: UInt64 + private var _3: UInt64 + + init(_ uuid: (UInt64, UInt64, UInt64, UInt64)) { + self._0 = uuid.0 + self._1 = uuid.1 + self._2 = uuid.2 + self._3 = uuid.3 } } @@ -270,7 +274,7 @@ extension ExitTest: DiscoverableAsTestContent { /// - Warning: This function is used to implement the `#expect(exitsWith:)` /// macro. Do not use it directly. public static func __store( - _ id: (UInt64, UInt64), + _ id: (UInt64, UInt64, UInt64, UInt64), _ body: @escaping @Sendable () async throws -> Void, into outValue: UnsafeMutableRawPointer, asTypeAt typeAddress: UnsafeRawPointer, @@ -344,7 +348,7 @@ extension ExitTest { /// `await #expect(exitsWith:) { }` invocations regardless of calling /// convention. func callExitTest( - identifiedBy exitTestID: (UInt64, UInt64), + identifiedBy exitTestID: (UInt64, UInt64, UInt64, UInt64), exitsWith expectedExitCondition: ExitTest.Condition, observing observedValues: [any PartialKeyPath & Sendable], expression: __Expression, diff --git a/Sources/Testing/Expectations/ExpectationChecking+Macro.swift b/Sources/Testing/Expectations/ExpectationChecking+Macro.swift index 7254ad049..aa999395a 100644 --- a/Sources/Testing/Expectations/ExpectationChecking+Macro.swift +++ b/Sources/Testing/Expectations/ExpectationChecking+Macro.swift @@ -1147,7 +1147,7 @@ public func __checkClosureCall( /// `#require()` macros. Do not call it directly. @_spi(Experimental) public func __checkClosureCall( - identifiedBy exitTestID: (UInt64, UInt64), + identifiedBy exitTestID: (UInt64, UInt64, UInt64, UInt64), exitsWith expectedExitCondition: ExitTest.Condition, observing observedValues: [any PartialKeyPath & Sendable], performing body: @convention(thin) () -> Void, diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index c82acd725..f8b87e1fa 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -551,30 +551,35 @@ extension ExitTestConditionMacro { for macro: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext ) -> ExprSyntax { - let exitTestID: (UInt64, UInt64) - if let sourceLocation = context.location(of: macro, at: .afterLeadingTrivia, filePathMode: .fileID), - let fileID = sourceLocation.file.as(StringLiteralExprSyntax.self)?.representedLiteralValue, - let line = sourceLocation.line.as(IntegerLiteralExprSyntax.self)?.representedLiteralValue, - let column = sourceLocation.column.as(IntegerLiteralExprSyntax.self)?.representedLiteralValue { - // Hash the entire source location and store as many bits as possible in - // the resulting ID. - let stringValue = "\(fileID):\(line):\(column)" - exitTestID = SHA256.hash(stringValue.utf8).withUnsafeBytes { sha256 in - sha256.loadUnaligned(as: (UInt64, UInt64).self) + withUnsafeTemporaryAllocation(of: UInt64.self, capacity: 4) { exitTestID in + if let sourceLocation = context.location(of: macro, at: .afterLeadingTrivia, filePathMode: .fileID), + let fileID = sourceLocation.file.as(StringLiteralExprSyntax.self)?.representedLiteralValue, + let line = sourceLocation.line.as(IntegerLiteralExprSyntax.self)?.representedLiteralValue, + let column = sourceLocation.column.as(IntegerLiteralExprSyntax.self)?.representedLiteralValue { + // Hash the entire source location and store the entire hash in the + // resulting ID. + let stringValue = "\(fileID):\(line):\(column)" + exitTestID.withMemoryRebound(to: UInt8.self) { exitTestID in + _ = exitTestID.initialize(from: SHA256.hash(stringValue.utf8)) + } + } else { + // This branch is dead code in production, but is used when we expand a + // macro in our own unit tests because the macro expansion context does + // not have real source location information. + for i in 0 ..< exitTestID.count { + exitTestID[i] = .random(in: 0 ... .max) + } } - } else { - // This branch is dead code in production, but is used when we expand a - // macro in our own unit tests because the macro expansion context does - // not have real source location information. - exitTestID.0 = .random(in: 0 ... .max) - exitTestID.1 = .random(in: 0 ... .max) - } - // Return a tuple of integer literals (which is what the runtime __store() - // function is expecting.) - return """ - (\(IntegerLiteralExprSyntax(exitTestID.0, radix: .hex)), \(IntegerLiteralExprSyntax(exitTestID.1, radix: .hex))) - """ + // Return a tuple of integer literals (which is what the runtime __store() + // function is expecting.) + let tupleExpr = TupleExprSyntax { + for uint64 in exitTestID { + LabeledExprSyntax(expression: IntegerLiteralExprSyntax(uint64, radix: .hex)) + } + } + return ExprSyntax(tupleExpr) + } } }