diff --git a/Sources/Testing/ABI/ABI.Record+Streaming.swift b/Sources/Testing/ABI/ABI.Record+Streaming.swift index 1aa1362ec..2ba09cf25 100644 --- a/Sources/Testing/ABI/ABI.Record+Streaming.swift +++ b/Sources/Testing/ABI/ABI.Record+Streaming.swift @@ -14,7 +14,7 @@ private import Foundation extension ABI.Version { static func eventHandler( encodeAsJSONLines: Bool, - forwardingTo eventHandler: @escaping @Sendable (_ recordJSON: UnsafeRawBufferPointer) -> Void + forwardingTo eventHandler: @escaping @Sendable (_ recordJSON: borrowing RawSpan) -> Void ) -> Event.Handler { // Encode as JSON Lines if requested. var eventHandlerCopy = eventHandler @@ -44,7 +44,7 @@ extension ABI.Version { extension ABI.Xcode16 { static func eventHandler( encodeAsJSONLines: Bool, - forwardingTo eventHandler: @escaping @Sendable (_ recordJSON: UnsafeRawBufferPointer) -> Void + forwardingTo eventHandler: @escaping @Sendable (_ recordJSON: borrowing RawSpan) -> Void ) -> Event.Handler { return { event, context in if case .testDiscovered = event.kind { @@ -63,9 +63,7 @@ extension ABI.Xcode16 { eventContext: Event.Context.Snapshot(snapshotting: context) ) try? JSON.withEncoding(of: snapshot) { eventAndContextJSON in - eventAndContextJSON.withUnsafeBytes { eventAndContextJSON in - eventHandler(eventAndContextJSON) - } + eventHandler(eventAndContextJSON) } } } diff --git a/Sources/Testing/ABI/ABI.swift b/Sources/Testing/ABI/ABI.swift index 0089510b6..c74cc1848 100644 --- a/Sources/Testing/ABI/ABI.swift +++ b/Sources/Testing/ABI/ABI.swift @@ -37,7 +37,7 @@ extension ABI { /// associated context is created and is passed to `eventHandler`. static func eventHandler( encodeAsJSONLines: Bool, - forwardingTo eventHandler: @escaping @Sendable (_ recordJSON: UnsafeRawBufferPointer) -> Void + forwardingTo eventHandler: @escaping @Sendable (_ recordJSON: borrowing RawSpan) -> Void ) -> Event.Handler #endif } diff --git a/Sources/Testing/ABI/EntryPoints/ABIEntryPoint.swift b/Sources/Testing/ABI/EntryPoints/ABIEntryPoint.swift index 381cd98f7..2a427da2f 100644 --- a/Sources/Testing/ABI/EntryPoints/ABIEntryPoint.swift +++ b/Sources/Testing/ABI/EntryPoints/ABIEntryPoint.swift @@ -48,9 +48,11 @@ extension ABI.v0 { public static var entryPoint: EntryPoint { return { configurationJSON, recordHandler in let args = try configurationJSON.map { configurationJSON in - try JSON.decode(__CommandLineArguments_v0.self, from: configurationJSON) + try JSON.decode(__CommandLineArguments_v0.self, from: configurationJSON.bytes) + } + let eventHandler = try eventHandlerForStreamingEvents(withVersionNumber: args?.eventStreamVersionNumber, encodeAsJSONLines: false) { recordJSON in + recordJSON.withUnsafeBytes(recordHandler) } - let eventHandler = try eventHandlerForStreamingEvents(withVersionNumber: args?.eventStreamVersionNumber, encodeAsJSONLines: false, forwardingTo: recordHandler) switch await Testing.entryPoint(passing: args, eventHandler: eventHandler) { case EXIT_SUCCESS, EXIT_NO_TESTS_FOUND: diff --git a/Sources/Testing/ABI/EntryPoints/EntryPoint.swift b/Sources/Testing/ABI/EntryPoints/EntryPoint.swift index 227667d73..c1b6d3673 100644 --- a/Sources/Testing/ABI/EntryPoints/EntryPoint.swift +++ b/Sources/Testing/ABI/EntryPoints/EntryPoint.swift @@ -425,9 +425,7 @@ func parseCommandLineArguments(from args: [String]) throws -> __CommandLineArgum if let path = args.argumentValue(forLabel: "--configuration-path") ?? args.argumentValue(forLabel: "--experimental-configuration-path") { let file = try FileHandle(forReadingAtPath: path) let configurationJSON = try file.readToEnd() - result = try configurationJSON.withUnsafeBufferPointer { configurationJSON in - try JSON.decode(__CommandLineArguments_v0.self, from: .init(configurationJSON)) - } + result = try JSON.decode(__CommandLineArguments_v0.self, from: configurationJSON.span.bytes) // NOTE: We don't return early or block other arguments here: a caller is // allowed to pass a configuration AND e.g. "--verbose" and they'll both be @@ -706,7 +704,7 @@ public func configurationForEntryPoint(from args: __CommandLineArguments_v0) thr func eventHandlerForStreamingEvents( withVersionNumber versionNumber: VersionNumber?, encodeAsJSONLines: Bool, - forwardingTo targetEventHandler: @escaping @Sendable (UnsafeRawBufferPointer) -> Void + forwardingTo targetEventHandler: @escaping @Sendable (borrowing RawSpan) -> Void ) throws -> Event.Handler { let versionNumber = versionNumber ?? ABI.CurrentVersion.versionNumber guard let abi = ABI.version(forVersionNumber: versionNumber) else { diff --git a/Sources/Testing/Attachments/Attachment.swift b/Sources/Testing/Attachments/Attachment.swift index c5912992f..5971df29e 100644 --- a/Sources/Testing/Attachments/Attachment.swift +++ b/Sources/Testing/Attachments/Attachment.swift @@ -467,7 +467,7 @@ extension Attachment where AttachableValue: ~Copyable { // There should be no code path that leads to this call where the attachable // value is nil. try withUnsafeBytes { buffer in - try file!.write(buffer) + try file!.write(buffer.bytes) } return result diff --git a/Sources/Testing/Events/Event+FallbackHandler.swift b/Sources/Testing/Events/Event+FallbackHandler.swift index 57067b3fe..ad023aeca 100644 --- a/Sources/Testing/Events/Event+FallbackHandler.swift +++ b/Sources/Testing/Events/Event+FallbackHandler.swift @@ -12,9 +12,27 @@ private import _TestingInternals extension Event { #if compiler(>=6.3) && !SWT_NO_INTEROP + /// The installed fallback event handler. private static let _fallbackEventHandler: SWTFallbackEventHandler? = { _swift_testing_getFallbackEventHandler() }() + + /// Encode an event and pass it to the installed fallback event handler. + private static let _encodeAndInvoke: Event.Handler? = { [fallbackEventHandler = _fallbackEventHandler] in + guard let fallbackEventHandler else { + return nil + } + return ABI.CurrentVersion.eventHandler(encodeAsJSONLines: false) { recordJSON in + recordJSON.withUnsafeBytes { recordJSON in + fallbackEventHandler( + String(describing: ABI.CurrentVersion.versionNumber), + recordJSON.baseAddress!, + recordJSON.count, + nil + ) + } + } + }() #endif /// Post this event to the currently-installed fallback event handler. @@ -27,23 +45,12 @@ extension Event { /// `false`. borrowing func postToFallbackHandler(in context: borrowing Context) -> Bool { #if compiler(>=6.3) && !SWT_NO_INTEROP - guard let fallbackEventHandler = Self._fallbackEventHandler else { - return false - } - // Encode the event as JSON and pass it to the handler. - let encodeAndInvoke = ABI.CurrentVersion.eventHandler(encodeAsJSONLines: false) { recordJSON in - fallbackEventHandler( - String(describing: ABI.CurrentVersion.versionNumber), - recordJSON.baseAddress!, - recordJSON.count, - nil - ) + if let encodeAndInvoke = Self._encodeAndInvoke { + encodeAndInvoke(self, context) + return true } - encodeAndInvoke(self, context) - return true -#else - return false #endif + return false } } diff --git a/Sources/Testing/ExitTests/ExitTest.swift b/Sources/Testing/ExitTests/ExitTest.swift index a99060e61..a1a7e3af2 100644 --- a/Sources/Testing/ExitTests/ExitTest.swift +++ b/Sources/Testing/ExitTests/ExitTest.swift @@ -605,8 +605,8 @@ extension ExitTest { /// and standard error streams of the current process. private static func _writeBarrierValues() { let barrierValue = Self.barrierValue - try? FileHandle.stdout.write(barrierValue) - try? FileHandle.stderr.write(barrierValue) + try? FileHandle.stdout.write(barrierValue.span.bytes) + try? FileHandle.stderr.write(barrierValue.span.bytes) } /// A handler that is invoked when an exit test starts. @@ -712,13 +712,11 @@ extension ExitTest { /// The ID of the exit test to run, if any, specified in the environment. static var environmentIDForEntryPoint: ID? { - guard var idString = Environment.variable(named: Self._idEnvironmentVariableName) else { + guard let idString = Environment.variable(named: Self._idEnvironmentVariableName) else { return nil } - return try? idString.withUTF8 { idBuffer in - try JSON.decode(ExitTest.ID.self, from: UnsafeRawBufferPointer(idBuffer)) - } + return try? JSON.decode(ExitTest.ID.self, from: idString.utf8.span.bytes) } /// Find the exit test function specified in the environment of the current @@ -870,7 +868,7 @@ extension ExitTest { // Insert a specific variable that tells the child process which exit test // to run. try JSON.withEncoding(of: exitTest.id) { json in - childEnvironment[Self._idEnvironmentVariableName] = String(decoding: json, as: UTF8.self) + childEnvironment[Self._idEnvironmentVariableName] = String(decoding: Array(json), as: UTF8.self) } typealias ResultUpdater = @Sendable (inout ExitTest.Result) -> Void @@ -1007,9 +1005,7 @@ extension ExitTest { for recordJSON in bytes.split(whereSeparator: \.isASCIINewline) where !recordJSON.isEmpty { do { - try recordJSON.withUnsafeBufferPointer { recordJSON in - try Self._processRecord(.init(recordJSON), fromBackChannel: backChannel) - } + try Self._processRecord(recordJSON.span.bytes, fromBackChannel: backChannel) } catch { // NOTE: an error caught here indicates a decoding problem. // TODO: should we record these issues as systemic instead? @@ -1026,7 +1022,7 @@ extension ExitTest { /// - backChannel: The file handle that `recordJSON` was read from. /// /// - Throws: Any error encountered attempting to decode or process the JSON. - private static func _processRecord(_ recordJSON: UnsafeRawBufferPointer, fromBackChannel backChannel: borrowing FileHandle) throws { + private static func _processRecord(_ recordJSON: borrowing RawSpan, fromBackChannel backChannel: borrowing FileHandle) throws { let record = try JSON.decode(ABI.Record.self, from: recordJSON) guard case let .event(event) = record.kind else { return @@ -1093,9 +1089,7 @@ extension ExitTest { var capturedValue = capturedValue func open(_ type: T.Type) throws -> T where T: Codable & Sendable { - return try capturedValueJSON.withUnsafeBytes { capturedValueJSON in - try JSON.decode(type, from: capturedValueJSON) - } + return try JSON.decode(type, from: capturedValueJSON.span.bytes) } capturedValue.wrappedValue = try open(capturedValue.typeOfWrappedValue) @@ -1118,7 +1112,7 @@ extension ExitTest { /// This function should only be used when the process was started via the /// `__swiftPMEntryPoint()` function. The effect of using it under other /// configurations is undefined. - private borrowing func _withEncodedCapturedValuesForEntryPoint(_ body: (UnsafeRawBufferPointer) throws -> Void) throws -> Void { + private borrowing func _withEncodedCapturedValuesForEntryPoint(_ body: (borrowing RawSpan) throws -> Void) throws -> Void { for capturedValue in capturedValues { try JSON.withEncoding(of: capturedValue.wrappedValue!) { capturedValueJSON in try JSON.asJSONLine(capturedValueJSON, body) diff --git a/Sources/Testing/Support/Additions/ArrayAdditions.swift b/Sources/Testing/Support/Additions/ArrayAdditions.swift index cc46a4b9b..9259ed666 100644 --- a/Sources/Testing/Support/Additions/ArrayAdditions.swift +++ b/Sources/Testing/Support/Additions/ArrayAdditions.swift @@ -67,6 +67,52 @@ extension Array { } } +// MARK: - Span/RawSpan support + +extension Array where Element == UInt8 { + init(_ bytes: borrowing RawSpan) { + self = bytes.withUnsafeBytes { Array($0) } + } +} + +#if SWT_TARGET_OS_APPLE +extension Array { + /// The elements of this array as a span. + /// + /// This property is equivalent to the `span` property in the Swift standard + /// library, but is available on earlier Apple platforms. + /// + /// For arrays with contiguous storage, getting the value of this property is + /// an _O_(1) operation. For arrays with non-contiguous storage (i.e. bridged + /// from Objective-C), the operation may be up to _O_(_n_). + var span: Span { + @_lifetime(borrow self) + _read { + let slice = self[...] + yield slice.span + } + } +} + +extension String.UTF8View { + /// A raw span representing this string as UTF-8, not including a trailing + /// null character. + /// + /// This property is equivalent to the `span` property in the Swift standard + /// library, but is available on earlier Apple platforms. + var span: Span { + @_lifetime(borrow self) + _read { + // This implementation incurs a copy even for native Swift strings. This + // isn't currently a hot path in the testing library though. + yield ContiguousArray(self).span + } + } +} +#endif + +// MARK: - Parameter pack additions + /// Get the number of elements in a parameter pack. /// /// - Parameters: diff --git a/Sources/Testing/Support/FileHandle.swift b/Sources/Testing/Support/FileHandle.swift index 408ba2cd6..b7ae4a6cd 100644 --- a/Sources/Testing/Support/FileHandle.swift +++ b/Sources/Testing/Support/FileHandle.swift @@ -365,7 +365,7 @@ extension FileHandle { // MARK: - Writing extension FileHandle { - /// Write a sequence of bytes to this file handle. + /// Write a span of bytes to this file handle. /// /// - Parameters: /// - bytes: The bytes to write. This untyped buffer is interpreted as a @@ -376,7 +376,7 @@ extension FileHandle { /// /// - Throws: Any error that occurred while writing `bytes`. If an error /// occurs while flushing the file, it is not thrown. - func write(_ bytes: UnsafeBufferPointer, flushAfterward: Bool = true) throws { + func write(_ bytes: borrowing RawSpan, flushAfterward: Bool = true) throws { try withUnsafeCFILEHandle { file in defer { if flushAfterward { @@ -384,49 +384,18 @@ extension FileHandle { } } - let countWritten = fwrite(bytes.baseAddress!, MemoryLayout.stride, bytes.count, file) - if countWritten < bytes.count { + if bytes.isEmpty { + return + } + let countWritten = bytes.withUnsafeBytes { bytes in + fwrite(bytes.baseAddress!, MemoryLayout.stride, bytes.count, file) + } + if countWritten < bytes.byteCount { throw CError(rawValue: swt_errno()) } } } - /// Write a sequence of bytes to this file handle. - /// - /// - Parameters: - /// - bytes: The bytes to write. - /// - flushAfterward: Whether or not to flush the file (with `fflush()`) - /// after writing. If `true`, `fflush()` is called even if an error - /// occurred while writing. - /// - /// - Throws: Any error that occurred while writing `bytes`. If an error - /// occurs while flushing the file, it is not thrown. - /// - /// - Precondition: `bytes` must provide contiguous storage. - func write(_ bytes: some Sequence, flushAfterward: Bool = true) throws { - let hasContiguousStorage: Void? = try bytes.withContiguousStorageIfAvailable { bytes in - try write(bytes, flushAfterward: flushAfterward) - } - precondition(hasContiguousStorage != nil, "byte sequence must provide contiguous storage: \(bytes)") - } - - /// Write a sequence of bytes to this file handle. - /// - /// - Parameters: - /// - bytes: The bytes to write. This untyped buffer is interpreted as a - /// sequence of `UInt8` values. - /// - flushAfterward: Whether or not to flush the file (with `fflush()`) - /// after writing. If `true`, `fflush()` is called even if an error - /// occurred while writing. - /// - /// - Throws: Any error that occurred while writing `bytes`. If an error - /// occurs while flushing the file, it is not thrown. - func write(_ bytes: UnsafeRawBufferPointer, flushAfterward: Bool = true) throws { - try bytes.withMemoryRebound(to: UInt8.self) { bytes in - try write(bytes, flushAfterward: flushAfterward) - } - } - /// Write a string to this file handle. /// /// - Parameters: @@ -441,19 +410,7 @@ extension FileHandle { /// `string` is converted to a UTF-8 C string (UTF-16 on Windows) and written /// to this file handle. func write(_ string: String, flushAfterward: Bool = true) throws { - try withUnsafeCFILEHandle { file in - defer { - if flushAfterward { - _ = fflush(file) - } - } - - try string.withCString { string in - if EOF == fputs(string, file) { - throw CError(rawValue: swt_errno()) - } - } - } + try write(string.utf8.span.bytes, flushAfterward: flushAfterward) } } diff --git a/Sources/Testing/Support/JSON.swift b/Sources/Testing/Support/JSON.swift index 3d656687f..59e501ba1 100644 --- a/Sources/Testing/Support/JSON.swift +++ b/Sources/Testing/Support/JSON.swift @@ -29,7 +29,7 @@ enum JSON { /// - Returns: Whatever is returned by `body`. /// /// - Throws: Whatever is thrown by `body` or by the encoding process. - static func withEncoding(of value: some Encodable, userInfo: [CodingUserInfoKey: any Sendable] = [:], _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + static func withEncoding(of value: some Encodable, userInfo: [CodingUserInfoKey: any Sendable] = [:], _ body: (borrowing RawSpan) throws -> R) throws -> R { #if canImport(Foundation) let encoder = JSONEncoder() @@ -44,7 +44,10 @@ enum JSON { encoder.userInfo.merge(userInfo, uniquingKeysWith: { _, rhs in rhs}) let data = try encoder.encode(value) - return try data.withUnsafeBytes(body) + // WORKAROUND for older SDK on swift-ci (rdar://169480914) + return try data.withUnsafeBytes { data in + try body(data.bytes) + } #else throw SystemError(description: "JSON encoding requires Foundation which is not available in this environment.") #endif @@ -60,14 +63,17 @@ enum JSON { /// - Returns: Whatever is returned by `body`. /// /// - Throws: Whatever is thrown by `body`. - static func asJSONLine(_ json: UnsafeRawBufferPointer, _ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { - if _slowPath(json.contains(where: \.isASCIINewline)) { + static func asJSONLine(_ json: borrowing RawSpan, _ body: (borrowing RawSpan) throws -> R) rethrows -> R { + let containsASCIINewline = json.withUnsafeBytes { json in + json.contains(where: \.isASCIINewline) + } + if _slowPath(containsASCIINewline) { // Remove the newline characters to conform to JSON lines specification. // This is not actually expected to happen in practice with Foundation's // JSON encoder. var json = Array(json) json.removeAll(where: \.isASCIINewline) - return try json.withUnsafeBytes(body) + return try body(json.span.bytes) } else { // No newlines found, no need to copy the buffer. return try body(json) @@ -83,20 +89,22 @@ enum JSON { /// - Returns: An instance of `T` decoded from `jsonRepresentation`. /// /// - Throws: Whatever is thrown by the decoding process. - static func decode(_ type: T.Type, from jsonRepresentation: UnsafeRawBufferPointer) throws -> T where T: Decodable { + static func decode(_ type: T.Type, from jsonRepresentation: borrowing RawSpan) throws -> T where T: Decodable { #if canImport(Foundation) try withExtendedLifetime(jsonRepresentation) { - let byteCount = jsonRepresentation.count - let data = if byteCount > 0 { - Data( - bytesNoCopy: .init(mutating: jsonRepresentation.baseAddress!), - count: byteCount, - deallocator: .none - ) - } else { - Data() + try jsonRepresentation.withUnsafeBytes { jsonRepresentation in + let byteCount = jsonRepresentation.count + let data = if byteCount > 0 { + Data( + bytesNoCopy: .init(mutating: jsonRepresentation.baseAddress!), + count: byteCount, + deallocator: .none + ) + } else { + Data() + } + return try JSONDecoder().decode(type, from: data) } - return try JSONDecoder().decode(type, from: data) } #else throw SystemError(description: "JSON decoding requires Foundation which is not available in this environment.") diff --git a/Sources/Testing/Traits/Tags/Tag.Color+Loading.swift b/Sources/Testing/Traits/Tags/Tag.Color+Loading.swift index 2ab35b107..2323e82ca 100644 --- a/Sources/Testing/Traits/Tags/Tag.Color+Loading.swift +++ b/Sources/Testing/Traits/Tags/Tag.Color+Loading.swift @@ -113,9 +113,7 @@ func loadTagColors(fromFileInDirectoryAtPath swiftTestingDirectoryPath: String? // nil is a valid decoded color value (representing "no color") that we can // use for merging tag color data from multiple sources, but it is not valid // as an actual tag color, so we have a step here that filters it. - return try tagColorsData.withUnsafeBytes { tagColorsData in - try JSON.decode([Tag: Tag.Color?].self, from: tagColorsData) - .compactMapValues { $0 } - } + return try JSON.decode([Tag: Tag.Color?].self, from: tagColorsData.span.bytes) + .compactMapValues { $0 } } #endif diff --git a/Tests/TestingTests/ABIEntryPointTests.swift b/Tests/TestingTests/ABIEntryPointTests.swift index a50f92afa..470eb03fa 100644 --- a/Tests/TestingTests/ABIEntryPointTests.swift +++ b/Tests/TestingTests/ABIEntryPointTests.swift @@ -65,7 +65,7 @@ struct ABIEntryPointTests { private func _invokeEntryPointV0( passing arguments: __CommandLineArguments_v0, - recordHandler: @escaping @Sendable (_ recordJSON: UnsafeRawBufferPointer) -> Void = { _ in } + recordHandler: @escaping @Sendable (_ recordJSON: borrowing RawSpan) -> Void = { _ in } ) async throws -> Bool { #if !(os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android)) && !SWT_NO_DYNAMIC_LINKING // Get the ABI entry point by dynamically looking it up at runtime. @@ -84,23 +84,27 @@ struct ABIEntryPointTests { let abiEntryPoint = unsafeBitCast(abiv0_getEntryPoint(), to: ABI.v0.EntryPoint.self) let argumentsJSON = try JSON.withEncoding(of: arguments) { argumentsJSON in - let result = UnsafeMutableRawBufferPointer.allocate(byteCount: argumentsJSON.count, alignment: 1) - result.copyMemory(from: argumentsJSON) - return result + argumentsJSON.withUnsafeBytes { argumentsJSON in + let result = UnsafeMutableRawBufferPointer.allocate(byteCount: argumentsJSON.count, alignment: 1) + result.copyMemory(from: argumentsJSON) + return result + } } defer { argumentsJSON.deallocate() } // Call the entry point function. - return try await abiEntryPoint(.init(argumentsJSON), recordHandler) + return try await abiEntryPoint(.init(argumentsJSON)) { recordJSON in + recordHandler(recordJSON.bytes) + } } #if canImport(Foundation) @Test func decodeEmptyConfiguration() throws { let emptyBuffer = UnsafeRawBufferPointer(start: nil, count: 0) #expect(throws: DecodingError.self) { - _ = try JSON.decode(__CommandLineArguments_v0.self, from: emptyBuffer) + _ = try JSON.decode(__CommandLineArguments_v0.self, from: emptyBuffer.bytes) } } diff --git a/Tests/TestingTests/Support/FileHandleTests.swift b/Tests/TestingTests/Support/FileHandleTests.swift index fd52678d7..22488afe0 100644 --- a/Tests/TestingTests/Support/FileHandleTests.swift +++ b/Tests/TestingTests/Support/FileHandleTests.swift @@ -77,21 +77,11 @@ struct FileHandleTests { func canWrite() throws { try withTemporaryPath { path in let fileHandle = try FileHandle(forWritingAtPath: path) - try fileHandle.write([0, 1, 2, 3, 4, 5]) + try fileHandle.write([0, 1, 2, 3, 4, 5].span.bytes) try fileHandle.write("Hello world!") } } -#if !SWT_NO_EXIT_TESTS - @Test("Writing requires contiguous storage") - func writeIsContiguous() async { - await #expect(processExitsWith: .failure) { - let fileHandle = try FileHandle.null(mode: "wb") - try fileHandle.write([1, 2, 3, 4, 5].lazy.filter { $0 == 1 }) - } - } -#endif - @Test("Can read from a file") func canRead() throws { let bytes: [UInt8] = (0 ..< 8192).map { _ in @@ -100,7 +90,7 @@ struct FileHandleTests { try withTemporaryPath { path in do { let fileHandle = try FileHandle(forWritingAtPath: path) - try fileHandle.write(bytes) + try fileHandle.write(bytes.span.bytes) } let fileHandle = try FileHandle(forReadingAtPath: path) let bytes2 = try fileHandle.readToEnd() @@ -112,7 +102,7 @@ struct FileHandleTests { func cannotWriteBytesToReadOnlyFile() throws { let fileHandle = try FileHandle.null(mode: "rb") #expect(throws: CError.self) { - try fileHandle.write([0, 1, 2, 3, 4, 5]) + try fileHandle.write([0, 1, 2, 3, 4, 5].span.bytes) } } diff --git a/Tests/TestingTests/SwiftPMTests.swift b/Tests/TestingTests/SwiftPMTests.swift index 44877a1df..375654ab7 100644 --- a/Tests/TestingTests/SwiftPMTests.swift +++ b/Tests/TestingTests/SwiftPMTests.swift @@ -26,9 +26,7 @@ private func decodedEventStreamRecords(fromPath filePath: String try FileHandle(forReadingAtPath: filePath).readToEnd() .split(whereSeparator: \.isASCIINewline) .map { line in - try line.withUnsafeBytes { line in - return try JSON.decode(ABI.Record.self, from: line) - } + try JSON.decode(ABI.Record.self, from: line.span.bytes) } } diff --git a/Tests/TestingTests/Test.Case.Argument.IDTests.swift b/Tests/TestingTests/Test.Case.Argument.IDTests.swift index 052213912..882f3e70a 100644 --- a/Tests/TestingTests/Test.Case.Argument.IDTests.swift +++ b/Tests/TestingTests/Test.Case.Argument.IDTests.swift @@ -38,9 +38,7 @@ struct Test_Case_Argument_IDTests { #expect(arguments.count == 1) let argument = try #require(arguments.first) #if canImport(Foundation) - let decodedArgument = try argument.id.bytes.withUnsafeBufferPointer { argumentID in - try JSON.decode(MyCustomTestArgument.self, from: .init(argumentID)) - } + let decodedArgument = try JSON.decode(MyCustomTestArgument.self, from: argument.id.bytes.span.bytes) #expect(decodedArgument == MyCustomTestArgument(x: 123, y: "abc")) #endif } diff --git a/Tests/TestingTests/Test.CaseTests.swift b/Tests/TestingTests/Test.CaseTests.swift index f9e955fe9..942291066 100644 --- a/Tests/TestingTests/Test.CaseTests.swift +++ b/Tests/TestingTests/Test.CaseTests.swift @@ -74,7 +74,7 @@ struct Test_CaseTests { {"bytes": [1]} ]} """.utf8) - let testCaseID = try JSON.decode(Test.Case.ID.self, from: encodedData) + let testCaseID = try JSON.decode(Test.Case.ID.self, from: encodedData.bytes) #expect(testCaseID.isStable) let argumentIDs = try #require(testCaseID.argumentIDs) @@ -83,7 +83,7 @@ struct Test_CaseTests { @Test func legacyDecoding_nonStable() throws { let encodedData = Data("{}".utf8) - let testCaseID = try JSON.decode(Test.Case.ID.self, from: encodedData) + let testCaseID = try JSON.decode(Test.Case.ID.self, from: encodedData.bytes) #expect(!testCaseID.isStable) let argumentIDs = try #require(testCaseID.argumentIDs) @@ -92,7 +92,7 @@ struct Test_CaseTests { @Test func legacyDecoding_nonParameterized() throws { let encodedData = Data(#"{"argumentIDs": []}"#.utf8) - let testCaseID = try JSON.decode(Test.Case.ID.self, from: encodedData) + let testCaseID = try JSON.decode(Test.Case.ID.self, from: encodedData.bytes) #expect(testCaseID.isStable) #expect(testCaseID.argumentIDs == nil) #expect(testCaseID.discriminator == nil) @@ -100,7 +100,7 @@ struct Test_CaseTests { @Test func newDecoding_nonParameterized() throws { let encodedData = Data(#"{"isStable": true}"#.utf8) - let testCaseID = try JSON.decode(Test.Case.ID.self, from: encodedData) + let testCaseID = try JSON.decode(Test.Case.ID.self, from: encodedData.bytes) #expect(testCaseID.isStable) #expect(testCaseID.argumentIDs == nil) #expect(testCaseID.discriminator == nil) @@ -116,7 +116,7 @@ struct Test_CaseTests { "discriminator": 0 } """.utf8) - let testCaseID = try JSON.decode(Test.Case.ID.self, from: encodedData) + let testCaseID = try JSON.decode(Test.Case.ID.self, from: encodedData.bytes) #expect(testCaseID.isStable) #expect(testCaseID.argumentIDs?.count == 1) #expect(testCaseID.discriminator == 0) diff --git a/Tests/TestingTests/TestSupport/TestingAdditions.swift b/Tests/TestingTests/TestSupport/TestingAdditions.swift index ca3a660aa..9ea2fab3e 100644 --- a/Tests/TestingTests/TestSupport/TestingAdditions.swift +++ b/Tests/TestingTests/TestSupport/TestingAdditions.swift @@ -412,24 +412,6 @@ extension JSON { try JSON.decode(T.self, from: data) } } - -#if canImport(Foundation) - /// Decode a value from JSON data. - /// - /// - Parameters: - /// - type: The type of value to decode. - /// - jsonRepresentation: Data of the JSON encoding of the value to decode. - /// - /// - Returns: An instance of `T` decoded from `jsonRepresentation`. - /// - /// - Throws: Whatever is thrown by the decoding process. - @_disfavoredOverload - static func decode(_ type: T.Type, from jsonRepresentation: Data) throws -> T where T: Decodable { - try jsonRepresentation.withUnsafeBytes { bytes in - try JSON.decode(type, from: bytes) - } - } -#endif } @available(_clockAPI, *) diff --git a/Tests/TestingTests/Traits/TagListTests.swift b/Tests/TestingTests/Traits/TagListTests.swift index 81cba285c..aaaba3b46 100644 --- a/Tests/TestingTests/Traits/TagListTests.swift +++ b/Tests/TestingTests/Traits/TagListTests.swift @@ -127,7 +127,7 @@ struct TagListTests { func tagColorsReadFromDisk() throws { let tempDirPath = try temporaryDirectory() let jsonPath = appendPathComponent("tag-colors.json", to: tempDirPath) - var jsonContent = """ + let jsonContent = """ { "alpha": "red", "beta": "#00CCFF", @@ -142,7 +142,7 @@ struct TagListTests { "encode purple": "purple" } """ - try jsonContent.withUTF8 { jsonContent in + do { let fileHandle = try FileHandle(forWritingAtPath: jsonPath) try fileHandle.write(jsonContent) } @@ -173,11 +173,8 @@ struct TagListTests { @Test("Invalid tag color decoding", arguments: [##""#NOTHEX""##, #""garbageColorName""#]) func noTagColorsReadFromBadPath(tagColorJSON: String) throws { - var tagColorJSON = tagColorJSON - tagColorJSON.withUTF8 { tagColorJSON in - _ = #expect(throws: (any Error).self) { - _ = try JSON.decode(Tag.Color.self, from: .init(tagColorJSON)) - } + _ = #expect(throws: (any Error).self) { + _ = try JSON.decode(Tag.Color.self, from: tagColorJSON.utf8.span.bytes) } } #endif