diff --git a/Sources/Testing/ABI/v0/Encoded/ABIv0.EncodedAttachment.swift b/Sources/Testing/ABI/v0/Encoded/ABIv0.EncodedAttachment.swift index 29ea5e414..c04edcc69 100644 --- a/Sources/Testing/ABI/v0/Encoded/ABIv0.EncodedAttachment.swift +++ b/Sources/Testing/ABI/v0/Encoded/ABIv0.EncodedAttachment.swift @@ -21,7 +21,7 @@ extension ABIv0 { /// The path where the attachment was written. var path: String? - init(encoding attachment: borrowing Test.Attachment, in eventContext: borrowing Event.Context) { + init(encoding attachment: borrowing Attachment, in eventContext: borrowing Event.Context) { path = attachment.fileSystemPath } } diff --git a/Sources/Testing/Attachments/Attachable.swift b/Sources/Testing/Attachments/Attachable.swift new file mode 100644 index 000000000..200421240 --- /dev/null +++ b/Sources/Testing/Attachments/Attachable.swift @@ -0,0 +1,144 @@ +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for Swift project authors +// + +/// A protocol describing a type that can be attached to a test report or +/// written to disk when a test is run. +/// +/// To attach an attachable value to a test report or test run output, use it to +/// initialize a new instance of ``Test/Attachment``, then call +/// ``Test/Attachment/attach(sourceLocation:)``. An attachment can only be +/// attached once. +/// +/// The testing library provides default conformances to this protocol for a +/// variety of standard library types. Most user-defined types do not need to +/// conform to this protocol. +/// +/// A type should conform to this protocol if it can be represented as a +/// sequence of bytes that would be diagnostically useful if a test fails. If a +/// type cannot conform directly to this protocol (such as a non-final class or +/// a type declared in a third-party module), you can create a container type +/// that conforms to ``Test/AttachableContainer`` to act as a proxy. +@_spi(Experimental) +public protocol Attachable: ~Copyable { + /// An estimate of the number of bytes of memory needed to store this value as + /// an attachment. + /// + /// The testing library uses this property to determine if an attachment + /// should be held in memory or should be immediately persisted to storage. + /// Larger attachments are more likely to be persisted, but the algorithm the + /// testing library uses is an implementation detail and is subject to change. + /// + /// The value of this property is approximately equal to the number of bytes + /// that will actually be needed, or `nil` if the value cannot be computed + /// efficiently. The default implementation of this property returns `nil`. + /// + /// - Complexity: O(1) unless `Self` conforms to `Collection`, in which case + /// up to O(_n_) where _n_ is the length of the collection. + var estimatedAttachmentByteCount: Int? { get } + + /// Call a function and pass a buffer representing this instance to it. + /// + /// - Parameters: + /// - attachment: The attachment that is requesting a buffer (that is, the + /// attachment containing this instance.) + /// - body: A function to call. A temporary buffer containing a data + /// representation of this instance is passed to it. + /// + /// - Returns: Whatever is returned by `body`. + /// + /// - Throws: Whatever is thrown by `body`, or any error that prevented the + /// creation of the buffer. + /// + /// The testing library uses this function when writing an attachment to a + /// test report or to a file on disk. The format of the buffer is + /// implementation-defined, but should be "idiomatic" for this type: for + /// example, if this type represents an image, it would be appropriate for + /// the buffer to contain an image in PNG format, JPEG format, etc., but it + /// would not be idiomatic for the buffer to contain a textual description of + /// the image. + borrowing func withUnsafeBufferPointer(for attachment: borrowing Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R +} + +// MARK: - Default implementations + +extension Attachable where Self: ~Copyable { + public var estimatedAttachmentByteCount: Int? { + nil + } +} + +extension Attachable where Self: Collection, Element == UInt8 { + public var estimatedAttachmentByteCount: Int? { + count + } + + // We do not provide an implementation of withUnsafeBufferPointer(for:_:) here + // because there is no way in the standard library to statically detect if a + // collection can provide contiguous storage (_HasContiguousBytes is not API.) + // If withContiguousBytesIfAvailable(_:) fails, we don't want to make a + // (potentially expensive!) copy of the collection. + // + // The planned Foundation cross-import overlay can provide a default + // implementation for collection types that conform to Foundation's + // ContiguousBytes protocol. +} + +extension Attachable where Self: StringProtocol { + 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 + } +} + +// MARK: - Default conformances + +// Implement the protocol requirements for byte arrays and buffers so that +// developers can attach raw data when needed. +@_spi(Experimental) +extension Array: Attachable { + public func withUnsafeBufferPointer(for attachment: borrowing Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + try withUnsafeBytes(body) + } +} + +@_spi(Experimental) +extension ContiguousArray: Attachable { + public func withUnsafeBufferPointer(for attachment: borrowing Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + try withUnsafeBytes(body) + } +} + +@_spi(Experimental) +extension ArraySlice: Attachable { + public func withUnsafeBufferPointer(for attachment: borrowing Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + try withUnsafeBytes(body) + } +} + +@_spi(Experimental) +extension String: Attachable { + public func withUnsafeBufferPointer(for attachment: borrowing Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + var selfCopy = self + return try selfCopy.withUTF8 { utf8 in + try body(UnsafeRawBufferPointer(utf8)) + } + } +} + +@_spi(Experimental) +extension Substring: Attachable { + public func withUnsafeBufferPointer(for attachment: borrowing Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + var selfCopy = self + return try selfCopy.withUTF8 { utf8 in + try body(UnsafeRawBufferPointer(utf8)) + } + } +} diff --git a/Sources/Testing/Attachments/AttachableContainer.swift b/Sources/Testing/Attachments/AttachableContainer.swift new file mode 100644 index 000000000..e9631c2ac --- /dev/null +++ b/Sources/Testing/Attachments/AttachableContainer.swift @@ -0,0 +1,35 @@ +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for Swift project authors +// + +/// A protocol describing a type that can be attached to a test report or +/// written to disk when a test is run and which contains another value that it +/// stands in for. +/// +/// To attach an attachable value to a test report or test run output, use it to +/// initialize a new instance of ``Test/Attachment``, then call +/// ``Test/Attachment/attach(sourceLocation:)``. An attachment can only be +/// attached once. +/// +/// A type can conform to this protocol if it represents another type that +/// cannot directly conform to ``Test/Attachable``, such as a non-final class or +/// a type declared in a third-party module. +@_spi(Experimental) +public protocol AttachableContainer: Attachable, ~Copyable { +#if hasFeature(SuppressedAssociatedTypes) + /// The type of the attachable value represented by this type. + associatedtype AttachableValue: ~Copyable +#else + /// The type of the attachable value represented by this type. + associatedtype AttachableValue +#endif + + /// The attachable value represented by this instance. + var attachableValue: AttachableValue { get } +} diff --git a/Sources/Testing/Attachments/Test.Attachment.swift b/Sources/Testing/Attachments/Attachment.swift similarity index 68% rename from Sources/Testing/Attachments/Test.Attachment.swift rename to Sources/Testing/Attachments/Attachment.swift index f4817a211..8e8788eb4 100644 --- a/Sources/Testing/Attachments/Test.Attachment.swift +++ b/Sources/Testing/Attachments/Attachment.swift @@ -10,75 +10,73 @@ private import _TestingInternals +/// A type describing values that can be attached to the output of a test run +/// and inspected later by the user. +/// +/// Attachments are included in test reports in Xcode or written to disk when +/// tests are run at the command line. To create an attachment, you need a value +/// of some type that conforms to ``Test/Attachable``. Initialize an instance of +/// ``Test/Attachment`` with that value and, optionally, a preferred filename to +/// use when writing to disk. @_spi(Experimental) -extension Test { - /// A type describing values that can be attached to the output of a test run - /// and inspected later by the user. - /// - /// Attachments are included in test reports in Xcode or written to disk when - /// tests are run at the command line. To create an attachment, you need a - /// value of some type that conforms to ``Test/Attachable``. Initialize an - /// instance of ``Test/Attachment`` with that value and, optionally, a - /// preferred filename to use when writing to disk. - public struct Attachment: ~Copyable where AttachableValue: Test.Attachable & ~Copyable { - /// Storage for ``attachableValue-7dyjv``. - fileprivate var _attachableValue: AttachableValue - - /// The path to which the this attachment was written, if any. - /// - /// If a developer sets the ``Configuration/attachmentsPath`` property of - /// the current configuration before running tests, or if a developer passes - /// `--experimental-attachments-path` on the command line, then attachments - /// will be automatically written to disk when they are attached and the - /// value of this property will describe the path where they were written. - /// - /// If no destination path is set, or if an error occurred while writing - /// this attachment to disk, the value of this property is `nil`. - @_spi(ForToolsIntegrationOnly) - public var fileSystemPath: String? - - /// The default preferred name to use if the developer does not supply one. - package static var defaultPreferredName: String { - "untitled" - } +public struct Attachment: ~Copyable where AttachableValue: Attachable & ~Copyable { + /// Storage for ``attachableValue-7dyjv``. + fileprivate var _attachableValue: AttachableValue - /// A filename to use when writing this attachment to a test report or to a - /// file on disk. - /// - /// The value of this property is used as a hint to the testing library. The - /// testing library may substitute a different filename as needed. If the - /// value of this property has not been explicitly set, the testing library - /// will attempt to generate its own value. - public var preferredName: String - - /// The source location of this instance. - /// - /// This property is not part of the public interface of the testing - /// library. It is initially set when the attachment is created and is - /// updated later when the attachment is attached to something. - /// - /// The value of this property is used when recording issues associated with - /// the attachment. - var sourceLocation: SourceLocation + /// The path to which the this attachment was written, if any. + /// + /// If a developer sets the ``Configuration/attachmentsPath`` property of the + /// current configuration before running tests, or if a developer passes + /// `--experimental-attachments-path` on the command line, then attachments + /// will be automatically written to disk when they are attached and the value + /// of this property will describe the path where they were written. + /// + /// If no destination path is set, or if an error occurred while writing this + /// attachment to disk, the value of this property is `nil`. + @_spi(ForToolsIntegrationOnly) + public var fileSystemPath: String? + + /// The default preferred name to use if the developer does not supply one. + package static var defaultPreferredName: String { + "untitled" } + + /// A filename to use when writing this attachment to a test report or to a + /// file on disk. + /// + /// The value of this property is used as a hint to the testing library. The + /// testing library may substitute a different filename as needed. If the + /// value of this property has not been explicitly set, the testing library + /// will attempt to generate its own value. + public var preferredName: String + + /// The source location of this instance. + /// + /// This property is not part of the public interface of the testing library. + /// It is initially set when the attachment is created and is updated later + /// when the attachment is attached to something. + /// + /// The value of this property is used when recording issues associated with + /// the attachment. + var sourceLocation: SourceLocation } -extension Test.Attachment: Copyable where AttachableValue: Copyable {} -extension Test.Attachment: Sendable where AttachableValue: Sendable {} +extension Attachment: Copyable where AttachableValue: Copyable {} +extension Attachment: Sendable where AttachableValue: Sendable {} // MARK: - Initializing an attachment #if !SWT_NO_LAZY_ATTACHMENTS -extension Test.Attachment where AttachableValue: ~Copyable { +extension Attachment where AttachableValue: ~Copyable { /// Initialize an instance of this type that encloses the given attachable /// value. /// /// - Parameters: - /// - attachableValue: The value that will be attached to the output of - /// the test run. - /// - preferredName: The preferred name of the attachment when writing it - /// to a test report or to disk. If `nil`, the testing library attempts - /// to derive a reasonable filename for the attached value. + /// - attachableValue: The value that will be attached to the output of the + /// test run. + /// - preferredName: The preferred name of the attachment when writing it to + /// a test report or to disk. If `nil`, the testing library attempts to + /// derive a reasonable filename for the attached value. /// - sourceLocation: The source location of the call to this initializer. /// This value is used when recording issues associated with the /// attachment. @@ -90,14 +88,14 @@ extension Test.Attachment where AttachableValue: ~Copyable { } @_spi(Experimental) @_spi(ForToolsIntegrationOnly) -extension Test.Attachment where AttachableValue == Test.AnyAttachable { +extension Attachment where AttachableValue == AnyAttachable { /// Create a type-erased attachment from an instance of ``Test/Attachment``. /// /// - Parameters: /// - attachment: The attachment to type-erase. - fileprivate init(_ attachment: Test.Attachment) { + fileprivate init(_ attachment: Attachment) { self.init( - _attachableValue: Test.AnyAttachable(attachableValue: attachment.attachableValue), + _attachableValue: AnyAttachable(attachableValue: attachment.attachableValue), fileSystemPath: attachment.fileSystemPath, preferredName: attachment.preferredName, sourceLocation: attachment.sourceLocation @@ -106,55 +104,53 @@ extension Test.Attachment where AttachableValue == Test.AnyAttachable { } #endif -extension Test { - /// A type-erased container type that represents any attachable value. - /// - /// This type is not generally visible to developers. It is used when posting - /// events of kind ``Event/Kind/valueAttached(_:)``. Test tools authors who - /// use `@_spi(ForToolsIntegrationOnly)` will see instances of this type when - /// handling those events. - /// - /// @Comment { - /// Swift's type system requires that this type be at least as visible as - /// `Event.Kind.valueAttached(_:)`, otherwise it would be declared private. - /// } - @_spi(Experimental) @_spi(ForToolsIntegrationOnly) - public struct AnyAttachable: Test.AttachableContainer, Copyable, Sendable { +/// A type-erased container type that represents any attachable value. +/// +/// This type is not generally visible to developers. It is used when posting +/// events of kind ``Event/Kind/valueAttached(_:)``. Test tools authors who use +/// `@_spi(ForToolsIntegrationOnly)` will see instances of this type when +/// handling those events. +/// +/// @Comment { +/// Swift's type system requires that this type be at least as visible as +/// `Event.Kind.valueAttached(_:)`, otherwise it would be declared private. +/// } +@_spi(Experimental) @_spi(ForToolsIntegrationOnly) +public struct AnyAttachable: AttachableContainer, Copyable, Sendable { #if !SWT_NO_LAZY_ATTACHMENTS - public typealias AttachableValue = any Test.Attachable & Sendable /* & Copyable rdar://137614425 */ + public typealias AttachableValue = any Attachable & Sendable /* & Copyable rdar://137614425 */ #else - public typealias AttachableValue = [UInt8] + public typealias AttachableValue = [UInt8] #endif - public var attachableValue: AttachableValue + public var attachableValue: AttachableValue - init(attachableValue: AttachableValue) { - self.attachableValue = attachableValue - } + init(attachableValue: AttachableValue) { + self.attachableValue = attachableValue + } - public var estimatedAttachmentByteCount: Int? { - attachableValue.estimatedAttachmentByteCount - } + public var estimatedAttachmentByteCount: Int? { + attachableValue.estimatedAttachmentByteCount + } - public func withUnsafeBufferPointer(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { - func open(_ attachableValue: T, for attachment: borrowing Test.Attachment) throws -> R where T: Test.Attachable & Sendable & Copyable { - let temporaryAttachment = Test.Attachment( - _attachableValue: attachableValue, - fileSystemPath: attachment.fileSystemPath, - preferredName: attachment.preferredName, - sourceLocation: attachment.sourceLocation - ) - return try temporaryAttachment.withUnsafeBufferPointer(body) - } - return try open(attachableValue, for: attachment) + public func withUnsafeBufferPointer(for attachment: borrowing Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + func open(_ attachableValue: T, for attachment: borrowing Attachment) throws -> R where T: Attachable & Sendable & Copyable { + let temporaryAttachment = Attachment( + _attachableValue: attachableValue, + fileSystemPath: attachment.fileSystemPath, + preferredName: attachment.preferredName, + sourceLocation: attachment.sourceLocation + ) + return try temporaryAttachment.withUnsafeBufferPointer(body) } + return try open(attachableValue, for: attachment) } } // MARK: - Getting an attachable value from an attachment @_spi(Experimental) -extension Test.Attachment where AttachableValue: ~Copyable { +extension Attachment where AttachableValue: ~Copyable { /// The value of this attachment. @_disfavoredOverload public var attachableValue: AttachableValue { _read { @@ -164,7 +160,7 @@ extension Test.Attachment where AttachableValue: ~Copyable { } @_spi(Experimental) -extension Test.Attachment where AttachableValue: Test.AttachableContainer & ~Copyable { +extension Attachment where AttachableValue: AttachableContainer & ~Copyable { /// The value of this attachment. /// /// When the attachable value's type conforms to ``Test/AttachableContainer``, @@ -185,7 +181,7 @@ extension Test.Attachment where AttachableValue: Test.AttachableContainer & ~Cop // MARK: - Attaching an attachment to a test (etc.) #if !SWT_NO_LAZY_ATTACHMENTS -extension Test.Attachment where AttachableValue: Sendable & Copyable { +extension Attachment where AttachableValue: Sendable & Copyable { /// Attach this instance to the current test. /// /// - Parameters: @@ -194,14 +190,14 @@ extension Test.Attachment where AttachableValue: Sendable & Copyable { /// An attachment can only be attached once. @_documentation(visibility: private) public consuming func attach(sourceLocation: SourceLocation = #_sourceLocation) { - var attachmentCopy = Test.Attachment(self) + var attachmentCopy = Attachment(self) attachmentCopy.sourceLocation = sourceLocation Event.post(.valueAttached(attachmentCopy)) } } #endif -extension Test.Attachment where AttachableValue: ~Copyable { +extension Attachment where AttachableValue: ~Copyable { /// Attach this instance to the current test. /// /// - Parameters: @@ -219,8 +215,8 @@ extension Test.Attachment where AttachableValue: ~Copyable { public consuming func attach(sourceLocation: SourceLocation = #_sourceLocation) { do { let attachmentCopy = try withUnsafeBufferPointer { buffer in - let attachableContainer = Test.AnyAttachable(attachableValue: Array(buffer)) - return Test.Attachment(_attachableValue: attachableContainer, fileSystemPath: fileSystemPath, preferredName: preferredName, sourceLocation: sourceLocation) + let attachableContainer = AnyAttachable(attachableValue: Array(buffer)) + return Attachment(_attachableValue: attachableContainer, fileSystemPath: fileSystemPath, preferredName: preferredName, sourceLocation: sourceLocation) } Event.post(.valueAttached(attachmentCopy)) } catch { @@ -232,7 +228,7 @@ extension Test.Attachment where AttachableValue: ~Copyable { // MARK: - Getting the serialized form of an attachable value (generically) -extension Test.Attachment where AttachableValue: ~Copyable { +extension Attachment where AttachableValue: ~Copyable { /// Call a function and pass a buffer representing the value of this /// instance's ``attachableValue-7dyjv`` property to it. /// @@ -257,7 +253,7 @@ extension Test.Attachment where AttachableValue: ~Copyable { #if !SWT_NO_FILE_IO // MARK: - Writing -extension Test.Attachment where AttachableValue: ~Copyable { +extension Attachment where AttachableValue: ~Copyable { /// Write the attachment's contents to a file in the specified directory. /// /// - Parameters: diff --git a/Sources/Testing/Attachments/Test.Attachable.swift b/Sources/Testing/Attachments/Test.Attachable.swift deleted file mode 100644 index 83a7dc66c..000000000 --- a/Sources/Testing/Attachments/Test.Attachable.swift +++ /dev/null @@ -1,147 +0,0 @@ -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for Swift project authors -// - -@_spi(Experimental) -extension Test { - /// A protocol describing a type that can be attached to a test report or - /// written to disk when a test is run. - /// - /// To attach an attachable value to a test report or test run output, use it - /// to initialize a new instance of ``Test/Attachment``, then call - /// ``Test/Attachment/attach(sourceLocation:)``. An attachment can only be - /// attached once. - /// - /// The testing library provides default conformances to this protocol for a - /// variety of standard library types. Most user-defined types do not need to - /// conform to this protocol. - /// - /// A type should conform to this protocol if it can be represented as a - /// sequence of bytes that would be diagnostically useful if a test fails. If - /// a type cannot conform directly to this protocol (such as a non-final class - /// or a type declared in a third-party module), you can create a container - /// type that conforms to ``Test/AttachableContainer`` to act as a proxy. - public protocol Attachable: ~Copyable { - /// An estimate of the number of bytes of memory needed to store this value - /// as an attachment. - /// - /// The testing library uses this property to determine if an attachment - /// should be held in memory or should be immediately persisted to storage. - /// Larger attachments are more likely to be persisted, but the algorithm - /// the testing library uses is an implementation detail and is subject to - /// change. - /// - /// The value of this property is approximately equal to the number of bytes - /// that will actually be needed, or `nil` if the value cannot be computed - /// efficiently. The default implementation of this property returns `nil`. - /// - /// - Complexity: O(1) unless `Self` conforms to `Collection`, in which case - /// up to O(_n_) where _n_ is the length of the collection. - var estimatedAttachmentByteCount: Int? { get } - - /// Call a function and pass a buffer representing this instance to it. - /// - /// - Parameters: - /// - attachment: The attachment that is requesting a buffer (that is, the - /// attachment containing this instance.) - /// - body: A function to call. A temporary buffer containing a data - /// representation of this instance is passed to it. - /// - /// - Returns: Whatever is returned by `body`. - /// - /// - Throws: Whatever is thrown by `body`, or any error that prevented the - /// creation of the buffer. - /// - /// The testing library uses this function when writing an attachment to a - /// test report or to a file on disk. The format of the buffer is - /// implementation-defined, but should be "idiomatic" for this type: for - /// example, if this type represents an image, it would be appropriate for - /// the buffer to contain an image in PNG format, JPEG format, etc., but it - /// would not be idiomatic for the buffer to contain a textual description - /// of the image. - borrowing func withUnsafeBufferPointer(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R - } -} - -// MARK: - Default implementations - -extension Test.Attachable where Self: ~Copyable { - public var estimatedAttachmentByteCount: Int? { - nil - } -} - -extension Test.Attachable where Self: Collection, Element == UInt8 { - public var estimatedAttachmentByteCount: Int? { - count - } - - // We do not provide an implementation of withUnsafeBufferPointer(for:_:) here - // because there is no way in the standard library to statically detect if a - // collection can provide contiguous storage (_HasContiguousBytes is not API.) - // If withContiguousBytesIfAvailable(_:) fails, we don't want to make a - // (potentially expensive!) copy of the collection. - // - // The planned Foundation cross-import overlay can provide a default - // implementation for collection types that conform to Foundation's - // ContiguousBytes protocol. -} - -extension Test.Attachable where Self: StringProtocol { - 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 - } -} - -// MARK: - Default conformances - -// Implement the protocol requirements for byte arrays and buffers so that -// developers can attach raw data when needed. -@_spi(Experimental) -extension Array: Test.Attachable { - public func withUnsafeBufferPointer(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { - try withUnsafeBytes(body) - } -} - -@_spi(Experimental) -extension ContiguousArray: Test.Attachable { - public func withUnsafeBufferPointer(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { - try withUnsafeBytes(body) - } -} - -@_spi(Experimental) -extension ArraySlice: Test.Attachable { - public func withUnsafeBufferPointer(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { - try withUnsafeBytes(body) - } -} - -@_spi(Experimental) -extension String: Test.Attachable { - public func withUnsafeBufferPointer(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { - var selfCopy = self - return try selfCopy.withUTF8 { utf8 in - try body(UnsafeRawBufferPointer(utf8)) - } - } -} - -@_spi(Experimental) -extension Substring: Test.Attachable { - public func withUnsafeBufferPointer(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { - var selfCopy = self - return try selfCopy.withUTF8 { utf8 in - try body(UnsafeRawBufferPointer(utf8)) - } - } -} diff --git a/Sources/Testing/Attachments/Test.AttachableContainer.swift b/Sources/Testing/Attachments/Test.AttachableContainer.swift deleted file mode 100644 index 7eb2de3ba..000000000 --- a/Sources/Testing/Attachments/Test.AttachableContainer.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for Swift project authors -// - -@_spi(Experimental) -extension Test { - /// A protocol describing a type that can be attached to a test report or - /// written to disk when a test is run and which contains another value that - /// it stands in for. - /// - /// To attach an attachable value to a test report or test run output, use it - /// to initialize a new instance of ``Test/Attachment``, then call - /// ``Test/Attachment/attach(sourceLocation:)``. An attachment can only be - /// attached once. - /// - /// A type can conform to this protocol if it represents another type that - /// cannot directly conform to ``Test/Attachable``, such as a non-final class - /// or a type declared in a third-party module. - public protocol AttachableContainer: Attachable, ~Copyable { -#if hasFeature(SuppressedAssociatedTypes) - /// The type of the attachable value represented by this type. - associatedtype AttachableValue: ~Copyable -#else - /// The type of the attachable value represented by this type. - associatedtype AttachableValue -#endif - - /// The attachable value represented by this instance. - var attachableValue: AttachableValue { get } - } -} diff --git a/Sources/Testing/CMakeLists.txt b/Sources/Testing/CMakeLists.txt index 696a3d9d0..8efe7816c 100644 --- a/Sources/Testing/CMakeLists.txt +++ b/Sources/Testing/CMakeLists.txt @@ -21,9 +21,9 @@ add_library(Testing ABI/v0/Encoded/ABIv0.EncodedIssue.swift ABI/v0/Encoded/ABIv0.EncodedMessage.swift ABI/v0/Encoded/ABIv0.EncodedTest.swift - Attachments/Test.Attachable.swift - Attachments/Test.AttachableContainer.swift - Attachments/Test.Attachment.swift + Attachments/Attachable.swift + Attachments/AttachableContainer.swift + Attachments/Attachment.swift Events/Clock.swift Events/Event.swift Events/Recorder/Event.ConsoleOutputRecorder.swift diff --git a/Sources/Testing/Events/Event.swift b/Sources/Testing/Events/Event.swift index d7813e1c6..d76346623 100644 --- a/Sources/Testing/Events/Event.swift +++ b/Sources/Testing/Events/Event.swift @@ -103,7 +103,7 @@ public struct Event: Sendable { /// - Parameters: /// - attachment: The attachment that was created. @_spi(Experimental) - indirect case valueAttached(_ attachment: Test.Attachment) + indirect case valueAttached(_ attachment: Attachment) /// A test ended. /// diff --git a/Tests/TestingTests/AttachmentTests.swift b/Tests/TestingTests/AttachmentTests.swift index cd8a87493..37430a6a4 100644 --- a/Tests/TestingTests/AttachmentTests.swift +++ b/Tests/TestingTests/AttachmentTests.swift @@ -15,7 +15,7 @@ private import _TestingInternals struct AttachmentTests { @Test func saveValue() { let attachableValue = MyAttachable(string: "") - let attachment = Test.Attachment(attachableValue, named: "AttachmentTests.saveValue.html") + let attachment = Attachment(attachableValue, named: "AttachmentTests.saveValue.html") attachment.attach() } @@ -34,7 +34,7 @@ struct AttachmentTests { @Test func writeAttachment() throws { let attachableValue = MySendableAttachable(string: "") - let attachment = Test.Attachment(attachableValue, named: "loremipsum.html") + let attachment = Attachment(attachableValue, named: "loremipsum.html") // Write the attachment to disk, then read it back. let filePath = try attachment.write(toFileInDirectoryAtPath: temporaryDirectory()) @@ -61,7 +61,7 @@ struct AttachmentTests { for i in 0 ..< 5 { let attachableValue = MySendableAttachable(string: "\(i)") - let attachment = Test.Attachment(attachableValue, named: baseFileName) + let attachment = Attachment(attachableValue, named: baseFileName) // Write the attachment to disk, then read it back. let filePath = try attachment.write(toFileInDirectoryAtPath: temporaryDirectory(), appending: suffixes.next()!) @@ -78,7 +78,7 @@ struct AttachmentTests { @Test func writeAttachmentWithMultiplePathExtensions() throws { let attachableValue = MySendableAttachable(string: "") - let attachment = Test.Attachment(attachableValue, named: "loremipsum.tar.gz.gif.jpeg.html") + let attachment = Attachment(attachableValue, named: "loremipsum.tar.gz.gif.jpeg.html") // Write the attachment to disk once to ensure the original filename is not // available and we add a suffix. @@ -119,7 +119,7 @@ struct AttachmentTests { String(repeating: "a", count: maximumNameCount + 2), ] + reservedNames) func writeAttachmentWithBadName(name: String) throws { let attachableValue = MySendableAttachable(string: "") - let attachment = Test.Attachment(attachableValue, named: name) + let attachment = Attachment(attachableValue, named: name) // Write the attachment to disk, then read it back. let filePath = try attachment.write(toFileInDirectoryAtPath: temporaryDirectory()) @@ -154,7 +154,7 @@ struct AttachmentTests { } await Test { - let attachment = Test.Attachment(attachableValue, named: "loremipsum.html") + let attachment = Attachment(attachableValue, named: "loremipsum.html") attachment.attach() }.run(configuration: configuration) } @@ -176,7 +176,7 @@ struct AttachmentTests { await Test { let attachableValue = MyAttachable(string: "") - Test.Attachment(attachableValue, named: "loremipsum").attach() + Attachment(attachableValue, named: "loremipsum").attach() }.run(configuration: configuration) } } @@ -197,7 +197,7 @@ struct AttachmentTests { await Test { let attachableValue = MySendableAttachable(string: "") - Test.Attachment(attachableValue, named: "loremipsum").attach() + Attachment(attachableValue, named: "loremipsum").attach() }.run(configuration: configuration) } } @@ -221,7 +221,7 @@ struct AttachmentTests { await Test { var attachableValue = MyAttachable(string: "") attachableValue.errorToThrow = MyError() - Test.Attachment(attachableValue, named: "loremipsum").attach() + Attachment(attachableValue, named: "loremipsum").attach() }.run(configuration: configuration) } } @@ -231,9 +231,9 @@ struct AttachmentTests { extension AttachmentTests { @Suite("Built-in conformances") struct BuiltInConformances { - func test(_ value: some Test.Attachable) throws { + func test(_ value: some Attachable) throws { #expect(value.estimatedAttachmentByteCount == 6) - let attachment = Test.Attachment(value) + let attachment = Attachment(value) try attachment.withUnsafeBufferPointer { buffer in #expect(buffer.elementsEqual("abc123".utf8)) #expect(buffer.count == 6) @@ -269,11 +269,11 @@ extension AttachmentTests { // MARK: - Fixtures -struct MyAttachable: Test.Attachable, ~Copyable { +struct MyAttachable: Attachable, ~Copyable { var string: String var errorToThrow: (any Error)? - func withUnsafeBufferPointer(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + func withUnsafeBufferPointer(for attachment: borrowing Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { if let errorToThrow { throw errorToThrow } @@ -288,10 +288,10 @@ struct MyAttachable: Test.Attachable, ~Copyable { @available(*, unavailable) extension MyAttachable: Sendable {} -struct MySendableAttachable: Test.Attachable, Sendable { +struct MySendableAttachable: Attachable, Sendable { var string: String - func withUnsafeBufferPointer(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + func withUnsafeBufferPointer(for attachment: borrowing Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { #expect(attachment.attachableValue.string == string) var string = string return try string.withUTF8 { buffer in @@ -300,10 +300,10 @@ struct MySendableAttachable: Test.Attachable, Sendable { } } -struct MySendableAttachableWithDefaultByteCount: Test.Attachable, Sendable { +struct MySendableAttachableWithDefaultByteCount: Attachable, Sendable { var string: String - func withUnsafeBufferPointer(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + func withUnsafeBufferPointer(for attachment: borrowing Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { var string = string return try string.withUTF8 { buffer in try body(.init(buffer))