Skip to content

Use the entire SHA-256 hash as an exit test ID. #1053

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 7, 2025
Merged
Changes from all commits
Commits
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
22 changes: 13 additions & 9 deletions Sources/Testing/ExitTests/ExitTest.swift
Original file line number Diff line number Diff line change
@@ -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<ExitTest.Result> & Sendable],
expression: __Expression,
Original file line number Diff line number Diff line change
@@ -1147,7 +1147,7 @@ public func __checkClosureCall<R>(
/// `#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<ExitTest.Result> & Sendable],
performing body: @convention(thin) () -> Void,
49 changes: 27 additions & 22 deletions Sources/TestingMacros/ConditionMacro.swift
Original file line number Diff line number Diff line change
@@ -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)
}
}
}