Skip to content
Merged
Show file tree
Hide file tree
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
79 changes: 79 additions & 0 deletions Sources/Testing/Attachments/Attachable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,37 @@ public protocol Attachable: ~Copyable {

// MARK: - Default implementations

/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
extension Attachable where Self: ~Copyable {
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
public var estimatedAttachmentByteCount: Int? {
nil
}

/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
public borrowing func preferredName(for attachment: borrowing Attachment<Self>, basedOn suggestedName: String) -> String {
suggestedName
}
}

/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
extension Attachable where Self: Collection, Element == UInt8 {
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
public var estimatedAttachmentByteCount: Int? {
count
}
Expand All @@ -125,37 +145,88 @@ extension Attachable where Self: Collection, Element == UInt8 {
// (potentially expensive!) copy of the collection.
}

/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
extension Attachable where Self: StringProtocol {
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
public var estimatedAttachmentByteCount: Int? {
// NOTE: utf8.count may be O(n) for foreign strings.
// SEE: https://github.com/swiftlang/swift/blob/main/stdlib/public/core/StringUTF8View.swift
utf8.count
}

/// @Metadata {
/// @Available(Swift, introduced: 6.2)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conformance was introduced in 6.2, so yes this is accurate. :)

/// @Available(Xcode, introduced: 26.0)
/// }
public borrowing func preferredName(for attachment: borrowing Attachment<Self>, basedOn suggestedName: String) -> String {
if suggestedName.contains(".") {
return suggestedName
}
return "\(suggestedName).txt"
}
}

// MARK: - Default conformances

// Implement the protocol requirements for byte arrays and buffers so that
// developers can attach raw data when needed.
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
extension Array<UInt8>: Attachable {
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try withUnsafeBytes(body)
}
}

/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
extension ContiguousArray<UInt8>: Attachable {
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try withUnsafeBytes(body)
}
}

/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
extension ArraySlice<UInt8>: Attachable {
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try withUnsafeBytes(body)
}
}

/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
extension String: Attachable {
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
var selfCopy = self
return try selfCopy.withUTF8 { utf8 in
Expand All @@ -164,7 +235,15 @@ extension String: Attachable {
}
}

/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
extension Substring: Attachable {
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
/// }
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
var selfCopy = self
return try selfCopy.withUTF8 { utf8 in
Expand Down
8 changes: 8 additions & 0 deletions Tests/TestingTests/AttachmentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ struct AttachmentTests {
}
#endif

@Test func preferredNameOfStringAttachment() {
let attachment1 = Attachment("", named: "abc123")
#expect(attachment1.preferredName == "abc123.txt")

let attachment2 = Attachment("", named: "abc123.html")
#expect(attachment2.preferredName == "abc123.html")
}

#if !SWT_NO_FILE_IO
func compare(_ attachableValue: borrowing MySendableAttachable, toContentsOfFileAtPath filePath: String) throws {
let file = try FileHandle(forReadingAtPath: filePath)
Expand Down
10 changes: 5 additions & 5 deletions Tests/TestingTests/Traits/AttachmentSavingTraitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,27 +130,27 @@ extension `AttachmentSavingTrait tests` {
@Suite(.hidden, currentAttachmentSavingTrait)
struct FixtureSuite {
@Test(.hidden) func `Records an attachment (passing)`() {
Attachment.record("", named: "PASSING TEST")
Attachment.record([], named: "PASSING TEST")
}

@Test(.hidden) func `Records an attachment (warning)`() {
Attachment.record("", named: "PASSING TEST")
Attachment.record([], named: "PASSING TEST")
Issue.record("", severity: .warning)
}

@Test(.hidden) func `Records an attachment (failing)`() {
Attachment.record("", named: "FAILING TEST")
Attachment.record([], named: "FAILING TEST")
Issue.record("")
}

@Test(.hidden, arguments: 0 ..< 5)
func `Records an attachment (passing, parameterized)`(i: Int) async {
Attachment.record("\(i)", named: "PASSING TEST")
Attachment.record([UInt8(i)], named: "PASSING TEST")
}

@Test(.hidden, arguments: 0 ..< 7) // intentionally different count
func `Records an attachment (failing, parameterized)`(i: Int) async {
Attachment.record("\(i)", named: "FAILING TEST")
Attachment.record([UInt8(i)], named: "FAILING TEST")
Issue.record("\(i)")
}
}
Expand Down