Skip to content

Commit 950fa6e

Browse files
committed
Update to use generic Test.Attachment
1 parent 184774b commit 950fa6e

File tree

7 files changed

+96
-66
lines changed

7 files changed

+96
-66
lines changed

Sources/Overlays/_Testing_Foundation/Attachments/Data+Test.Attachable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public import Foundation
1414

1515
@_spi(Experimental)
1616
extension Data: Test.Attachable {
17-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
17+
public func withUnsafeBufferPointer<R>(for attachment: borrowing Test.Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
1818
try withUnsafeBytes(body)
1919
}
2020
}

Sources/Overlays/_Testing_Foundation/Attachments/EncodingFormat.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ enum EncodingFormat {
4141
/// - attachment: The attachment that will be encoded.
4242
///
4343
/// - Throws: If the attachment's content type or media type is unsupported.
44-
init(for attachment: borrowing Test.Attachment) throws {
44+
init(for attachment: borrowing Test.Attachment<some Test.Attachable>) throws {
4545
let ext = (attachment.preferredName as NSString).pathExtension
4646

4747
#if SWT_TARGET_OS_APPLE && canImport(UniformTypeIdentifiers)

Sources/Overlays/_Testing_Foundation/Attachments/Test.Attachable+Encodable&NSSecureCoding.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,8 @@ public import Foundation
1515
@_spi(Experimental)
1616
extension Test.Attachable where Self: Encodable & NSSecureCoding {
1717
@_documentation(visibility: private)
18-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
19-
func open(_ value: some Encodable & Test.Attachable, for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
20-
return try value.withUnsafeBufferPointer(for: attachment, body)
21-
}
22-
return try open(self, for: attachment, body)
18+
public func withUnsafeBufferPointer<R>(for attachment: borrowing Test.Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
19+
try _Testing_Foundation.withUnsafeBufferPointer(encoding: self, for: attachment, body)
2320
}
2421
}
2522
#endif

Sources/Overlays/_Testing_Foundation/Attachments/Test.Attachable+Encodable.swift

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,43 @@
1212
@_spi(Experimental) public import Testing
1313
private import Foundation
1414

15+
/// A common implementation of ``withUnsafeBufferPointer(for:_:)`` that is
16+
/// used when a type conforms to `Encodable`, whether or not it also conforms
17+
/// to `NSSecureCoding`.
18+
///
19+
/// - Parameters:
20+
/// - attachment: The attachment that is requesting a buffer (that is, the
21+
/// attachment containing this instance.)
22+
/// - body: A function to call. A temporary buffer containing a data
23+
/// representation of this instance is passed to it.
24+
///
25+
/// - Returns: Whatever is returned by `body`.
26+
///
27+
/// - Throws: Whatever is thrown by `body`, or any error that prevented the
28+
/// creation of the buffer.
29+
func withUnsafeBufferPointer<E, R>(encoding attachableValue: borrowing E, for attachment: borrowing Test.Attachment<E>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R where E: Test.Attachable & Encodable {
30+
let format = try EncodingFormat(for: attachment)
31+
32+
let data: Data
33+
switch format {
34+
case let .propertyListFormat(propertyListFormat):
35+
let plistEncoder = PropertyListEncoder()
36+
plistEncoder.outputFormat = propertyListFormat
37+
data = try plistEncoder.encode(attachableValue)
38+
case .default:
39+
// The default format is JSON.
40+
fallthrough
41+
case .json:
42+
// We cannot use our own JSON encoding wrapper here because that would
43+
// require it be exported with (at least) package visibility which would
44+
// create a visible external dependency on Foundation in the main testing
45+
// library target.
46+
data = try JSONEncoder().encode(attachableValue)
47+
}
48+
49+
return try data.withUnsafeBytes(body)
50+
}
51+
1552
// Implement the protocol requirements generically for any encodable value by
1653
// encoding to JSON. This lets developers provide trivial conformance to the
1754
// protocol for types that already support Codable.
@@ -53,27 +90,8 @@ extension Test.Attachable where Self: Encodable {
5390
/// some other path extension, that path extension must represent a type
5491
/// that conforms to [`UTType.propertyList`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/propertylist)
5592
/// or to [`UTType.json`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/json).
56-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
57-
let format = try EncodingFormat(for: attachment)
58-
59-
let data: Data
60-
switch format {
61-
case let .propertyListFormat(propertyListFormat):
62-
let plistEncoder = PropertyListEncoder()
63-
plistEncoder.outputFormat = propertyListFormat
64-
data = try plistEncoder.encode(self)
65-
case .default:
66-
// The default format is JSON.
67-
fallthrough
68-
case .json:
69-
// We cannot use our own JSON encoding wrapper here because that would
70-
// require it be exported with (at least) package visibility which would
71-
// create a visible external dependency on Foundation in the main testing
72-
// library target.
73-
data = try JSONEncoder().encode(self)
74-
}
75-
76-
return try data.withUnsafeBytes(body)
93+
public func withUnsafeBufferPointer<R>(for attachment: borrowing Test.Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
94+
try _Testing_Foundation.withUnsafeBufferPointer(encoding: self, for: attachment, body)
7795
}
7896
}
7997
#endif

Sources/Overlays/_Testing_Foundation/Attachments/Test.Attachable+NSSecureCoding.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ extension Test.Attachable where Self: NSSecureCoding {
5050
/// - Note: On Apple platforms, if the attachment's preferred name includes
5151
/// some other path extension, that path extension must represent a type
5252
/// that conforms to [`UTType.propertyList`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/propertylist).
53-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
53+
public func withUnsafeBufferPointer<R>(for attachment: borrowing Test.Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
5454
let format = try EncodingFormat(for: attachment)
5555

5656
var data = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: true)

Sources/Overlays/_Testing_Foundation/Attachments/Test.Attachment+URL.swift

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ extension UTType {
4242
#endif
4343

4444
@_spi(Experimental)
45-
extension Test.Attachment {
45+
extension Test.Attachment where AttachableValue == FileAttachment {
4646
/// Initialize an instance of this type with the contents of the given URL.
4747
///
4848
/// - Parameters:
@@ -55,8 +55,7 @@ extension Test.Attachment {
5555
/// - Throws: Any error that occurs attempting to read from `url`.
5656
public init(
5757
contentsOf url: URL,
58-
named preferredName: String? = nil,
59-
sourceLocation: SourceLocation = #_sourceLocation
58+
named preferredName: String? = nil
6059
) async throws {
6160
guard url.isFileURL else {
6261
// TODO: network URLs?
@@ -88,30 +87,39 @@ extension Test.Attachment {
8887
return (preferredName as NSString).appendingPathExtension("tgz") ?? preferredName
8988
}()
9089

91-
let attachableValue = try await _DirectoryContentAttachableProxy(contentsOfDirectoryAt: url)
92-
self.init(attachableValue, named: preferredName, sourceLocation: sourceLocation)
90+
try await self.init(FileAttachment(compressedContentsOfDirectoryAt: url), named: preferredName)
9391
} else {
9492
// Load the file.
95-
let attachableValue = try Data(contentsOf: url, options: [.mappedIfSafe])
96-
self.init(attachableValue, named: preferredName, sourceLocation: sourceLocation)
93+
try self.init(FileAttachment(contentsOfFileAt: url), named: preferredName)
9794
}
9895
}
9996
}
10097

101-
// MARK: - Attaching directories
98+
// MARK: - Attaching files and directories
10299

103-
/// A type representing the content of a directory as an attachable value.
104-
private struct _DirectoryContentAttachableProxy: Test.Attachable {
105-
/// The URL of the directory.
106-
///
107-
/// The contents of this directory may change after this instance is
108-
/// initialized. Such changes are not tracked.
109-
var url: URL
100+
/// A type representing a file system object that has been added to an
101+
/// attachment.
102+
@_spi(Experimental)
103+
public struct FileAttachment: Test.Attachable, Sendable {
104+
/// The file URL where the represented file system object is located.
105+
public private(set) var url: URL
110106

111-
/// The archived contents of the directory.
112-
private let _directoryContent: Data
107+
/// The contents of the file or directory at `url`.
108+
private var _data: Data
109+
110+
/// Initialize an instance of this type by reading the contents of a file.
111+
///
112+
/// - Parameters:
113+
/// - fileURL: A URL referring to the file to attach.
114+
///
115+
/// - Throws: Any error encountered trying to read the file at `fileURL`.
116+
init(contentsOfFileAt fileURL: URL) throws {
117+
url = fileURL
118+
_data = try Data(contentsOf: url, options: [.mappedIfSafe])
119+
}
113120

114-
/// Initialize an instance of this type.
121+
/// Initialize an instance of this type by compressing the contents of a
122+
/// directory.
115123
///
116124
/// - Parameters:
117125
/// - directoryURL: A URL referring to the directory to attach.
@@ -122,9 +130,7 @@ private struct _DirectoryContentAttachableProxy: Test.Attachable {
122130
/// This initializer asynchronously compresses the contents of `directoryURL`
123131
/// into an archive (currently of `.tgz` format, although this is subject to
124132
/// change) and stores a mapped copy of that archive.
125-
init(contentsOfDirectoryAt directoryURL: URL) async throws {
126-
url = directoryURL
127-
133+
init(compressedContentsOfDirectoryAt directoryURL: URL) async throws {
128134
let temporaryName = "\(UUID().uuidString).tgz"
129135
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(temporaryName)
130136

@@ -134,7 +140,7 @@ private struct _DirectoryContentAttachableProxy: Test.Attachable {
134140
#else
135141
let tarPath = "/usr/bin/tar"
136142
#endif
137-
let sourcePath = url.fileSystemPath
143+
let sourcePath = directoryURL.fileSystemPath
138144
let destinationPath = temporaryURL.fileSystemPath
139145
defer {
140146
remove(destinationPath)
@@ -161,14 +167,20 @@ private struct _DirectoryContentAttachableProxy: Test.Attachable {
161167
continuation.resume(throwing: error)
162168
}
163169
}
164-
_directoryContent = try Data(contentsOf: temporaryURL, options: [.mappedIfSafe])
170+
171+
url = directoryURL
172+
_data = try Data(contentsOf: temporaryURL, options: [.mappedIfSafe])
165173
#else
166174
throw CocoaError(.featureUnsupported, userInfo: [NSLocalizedDescriptionKey: "This platform does not support attaching directories to tests."])
167175
#endif
168176
}
169177

170-
func withUnsafeBufferPointer<R>(for attachment: borrowing Test.Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
171-
try _directoryContent.withUnsafeBytes(body)
178+
public var estimatedAttachmentByteCount: Int? {
179+
_data.count
180+
}
181+
182+
public func withUnsafeBufferPointer<R>(for attachment: borrowing Testing.Test.Attachment<FileAttachment>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
183+
try _data.withUnsafeBytes(body)
172184
}
173185
}
174186
#endif

Tests/TestingTests/AttachmentTests.swift

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,13 @@ struct AttachmentTests {
234234
await confirmation("Attachment detected") { valueAttached in
235235
var configuration = Configuration()
236236
configuration.eventHandler = { event, _ in
237-
guard case let .valueAttached(attachment) = event.kind else {
237+
guard case let .valueAttached(attachment, _) = event.kind else {
238238
return
239239
}
240240

241241
#expect(attachment.preferredName == temporaryFileName)
242242
#expect(throws: Never.self) {
243-
try attachment.attachableValue.withUnsafeBufferPointer(for: attachment) { buffer in
243+
try attachment.withUnsafeBufferPointer { buffer in
244244
#expect(buffer.count == data.count)
245245
}
246246
}
@@ -267,7 +267,7 @@ struct AttachmentTests {
267267
await confirmation("Attachment detected") { valueAttached in
268268
var configuration = Configuration()
269269
configuration.eventHandler = { event, _ in
270-
guard case let .valueAttached(attachment) = event.kind else {
270+
guard case let .valueAttached(attachment, _) = event.kind else {
271271
return
272272
}
273273

@@ -357,19 +357,22 @@ struct AttachmentTests {
357357
name = "\(name).\(ext)"
358358
}
359359

360-
var attachment: Test.Attachment
360+
func open<T>(_ attachment: borrowing Test.Attachment<T>) throws where T: Test.Attachable {
361+
try attachment.attachableValue.withUnsafeBufferPointer(for: attachment) { bytes in
362+
#expect(bytes.first == args.firstCharacter.asciiValue)
363+
let decodedStringValue = try args.decode(Data(bytes))
364+
#expect(decodedStringValue == "stringly speaking")
365+
}
366+
}
367+
361368
if args.forSecureCoding {
362369
let attachableValue = MySecureCodingAttachable(string: "stringly speaking")
363-
attachment = Test.Attachment(attachableValue, named: name)
370+
let attachment = Test.Attachment(attachableValue, named: name)
371+
try open(attachment)
364372
} else {
365373
let attachableValue = MyCodableAttachable(string: "stringly speaking")
366-
attachment = Test.Attachment(attachableValue, named: name)
367-
}
368-
369-
try attachment.attachableValue.withUnsafeBufferPointer(for: attachment) { bytes in
370-
#expect(bytes.first == args.firstCharacter.asciiValue)
371-
let decodedStringValue = try args.decode(Data(bytes))
372-
#expect(decodedStringValue == "stringly speaking")
374+
let attachment = Test.Attachment(attachableValue, named: name)
375+
try open(attachment)
373376
}
374377
}
375378

0 commit comments

Comments
 (0)