Skip to content
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

[Experimental] Implement JSON coding without using Foundation or Codable. #1024

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

#if canImport(Foundation)
#if !SWT_NO_FOUNDATION && canImport(Foundation)
@_spi(Experimental) public import Testing
public import Foundation

Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

#if canImport(Foundation)
#if !SWT_NO_FOUNDATION && canImport(Foundation)
@_spi(Experimental) public import Testing
private import Foundation

Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

#if canImport(Foundation)
#if !SWT_NO_FOUNDATION && canImport(Foundation)
@_spi(Experimental) public import Testing
public import Foundation

Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

#if canImport(Foundation)
#if !SWT_NO_FOUNDATION && canImport(Foundation)
@_spi(Experimental) public import Testing
public import Foundation

Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

#if canImport(Foundation)
#if !SWT_NO_FOUNDATION && canImport(Foundation)
@_spi(Experimental) public import Testing
public import Foundation

Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

#if canImport(Foundation)
#if !SWT_NO_FOUNDATION && canImport(Foundation)
@_spi(Experimental) import Testing
import Foundation

Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

#if canImport(Foundation)
#if !SWT_NO_FOUNDATION && canImport(Foundation)
@_spi(Experimental) public import Testing
public import Foundation

Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

#if canImport(Foundation) && !SWT_NO_UTC_CLOCK
#if !SWT_NO_FOUNDATION && canImport(Foundation) && !SWT_NO_UTC_CLOCK
@_spi(Experimental) @_spi(ForToolsIntegrationOnly) public import Testing
public import Foundation

12 changes: 3 additions & 9 deletions Sources/Testing/ABI/ABI.Record+Streaming.swift
Original file line number Diff line number Diff line change
@@ -8,9 +8,6 @@
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

#if canImport(Foundation) && (!SWT_NO_FILE_IO || !SWT_NO_ABI_ENTRY_POINT)
private import Foundation

extension ABI.Version {
/// Post-process encoded JSON and write it to a file.
///
@@ -58,13 +55,13 @@ extension ABI.Version {
let humanReadableOutputRecorder = Event.HumanReadableOutputRecorder()
return { [eventHandler = eventHandlerCopy] event, context in
if case .testDiscovered = event.kind, let test = context.test {
try? JSON.withEncoding(of: ABI.Record<Self>(encoding: test)) { testJSON in
JSON.withEncoding(of: ABI.Record<Self>(encoding: test)) { testJSON in
eventHandler(testJSON)
}
} else {
let messages = humanReadableOutputRecorder.record(event, in: context, verbosity: 0)
if let eventRecord = ABI.Record<Self>(encoding: event, in: context, messages: messages) {
try? JSON.withEncoding(of: eventRecord, eventHandler)
JSON.withEncoding(of: eventRecord, eventHandler)
}
}
}
@@ -96,12 +93,9 @@ extension ABI.Xcode16 {
eventContext: Event.Context.Snapshot(snapshotting: context)
)
try? JSON.withEncoding(of: snapshot) { eventAndContextJSON in
eventAndContextJSON.withUnsafeBytes { eventAndContextJSON in
eventHandler(eventAndContextJSON)
}
eventHandler(eventAndContextJSON)
}
}
}
}
#endif
#endif
36 changes: 21 additions & 15 deletions Sources/Testing/ABI/ABI.Record.swift
Original file line number Diff line number Diff line change
@@ -41,28 +41,15 @@ extension ABI {
}
}

// MARK: - Codable
// MARK: - Decodable

extension ABI.Record: Codable {
extension ABI.Record: Decodable {
private enum CodingKeys: String, CodingKey {
case version
case kind
case payload
}

func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(V.versionNumber, forKey: .version)
switch kind {
case let .test(test):
try container.encode("test", forKey: .kind)
try container.encode(test, forKey: .payload)
case let .event(event):
try container.encode("event", forKey: .kind)
try container.encode(event, forKey: .payload)
}
}

init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

@@ -93,3 +80,22 @@ extension ABI.Record: Codable {
}
}
}

extension ABI.Record: JSON.Serializable {
func makeJSONValue() -> JSON.Value {
var dict = [
"version": V.versionNumber.makeJSONValue()
]

switch kind {
case let .test(test):
dict["kind"] = "test".makeJSONValue()
dict["payload"] = test.makeJSONValue()
case let .event(event):
dict["kind"] = "event".makeJSONValue()
dict["payload"] = event.makeJSONValue()
}

return dict.makeJSONValue()
}
}
6 changes: 4 additions & 2 deletions Sources/Testing/ABI/ABI.swift
Original file line number Diff line number Diff line change
@@ -20,7 +20,6 @@ extension ABI {
/// The numeric representation of this ABI version.
static var versionNumber: Int { get }

#if canImport(Foundation) && (!SWT_NO_FILE_IO || !SWT_NO_ABI_ENTRY_POINT)
/// Create an event handler that encodes events as JSON and forwards them to
/// an ABI-friendly event handler.
///
@@ -39,7 +38,6 @@ extension ABI {
encodeAsJSONLines: Bool,
forwardingTo eventHandler: @escaping @Sendable (_ recordJSON: UnsafeRawBufferPointer) -> Void
) -> Event.Handler
#endif
}

/// The current supported ABI version (ignoring any experimental versions.)
@@ -50,6 +48,10 @@ extension ABI {

extension ABI {
#if !SWT_NO_SNAPSHOT_TYPES
#if SWT_NO_FOUNDATION || !canImport(Foundation)
#error("Platform-specific misconfiguration: Foundation is required for snapshot type support")
#endif

/// A namespace and version type for Xcode&nbsp;16 compatibility.
///
/// - Warning: This type will be removed in a future update.
16 changes: 14 additions & 2 deletions Sources/Testing/ABI/Encoded/ABI.EncodedAttachment.swift
Original file line number Diff line number Diff line change
@@ -27,6 +27,18 @@ extension ABI {
}
}

// MARK: - Codable
// MARK: - Decodable

extension ABI.EncodedAttachment: Codable {}
extension ABI.EncodedAttachment: Decodable {}

// MARK: - JSON.Serializable

extension ABI.EncodedAttachment: JSON.Serializable {
func makeJSONValue() -> JSON.Value {
var dict = [String: JSON.Value]()
if let path {
dict["path"] = path.makeJSONValue()
}
return dict.makeJSONValue()
}
}
33 changes: 27 additions & 6 deletions Sources/Testing/ABI/Encoded/ABI.EncodedBacktrace.swift
Original file line number Diff line number Diff line change
@@ -31,14 +31,35 @@ extension ABI {
}
}

// MARK: - Codable

extension ABI.EncodedBacktrace: Codable {
func encode(to encoder: any Encoder) throws {
try symbolicatedAddresses.encode(to: encoder)
}
// MARK: - Decodable

extension ABI.EncodedBacktrace: Decodable {
init(from decoder: any Decoder) throws {
self.symbolicatedAddresses = try [Backtrace.SymbolicatedAddress](from: decoder)
}
}

// MARK: - JSON.Serializable

extension ABI.EncodedBacktrace: JSON.Serializable {
func makeJSONValue() -> JSON.Value {
symbolicatedAddresses.makeJSONValue()
}
}

extension Backtrace.SymbolicatedAddress: JSON.Serializable {
func makeJSONValue() -> JSON.Value {
var dict = [
"address": address.makeJSONValue()
]

if let offset {
dict["offset"] = offset.makeJSONValue()
}
if let symbolName {
dict["symbolName"] = symbolName.makeJSONValue()
}

return dict.makeJSONValue()
}
}
17 changes: 15 additions & 2 deletions Sources/Testing/ABI/Encoded/ABI.EncodedError.swift
Original file line number Diff line number Diff line change
@@ -54,9 +54,22 @@ extension ABI.EncodedError: Error {
}
}

// MARK: - Codable
// MARK: - Decodable

extension ABI.EncodedError: Codable {}
extension ABI.EncodedError: Decodable {}

// MARK: - JSON.Serializable

extension ABI.EncodedError: JSON.Serializable {
func makeJSONValue() -> JSON.Value {
let dict = [
"description": description.makeJSONValue(),
"domain": domain.makeJSONValue(),
"code": code.makeJSONValue()
]
return dict.makeJSONValue()
}
}

// MARK: - CustomTestStringConvertible

35 changes: 32 additions & 3 deletions Sources/Testing/ABI/Encoded/ABI.EncodedEvent.swift
Original file line number Diff line number Diff line change
@@ -107,7 +107,36 @@ extension ABI {
}
}

// MARK: - Codable
// MARK: - Decodable

extension ABI.EncodedEvent: Codable {}
extension ABI.EncodedEvent.Kind: Codable {}
extension ABI.EncodedEvent: Decodable {}
extension ABI.EncodedEvent.Kind: Decodable {}

// MARK: - JSON.Serializable

extension ABI.EncodedEvent: JSON.Serializable {
func makeJSONValue() -> JSON.Value {
var dict = [
"kind": kind.makeJSONValue(),
"instant": instant.makeJSONValue(),
"messages": messages.makeJSONValue(),
]

if let issue {
dict["issue"] = issue.makeJSONValue()
}
if let _attachment {
dict["_attachment"] = _attachment.makeJSONValue()
}
if let testID {
dict["testID"] = testID.makeJSONValue()
}
if let _testCase {
dict["_testCase"] = _testCase.makeJSONValue()
}

return dict.makeJSONValue()
}
}

extension ABI.EncodedEvent.Kind: JSON.Serializable {}
16 changes: 14 additions & 2 deletions Sources/Testing/ABI/Encoded/ABI.EncodedInstant.swift
Original file line number Diff line number Diff line change
@@ -35,6 +35,18 @@ extension ABI {
}
}

// MARK: - Codable
// MARK: - Decodable

extension ABI.EncodedInstant: Codable {}
extension ABI.EncodedInstant: Decodable {}

// MARK: - JSON.Serializable

extension ABI.EncodedInstant: JSON.Serializable {
func makeJSONValue() -> JSON.Value {
let dict = [
"absolute": absolute.makeJSONValue(),
"since1970": since1970.makeJSONValue()
]
return dict.makeJSONValue()
}
}
37 changes: 32 additions & 5 deletions Sources/Testing/ABI/Encoded/ABI.EncodedIssue.swift
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ extension ABI {
var isKnown: Bool

/// The location in source where this issue occurred, if available.
var sourceLocation: SourceLocation?
var sourceLocation: EncodedSourceLocation<V>?

/// The backtrace where this issue occurred, if available.
///
@@ -51,7 +51,9 @@ extension ABI {
case .error: .error
}
isKnown = issue.isKnown
sourceLocation = issue.sourceLocation
if let sourceLocation = issue.sourceLocation {
self.sourceLocation = EncodedSourceLocation<V>(encoding: sourceLocation)
}
if let backtrace = issue.sourceContext.backtrace {
_backtrace = EncodedBacktrace(encoding: backtrace, in: eventContext)
}
@@ -62,7 +64,32 @@ extension ABI {
}
}

// MARK: - Codable
// MARK: - Decodable

extension ABI.EncodedIssue: Decodable {}
extension ABI.EncodedIssue.Severity: Decodable {}

// MARK: - JSON.Serializable

extension ABI.EncodedIssue: JSON.Serializable {
func makeJSONValue() -> JSON.Value {
var dict = [
"_severity": _severity.makeJSONValue(),
"isKnown": isKnown.makeJSONValue()
]

if let sourceLocation {
dict["sourceLocation"] = sourceLocation.makeJSONValue()
}
if let _backtrace {
dict["_backtrace"] = _backtrace.makeJSONValue()
}
if let _error {
dict["_error"] = _error.makeJSONValue()
}

return dict.makeJSONValue()
}
}

extension ABI.EncodedIssue: Codable {}
extension ABI.EncodedIssue.Severity: Codable {}
extension ABI.EncodedIssue.Severity: JSON.Serializable {}
Loading