From 27afe7f0e98266c61f15fe4280f519c4bc9b0022 Mon Sep 17 00:00:00 2001 From: Adora Lynch Date: Fri, 11 Oct 2024 13:37:10 -0400 Subject: [PATCH] Adress Swiftlint errors --- Sources/Yams/Anchor.swift | 10 +- Sources/Yams/Constructor.swift | 18 +- Sources/Yams/Decoder.swift | 14 +- Sources/Yams/Emitter.swift | 18 +- Sources/Yams/Encoder.swift | 5 +- Sources/Yams/Node.Alias.swift | 4 +- Sources/Yams/Node.Mapping.swift | 6 +- Sources/Yams/Node.Scalar.swift | 6 +- Sources/Yams/Node.Sequence.swift | 6 +- Sources/Yams/Node.swift | 21 +- Sources/Yams/Parser.swift | 20 +- Sources/Yams/RedundancyAliasingStrategy.swift | 35 +- Sources/Yams/YamlAnchorProviding.swift | 10 +- Sources/Yams/YamlTagProviding.swift | 8 +- Tests/YamsTests/AnchorCodingTests.swift | 332 ++++++++++-------- Tests/YamsTests/AnchorTolerancesTests.swift | 123 +++---- Tests/YamsTests/EncoderTests.swift | 9 +- Tests/YamsTests/TagCodingTests.swift | 234 ++++++------ Tests/YamsTests/TagTolerancesTests.swift | 81 ++--- 19 files changed, 515 insertions(+), 445 deletions(-) diff --git a/Sources/Yams/Anchor.swift b/Sources/Yams/Anchor.swift index 6e63d256..30294ad5 100644 --- a/Sources/Yams/Anchor.swift +++ b/Sources/Yams/Anchor.swift @@ -8,23 +8,22 @@ import Foundation public final class Anchor: RawRepresentable, ExpressibleByStringLiteral, Codable, Hashable { - + public static let permittedCharacters = CharacterSet.lowercaseLetters .union(.uppercaseLetters) .union(.decimalDigits) .union(.init(charactersIn: "-_")) - + public static func is_cyamlAlpha(_ string: String) -> Bool { Anchor.permittedCharacters.isSuperset(of: .init(charactersIn: string)) } - public let rawValue: String - + public init(rawValue: String) { self.rawValue = rawValue } - + public init(stringLiteral value: String) { rawValue = value } @@ -33,4 +32,3 @@ public final class Anchor: RawRepresentable, ExpressibleByStringLiteral, Codable extension Anchor: CustomStringConvertible { public var description: String { rawValue } } - diff --git a/Sources/Yams/Constructor.swift b/Sources/Yams/Constructor.swift index e93ed2c5..ae56777b 100644 --- a/Sources/Yams/Constructor.swift +++ b/Sources/Yams/Constructor.swift @@ -48,13 +48,13 @@ public final class Constructor { if let method = mappingMap[node.tag.name], let result = method(mapping) { return result } - return [AnyHashable: Any]._construct_mapping(from: mapping) + return [AnyHashable: Any].private_construct_mapping(from: mapping) case .sequence(let sequence): if let method = sequenceMap[node.tag.name], let result = method(sequence) { return result } return [Any].construct_seq(from: sequence) - case .alias(_): + case .alias: preconditionFailure("Aliases should be resolved before construction") } } @@ -272,7 +272,7 @@ extension ScalarConstructible where Self: FloatingPoint & SexagesimalConvertible } private extension FixedWidthInteger where Self: SexagesimalConvertible { - static func _construct(from scalar: Node.Scalar) -> Self? { + static func private_construct(from scalar: Node.Scalar) -> Self? { guard scalar.style == .any || scalar.style == .plain else { return nil } @@ -317,7 +317,7 @@ extension Int: ScalarConstructible { /// /// - returns: An instance of `Int`, if one was successfully extracted from the scalar. public static func construct(from scalar: Node.Scalar) -> Int? { - return _construct(from: scalar) + return private_construct(from: scalar) } } @@ -330,7 +330,7 @@ extension UInt: ScalarConstructible { /// /// - returns: An instance of `UInt`, if one was successfully extracted from the scalar. public static func construct(from scalar: Node.Scalar) -> UInt? { - return _construct(from: scalar) + return private_construct(from: scalar) } } @@ -343,7 +343,7 @@ extension Int64: ScalarConstructible { /// /// - returns: An instance of `Int64`, if one was successfully extracted from the scalar. public static func construct(from scalar: Node.Scalar) -> Int64? { - return _construct(from: scalar) + return private_construct(from: scalar) } } @@ -356,7 +356,7 @@ extension UInt64: ScalarConstructible { /// /// - returns: An instance of `UInt64`, if one was successfully extracted from the scalar. public static func construct(from scalar: Node.Scalar) -> UInt64? { - return _construct(from: scalar) + return private_construct(from: scalar) } } @@ -420,12 +420,12 @@ extension Dictionary { /// /// - returns: An instance of `[AnyHashable: Any]`, if one was successfully extracted from the mapping. public static func construct_mapping(from mapping: Node.Mapping) -> [AnyHashable: Any]? { - return _construct_mapping(from: mapping) + return private_construct_mapping(from: mapping) } } private extension Dictionary { - static func _construct_mapping(from mapping: Node.Mapping) -> [AnyHashable: Any] { + static func private_construct_mapping(from mapping: Node.Mapping) -> [AnyHashable: Any] { let mapping = mapping.flatten() // TODO: YAML supports keys other than str. return [AnyHashable: Any]( diff --git a/Sources/Yams/Decoder.swift b/Sources/Yams/Decoder.swift index ffd294b8..859c4583 100644 --- a/Sources/Yams/Decoder.swift +++ b/Sources/Yams/Decoder.swift @@ -51,7 +51,7 @@ public class YAMLDecoder { let parser = try Parser(yaml: yaml, resolver: Resolver([.merge]), encoding: encoding) // ^ the parser holds the references to Anchors while parsing, return try withExtendedLifetime(parser) { - //^ so we hold an explicit reference to the parser during decoding + // ^ so we hold an explicit reference to the parser during decoding let node = try parser.singleRoot() ?? "" // ^ nodes only have weak references to Anchors (the Anchors would disappear if not held by the parser) return try self.decode(type, from: node, userInfo: userInfo) @@ -151,24 +151,24 @@ private struct _KeyedDecodingContainer: KeyedDecodingContainerPr init(decoder: _Decoder, wrapping mapping: Node.Mapping) { self.decoder = decoder - + let keys = mapping.keys - + let decodeAnchor: Anchor? let decodeTag: Tag? - + if let anchor = mapping.anchor, keys.contains(.anchorKeyNode) == false { decodeAnchor = anchor } else { decodeAnchor = nil } - + if mapping.tag.name != .implicit && keys.contains(.tagKeyNode) == false { decodeTag = mapping.tag } else { decodeTag = nil } - + switch (decodeAnchor, decodeTag) { case (nil, nil): self.mapping = mapping @@ -426,3 +426,5 @@ extension YAMLDecoder: TopLevelDecoder { } } #endif + +// swiftlint:disable:this file_length diff --git a/Sources/Yams/Emitter.swift b/Sources/Yams/Emitter.swift index 565e1162..295a9d40 100644 --- a/Sources/Yams/Emitter.swift +++ b/Sources/Yams/Emitter.swift @@ -271,10 +271,10 @@ public final class Emitter { /// Set the style for scalars that include newlines public var newLineScalarStyle: Node.Scalar.Style = .any - + /// Redundancy aliasing strategy to use when encoding. Defaults to nil public var redundancyAliasingStrategy: RedundancyAliasingStrategy? - + /// Create `Emitter.Options` with the specified values. /// /// - parameter canonical: Set if the output should be in the "canonical" format described in the YAML @@ -290,7 +290,8 @@ public final class Emitter { /// - parameter sequenceStyle: Set the style for sequences (arrays / lists) /// - parameter mappingStyle: Set the style for mappings (dictionaries) /// - parameter newLineScalarStyle: Set the style for newline-containing scalars - /// - parameter redundancyAliasingStrategy: Set the strategy for identifying redundant structures and automatically aliasing them + /// - parameter redundancyAliasingStrategy: Set the strategy for identifying + /// redundant structures and automatically aliasing them public init(canonical: Bool = false, indent: Int = 0, width: Int = 0, allowUnicode: Bool = false, lineBreak: Emitter.LineBreak = .ln, explicitStart: Bool = false, @@ -338,7 +339,8 @@ public final class Emitter { /// - parameter sequenceStyle: Set the style for sequences (arrays / lists) /// - parameter mappingStyle: Set the style for mappings (dictionaries) /// - parameter newLineScalarStyle: Set the style for newline-containing scalars - /// - parameter redundancyAliasingStrategy: Set the strategy for identifying redundant structures and automatically aliasing them + /// - parameter redundancyAliasingStrategy: Set the strategy for identifying redundant + /// structures and automatically aliasing them public init(canonical: Bool = false, indent: Int = 0, width: Int = 0, @@ -466,12 +468,6 @@ public final class Emitter { } } -//// MARK: - Options Initializer - -extension Emitter.Options { - -} - // MARK: Implementation Details extension Emitter { @@ -489,7 +485,7 @@ extension Emitter { case .alias(let alias): try serializeAlias(alias) } } - + private func serializeAlias(_ alias: Node.Alias) throws { var event = yaml_event_t() let anchor = alias.anchor.rawValue diff --git a/Sources/Yams/Encoder.swift b/Sources/Yams/Encoder.swift index 39b58ac3..b648ba09 100644 --- a/Sources/Yams/Encoder.swift +++ b/Sources/Yams/Encoder.swift @@ -242,7 +242,7 @@ extension _Encoder: SingleValueEncodingContainer { assertCanEncodeNewValue() try encode(yamlEncodable: value) } - + private func encode(yamlEncodable encodable: YAMLEncodable) throws { func encodeNode() { node = encodable.box() @@ -270,7 +270,8 @@ extension _Encoder: SingleValueEncodingContainer { if let encodable = value as? YAMLEncodable { try encode(yamlEncodable: encodable) } else { - if let redundancyAliasingStrategy = userInfo[.redundancyAliasingStrategyKey] as? RedundancyAliasingStrategy { + if let redundancyAliasingStrategy = + userInfo[.redundancyAliasingStrategyKey] as? RedundancyAliasingStrategy { switch try redundancyAliasingStrategy.alias(for: value) { case .none: try value.encode(to: self) diff --git a/Sources/Yams/Node.Alias.swift b/Sources/Yams/Node.Alias.swift index 1c3585bb..80766eff 100644 --- a/Sources/Yams/Node.Alias.swift +++ b/Sources/Yams/Node.Alias.swift @@ -6,8 +6,6 @@ // Copyright (c) 2024 Yams. All rights reserved. // -import Foundation - // MARK: Node+Alias extension Node { @@ -19,7 +17,7 @@ extension Node { public var tag: Tag /// The location for this node. public var mark: Mark? - + /// Create a `Node.Alias` using the specified parameters. /// /// - parameter tag: This scalar's `Tag`. diff --git a/Sources/Yams/Node.Mapping.swift b/Sources/Yams/Node.Mapping.swift index e9635fb8..955939b6 100644 --- a/Sources/Yams/Node.Mapping.swift +++ b/Sources/Yams/Node.Mapping.swift @@ -35,7 +35,11 @@ extension Node { /// - parameter tag: This mapping's `Tag`. /// - parameter style: The style to use when emitting this `Mapping`. /// - parameter mark: This mapping's `Mark`. - public init(_ pairs: [(Node, Node)], _ tag: Tag = .implicit, _ style: Style = .any, _ mark: Mark? = nil, _ anchor: Anchor? = nil) { + public init(_ pairs: [(Node, Node)], + _ tag: Tag = .implicit, + _ style: Style = .any, + _ mark: Mark? = nil, + _ anchor: Anchor? = nil) { self.pairs = pairs.map { Pair($0.0, $0.1) } self.tag = tag self.style = style diff --git a/Sources/Yams/Node.Scalar.swift b/Sources/Yams/Node.Scalar.swift index 68640f0a..42ad2037 100644 --- a/Sources/Yams/Node.Scalar.swift +++ b/Sources/Yams/Node.Scalar.swift @@ -50,7 +50,11 @@ extension Node { /// - parameter tag: This scalar's `Tag`. /// - parameter style: The style to use when emitting this `Scalar`. /// - parameter mark: This scalar's `Mark`. - public init(_ string: String, _ tag: Tag = .implicit, _ style: Style = .any, _ mark: Mark? = nil, _ anchor: Anchor? = nil) { + public init(_ string: String, + _ tag: Tag = .implicit, + _ style: Style = .any, + _ mark: Mark? = nil, + _ anchor: Anchor? = nil) { self.string = string self.tag = tag self.style = style diff --git a/Sources/Yams/Node.Sequence.swift b/Sources/Yams/Node.Sequence.swift index c74f039e..80f7a40f 100644 --- a/Sources/Yams/Node.Sequence.swift +++ b/Sources/Yams/Node.Sequence.swift @@ -37,7 +37,11 @@ extension Node { /// - parameter tag: This sequence's `Tag`. /// - parameter style: The style to use when emitting this `Sequence`. /// - parameter mark: This sequence's `Mark`. - public init(_ nodes: [Node], _ tag: Tag = .implicit, _ style: Style = .any, _ mark: Mark? = nil, _ anchor: Anchor? = nil) { + public init(_ nodes: [Node], + _ tag: Tag = .implicit, + _ style: Style = .any, + _ mark: Mark? = nil, + _ anchor: Anchor? = nil) { self.nodes = nodes self.tag = tag self.style = style diff --git a/Sources/Yams/Node.swift b/Sources/Yams/Node.swift index c848da43..c335ee79 100644 --- a/Sources/Yams/Node.swift +++ b/Sources/Yams/Node.swift @@ -26,7 +26,10 @@ extension Node { /// - parameter string: String value for this node. /// - parameter tag: Tag for this node. /// - parameter style: Style to use when emitting this node. - public init(_ string: String, _ tag: Tag = .implicit, _ style: Scalar.Style = .any, _ anchor: Anchor? = nil) { + public init(_ string: String, + _ tag: Tag = .implicit, + _ style: Scalar.Style = .any, + _ anchor: Anchor? = nil) { self = .scalar(.init(string, tag, style, nil, anchor)) } @@ -35,7 +38,10 @@ extension Node { /// - parameter pairs: Pairs of nodes to use for this node. /// - parameter tag: Tag for this node. /// - parameter style: Style to use when emitting this node. - public init(_ pairs: [(Node, Node)], _ tag: Tag = .implicit, _ style: Mapping.Style = .any, _ anchor: Anchor? = nil) { + public init(_ pairs: [(Node, Node)], + _ tag: Tag = .implicit, + _ style: Mapping.Style = .any, + _ anchor: Anchor? = nil) { self = .mapping(.init(pairs, tag, style, nil, anchor)) } @@ -44,7 +50,10 @@ extension Node { /// - parameter nodes: Sequence of nodes to use for this node. /// - parameter tag: Tag for this node. /// - parameter style: Style to use when emitting this node. - public init(_ nodes: [Node], _ tag: Tag = .implicit, _ style: Sequence.Style = .any, _ anchor: Anchor? = nil) { + public init(_ nodes: [Node], + _ tag: Tag = .implicit, + _ style: Sequence.Style = .any, + _ anchor: Anchor? = nil) { self = .sequence(.init(nodes, tag, style, nil, anchor)) } } @@ -73,7 +82,7 @@ extension Node { case let .alias(alias): return alias.mark } } - + /// The anchor for this node. public var anchor: Anchor? { switch self { @@ -304,7 +313,7 @@ extension Node { } return false } - + func setting(anchor: Anchor) -> Self { switch self { case var .mapping(mapping): @@ -321,7 +330,7 @@ extension Node { return .alias(alias) } } - + func setting(tag: Tag) -> Self { switch self { case var .mapping(mapping): diff --git a/Sources/Yams/Parser.swift b/Sources/Yams/Parser.swift index 948c03be..46165ace 100644 --- a/Sources/Yams/Parser.swift +++ b/Sources/Yams/Parser.swift @@ -265,7 +265,7 @@ public final class Parser { case utf16(Data) } private var buffer: Buffer - + // MARK: – Pivate Mutators private func register(anchor: Anchor?, to node: Node) { if let anchor { @@ -341,7 +341,11 @@ private extension Parser { func loadScalar(from event: Event) throws -> Node { let anchor = event.scalarAnchor - let node = Node.scalar(.init(event.scalarValue, tag(event.scalarTag), event.scalarStyle, event.startMark, anchor)) + let node = Node.scalar(.init(event.scalarValue, + tag(event.scalarTag), + event.scalarStyle, + event.startMark, + anchor)) register(anchor: anchor, to: node) return node } @@ -354,7 +358,11 @@ private extension Parser { event = try parse() } let anchor = firstEvent.sequenceAnchor - let node = Node.sequence(.init(array, tag(firstEvent.sequenceTag), event.sequenceStyle, firstEvent.startMark, anchor)) + let node = Node.sequence(.init(array, + tag(firstEvent.sequenceTag), + event.sequenceStyle, + firstEvent.startMark, + anchor)) register(anchor: anchor, to: node) return node } @@ -370,7 +378,11 @@ private extension Parser { event = try parse() } let anchor = firstEvent.mappingAnchor - let node = Node.mapping(.init(pairs, tag(firstEvent.mappingTag), event.mappingStyle, firstEvent.startMark, anchor)) + let node = Node.mapping(.init(pairs, + tag(firstEvent.mappingTag), + event.mappingStyle, + firstEvent.startMark, + anchor)) register(anchor: anchor, to: node) return node } diff --git a/Sources/Yams/RedundancyAliasingStrategy.swift b/Sources/Yams/RedundancyAliasingStrategy.swift index d802847e..5d655230 100644 --- a/Sources/Yams/RedundancyAliasingStrategy.swift +++ b/Sources/Yams/RedundancyAliasingStrategy.swift @@ -6,8 +6,6 @@ // Copyright (c) 2024 Yams. All rights reserved. // -import Foundation - public enum RedundancyAliasingOutcome { case anchor(Anchor) case alias(Anchor) @@ -24,14 +22,14 @@ public enum RedundancyAliasingOutcome { /// when releaseAnchorReferences() is called by the Encoder. After this call the implementation will no longer be /// referenced by the Encoder and will itself be released. public protocol RedundancyAliasingStrategy: AnyObject { - + /// Implementations should return RedundancyAliasingOutcome.anchor(...) for the first occurrence of a value. /// Subsequent occurrences of the same value (where same-ness is defined by the implementation) should /// return RedundancyAliasingOutcome.alias(...) where the contained Anchor has the same value as the previously /// returned RedundancyAliasingOutcome.anchor(...). Its the identity of the Anchor values returned that ultimately /// informs the YAML encoder when to use aliases. func alias(for encodable: any Encodable) throws -> RedundancyAliasingOutcome - + /// It is essential that implementations release all references to Anchors which are created by this type /// when releaseAnchorReferences() is called by the Encoder. After this call, the implementation will no longer be /// referenced by the Encoder and will itself be released. @@ -43,18 +41,18 @@ public protocol RedundancyAliasingStrategy: AnyObject { /// i.e. if two values are Hashable-Equal, they will be aliased in the resultant YML document. public class HashableAliasingStrategy: RedundancyAliasingStrategy { private var hashesToAliases: [AnyHashable: Anchor] = [:] - + let uniqueAliasProvider = UniqueAliasProvider() - + public init() {} - + public func alias(for encodable: any Encodable) throws -> RedundancyAliasingOutcome { guard let hashable = encodable as? any Hashable & Encodable else { return .none } return try alias(for: hashable) } - + private func alias(for hashable: any Hashable & Encodable) throws -> RedundancyAliasingOutcome { let anyHashable = AnyHashable(hashable) if let existing = hashesToAliases[anyHashable] { @@ -65,23 +63,24 @@ public class HashableAliasingStrategy: RedundancyAliasingStrategy { return .anchor(newAlias) } } - + public func releaseAnchorReferences() throws { hashesToAliases.removeAll() } } -/// An implementation of RedundancyAliasingStrategy that defines alias-ability by the coded representation of the values. -/// i.e. if two values encode to exactly the same, they will be aliased in the resultant YML document even if the values themselves are of different types +/// An implementation of RedundancyAliasingStrategy that defines alias-ability by the coded representation +/// of the values. i.e. if two values encode to exactly the same, they will be aliased in the resultant YML +/// document even if the values themselves are of different types public class StrictEncodableAliasingStrategy: RedundancyAliasingStrategy { private var codedToAliases: [String: Anchor] = [:] - + let uniqueAliasProvider = UniqueAliasProvider() - + public init() {} - + private let encoder = YAMLEncoder() - + public func alias(for encodable: any Encodable) throws -> RedundancyAliasingOutcome { let coded = try encoder.encode(encodable) if let existing = codedToAliases[coded] { @@ -92,7 +91,7 @@ public class StrictEncodableAliasingStrategy: RedundancyAliasingStrategy { return .anchor(newAlias) } } - + public func releaseAnchorReferences() throws { codedToAliases.removeAll() } @@ -100,14 +99,14 @@ public class StrictEncodableAliasingStrategy: RedundancyAliasingStrategy { class UniqueAliasProvider { private var counter = 0 - + func uniqueAlias(for encodable: any Encodable) -> Anchor { if let anchorProviding = encodable as? YamlAnchorProviding, let anchor = anchorProviding.yamlAnchor { return anchor } else { counter += 1 - return Anchor(rawValue: String(counter)) + return Anchor(rawValue: String(counter)) } } } diff --git a/Sources/Yams/YamlAnchorProviding.swift b/Sources/Yams/YamlAnchorProviding.swift index dfc4e899..01eff6a7 100644 --- a/Sources/Yams/YamlAnchorProviding.swift +++ b/Sources/Yams/YamlAnchorProviding.swift @@ -6,8 +6,6 @@ // Copyright (c) 2024 Yams. All rights reserved. // -import Foundation - /// Types that conform to YamlAnchorProviding and Encodable can optionally dictate the name of /// a yaml anchor when they are encoded with YAMLEncoder public protocol YamlAnchorProviding { @@ -26,19 +24,19 @@ internal extension Node { } private final class YamlAnchorFunctionNameProvider: YamlAnchorProviding { - + fileprivate var functionName: StaticString? - + var yamlAnchor: Anchor? { functionName = #function return nil } - + func getName() -> StaticString { _ = yamlAnchor return functionName! } - + func getName() -> String { String(describing: getName() as StaticString) } diff --git a/Sources/Yams/YamlTagProviding.swift b/Sources/Yams/YamlTagProviding.swift index 0f2a2d63..b0beb945 100644 --- a/Sources/Yams/YamlTagProviding.swift +++ b/Sources/Yams/YamlTagProviding.swift @@ -24,19 +24,19 @@ internal extension Node { } private final class YamlTagFunctionNameProvider: YamlTagProviding { - + fileprivate var functionName: StaticString? - + var yamlTag: Tag? { functionName = #function return nil } - + func getName() -> StaticString { _ = yamlTag return functionName! } - + func getName() -> String { String(describing: getName() as StaticString) } diff --git a/Tests/YamsTests/AnchorCodingTests.swift b/Tests/YamsTests/AnchorCodingTests.swift index 24d89cba..997ced71 100644 --- a/Tests/YamsTests/AnchorCodingTests.swift +++ b/Tests/YamsTests/AnchorCodingTests.swift @@ -10,38 +10,42 @@ import XCTest import Yams class AnchorCodingTests: XCTestCase { - + /// Test the encoding of a yaml anchor using a type that conforms to YamlAnchorProviding func testYamlAnchorProviding_valuePresent() throws { - let simpleStruct = SimpleWithAnchor(nested: .init(stringValue: "it's a value"), intValue: 52) - + let simpleStruct = SimpleWithAnchor(nested: + .init(stringValue: "it's a value"), + intValue: 52) + _testRoundTrip(of: simpleStruct, - expectedYAML:""" - &simple - nested: - stringValue: it's a value - intValue: 52 - - """ ) // ^ the Yams.Anchor is encoded as a yaml anchor + expectedYAML: """ + &simple + nested: + stringValue: it's a value + intValue: 52 + + """ ) // ^ the Yams.Anchor is encoded as a yaml anchor } - - /// Test the encoding of a a type that does not conform to YamlAnchorProviding but none the less declares a coding member with the same name + + /// Test the encoding of a a type that does not conform to YamlAnchorProviding but none the less + /// declares a coding member with the same name func testStringTypeAnchorName_valuePresent() throws { - let simpleStruct = SimpleWithStringTypeAnchorName(nested: .init(stringValue: "it's a value"), + let simpleStruct = SimpleWithStringTypeAnchorName(nested: .init(stringValue: "it's a value"), intValue: 52, yamlAnchor: "but typed as a string") - + _testRoundTrip(of: simpleStruct, - expectedYAML:""" - nested: - stringValue: it's a value - intValue: 52 - yamlAnchor: but typed as a string - - """ ) // ^ the member is _not_ treated as an anchor + expectedYAML: """ + nested: + stringValue: it's a value + intValue: 52 + yamlAnchor: but typed as a string + + """ ) // ^ the member is _not_ treated as an anchor } - - /// Nothing interesting happens when a type does not conform to YamlAnchorProviding none the less declares a coding member with the same name but that value is nil + + /// Nothing interesting happens when a type does not conform to YamlAnchorProviding none the less + /// declares a coding member with the same name but that value is nil func testStringTypeAnchorName_valueNotPresent() throws { let expectedStruct = SimpleWithStringTypeAnchorName(nested: .init(stringValue: "it's a value"), intValue: 52, @@ -51,15 +55,18 @@ class AnchorCodingTests: XCTestCase { nested: stringValue: it's a value intValue: 52 - + """) } - + /// This test documents some undesirable behavior, but in an unlikely circumstance. - /// If the decoded type does not conform to YamlAnchorProviding it can still have a coding key called `yamlAnchor` - /// If Yams tries to decode such a type AND the document has a nil value for `yamlAnchor` AND the parent context is a mapping AND that mapping has an actual anchor (in the document) + /// If the decoded type does not conform to YamlAnchorProviding it can still have a coding key called + /// `yamlAnchor` + /// If Yams tries to decode such a type AND the document has a nil value for `yamlAnchor` AND the + /// parent context is a mapping AND that mapping has an actual anchor (in the document) /// THEN Yams wrongly tries to decode the anchor as the declared type of key `yamlAnchor`. - /// If that declared type can be decoded from a scalar string value (like String and RawRepresentable where RawValue == String) then the decoding will actually succeed. + /// If that declared type can be decoded from a scalar string value (like String and RawRepresentable + /// where RawValue == String) then the decoding will actually succeed. /// Which effectively injects an unexpected value into the decoded type. func testStringTypeAnchorName_withAnchorPresent_valueNil() throws { let expectedStruct = SimpleWithStringTypeAnchorName(nested: .init(stringValue: "it's a value"), @@ -71,228 +78,243 @@ class AnchorCodingTests: XCTestCase { nested: stringValue: it's a value intValue: 52 - + """.data(using: decoder.encoding.swiftStringEncoding)! - + let decodedStruct = try decoder.decode(SimpleWithStringTypeAnchorName.self, from: data) - + let fixBulletin = "YESS!!! YOU FIXED IT! See \(#file):\(#line) for explanation." - + // begin assertions of known-but-undesirable behavior XCTAssertNotEqual(decodedStruct, expectedStruct, fixBulletin) // We wish this was equal XCTAssertEqual(decodedStruct.yamlAnchor, "AnActualAnchor", fixBulletin) // we wish .yamlAnchor was nil // end assertions of known-but-undesirable behavior - - + // Check the remainder of the properties that the above confusion did not involve XCTAssertEqual(decodedStruct.nested, expectedStruct.nested) XCTAssertEqual(decodedStruct.intValue, expectedStruct.intValue) } } - + class AnchorAliasingTests: XCTestCase { - + /// CYaml library does not detect identical values and automatically alias them. func testCyamlDoesNotAutoAlias_noAnchor() throws { let simpleNoAnchor = SimpleWithoutAnchor(nested: .init(stringValue: "it's a value"), intValue: 52) let differentTypesOneAnchor = SimplePair(first: simpleNoAnchor, second: simpleNoAnchor) - + _testRoundTrip(of: differentTypesOneAnchor, - expectedYAML:""" - first: - nested: - stringValue: it's a value - intValue: 52 - second: - nested: - stringValue: it's a value - intValue: 52 - - """ ) + expectedYAML: """ + first: + nested: + stringValue: it's a value + intValue: 52 + second: + nested: + stringValue: it's a value + intValue: 52 + + """ ) } - - /// CYaml library does not detect identical values and automatically alias them even if the first occurrence has an anchor. + + /// CYaml library does not detect identical values and automatically alias them even if the first + /// occurrence has an anchor. func testCyamlDoesNotAutoAlias_uniqueAnchor() throws { let simpleStruct = SimpleWithAnchor(nested: .init(stringValue: "it's a value"), intValue: 52) let simpleNoAnchor = SimpleWithoutAnchor(nested: .init(stringValue: "it's a value"), intValue: 52) let differentTypesOneAnchor = SimplePair(first: simpleStruct, second: simpleNoAnchor) - + _testRoundTrip(of: differentTypesOneAnchor, - expectedYAML:""" - first: &simple - nested: - stringValue: it's a value - intValue: 52 - second: - nested: - stringValue: it's a value - intValue: 52 - - """ ) + expectedYAML: """ + first: &simple + nested: + stringValue: it's a value + intValue: 52 + second: + nested: + stringValue: it's a value + intValue: 52 + + """ ) } - + /// CYaml library does not detect identical values and automatically alias them even if they have identical anchors. - // This one is not a shortcoming of CYaml. The yaml spec requires that nodes can shadow earlier anchors. + /// This one is not a shortcoming of CYaml. The yaml spec requires that nodes can shadow earlier anchors. func testCyamlDoesNotAutoAlias_duplicateAnchor() throws { let simpleStruct = SimpleWithAnchor(nested: .init(stringValue: "it's a value"), intValue: 52) let duplicatedStructPair = SimplePair(first: simpleStruct, second: simpleStruct) - + _testRoundTrip(of: duplicatedStructPair, - expectedYAML:""" - first: &simple - nested: - stringValue: it's a value - intValue: 52 - second: &simple - nested: - stringValue: it's a value - intValue: 52 - - """ ) + expectedYAML: """ + first: &simple + nested: + stringValue: it's a value + intValue: 52 + second: &simple + nested: + stringValue: it's a value + intValue: 52 + + """ ) } - - + /// If types conform to YamlAnchorProviding and are Hashable-Equal then HashableAliasingStrategy aliases them func testEncoderAutoAlias_Hashable_duplicateAnchor() throws { let simpleStruct = SimpleWithAnchor(nested: .init(stringValue: "it's a value"), intValue: 52) let duplicatedStructArray = [simpleStruct, simpleStruct] - + let options = YAMLEncoder.Options(redundancyAliasingStrategy: HashableAliasingStrategy()) _testRoundTrip(of: duplicatedStructArray, with: options, - expectedYAML:""" - - &simple - nested: - stringValue: it's a value - intValue: 52 - - *simple - - """ ) + expectedYAML: """ + - &simple + nested: + stringValue: it's a value + intValue: 52 + - *simple + + """ ) } - + /// If types do NOT conform to YamlAnchorProviding and are Hashable-Equal then HashableAliasingStrategy aliases them func testEncoderAutoAlias_Hashable_noAnchors() throws { let simpleStruct = SimpleWithoutAnchor(nested: .init(stringValue: "it's a value"), intValue: 52) let duplicatedStructArray = [simpleStruct, simpleStruct] // zero specified anchor - + let options = YAMLEncoder.Options(redundancyAliasingStrategy: HashableAliasingStrategy()) _testRoundTrip(of: duplicatedStructArray, with: options, - expectedYAML:""" - - &2 - nested: - stringValue: it's a value - intValue: 52 - - *2 - - """ ) + expectedYAML: """ + - &2 + nested: + stringValue: it's a value + intValue: 52 + - *2 + + """ ) } - - /// If types conform to YamlAnchorProviding and are NOT Hashable-Equal then HashableAliasingStrategy does not alias them - /// even though their members may still be Hashable-Equal and therefor maybe aliased. + + /// If types conform to YamlAnchorProviding and are NOT Hashable-Equal then + /// HashableAliasingStrategy does not alias them even though their members may still be + /// Hashable-Equal and therefor maybe aliased. func testEncoderAutoAlias_Hashable_uniqueAnchor() throws { - let differentTypesOneAnchors = SimplePair(first: SimpleWithAnchor(nested: .init(stringValue: "it's a value"), intValue: 52), - second: SimpleWithoutAnchor(nested: .init(stringValue: "it's a value"), intValue: 52)) - + let differentTypesOneAnchors = SimplePair(first: + SimpleWithAnchor(nested: .init(stringValue: "it's a value"), + intValue: 52), + second: + SimpleWithoutAnchor(nested: .init(stringValue: "it's a value"), + intValue: 52)) + let options = YAMLEncoder.Options(redundancyAliasingStrategy: HashableAliasingStrategy()) _testRoundTrip(of: differentTypesOneAnchors, with: options, - expectedYAML:""" - first: &simple - nested: &2 - stringValue: it's a value - intValue: &4 52 - second: - nested: *2 - intValue: *4 - - """ ) + expectedYAML: """ + first: &simple + nested: &2 + stringValue: it's a value + intValue: &4 52 + second: + nested: *2 + intValue: *4 + + """ ) } - - /// If types conform to YamlAnchorProviding and are NOT Hashable-Equal then HashableAliasingStrategy does not alias them - /// even though their members may still be Hashable-Equal and therefor maybe aliased. - /// Note particularly that the to Simple* values here have exactly the same encoded representation, they're just different types and thus not Hashable-Equal + + /// If types conform to YamlAnchorProviding and are NOT Hashable-Equal then + /// HashableAliasingStrategy does not alias them even though their members may still be + /// Hashable-Equal and therefor maybe aliased. + /// Note particularly that the to Simple* values here have exactly the same encoded representation, + /// they're just different types and thus not Hashable-Equal func testEncoderAutoAlias_Hashable_noAnchor() throws { - let differentTypesNoAnchors = SimplePair(first: SimpleWithoutAnchor2(nested: .init(stringValue: "it's a value"), intValue: 52), - second: SimpleWithoutAnchor(nested: .init(stringValue: "it's a value"), intValue: 52)) - + let differentTypesNoAnchors = SimplePair(first: + SimpleWithoutAnchor2(nested: .init(stringValue: "it's a value"), + intValue: 52), + second: + SimpleWithoutAnchor(nested: .init(stringValue: "it's a value"), + intValue: 52)) + let options = YAMLEncoder.Options(redundancyAliasingStrategy: HashableAliasingStrategy()) _testRoundTrip(of: differentTypesNoAnchors, with: options, - expectedYAML:""" - first: - nested: &3 - stringValue: it's a value - intValue: &5 52 - second: - nested: *3 - intValue: *5 - - """ ) + expectedYAML: """ + first: + nested: &3 + stringValue: it's a value + intValue: &5 52 + second: + nested: *3 + intValue: *5 + + """ ) } - - /// If types conform to YamlAnchorProviding and have exactly the same encoded representation then StrictEncodableAliasingStrategy alias them - /// even though they are encoded and decoded from different types. + + /// If types conform to YamlAnchorProviding and have exactly the same encoded representation then + /// StrictEncodableAliasingStrategy alias them even though they are encoded and decoded from + /// different types. func testEncoderAutoAlias_StrictEncodable_NoAnchors() throws { - let differentTypesNoAnchors = SimplePair(first: SimpleWithoutAnchor2(nested: .init(stringValue: "it's a value"), intValue: 52), - second: SimpleWithoutAnchor(nested: .init(stringValue: "it's a value"), intValue: 52)) - + let differentTypesNoAnchors = SimplePair(first: + SimpleWithoutAnchor2(nested: .init(stringValue: "it's a value"), + intValue: 52), + second: + SimpleWithoutAnchor(nested: .init(stringValue: "it's a value"), + intValue: 52)) + var options = YAMLEncoder.Options() options.redundancyAliasingStrategy = StrictEncodableAliasingStrategy() _testRoundTrip(of: differentTypesNoAnchors, with: options, - expectedYAML:""" - first: &2 - nested: - stringValue: it's a value - intValue: 52 - second: *2 - - """ ) + expectedYAML: """ + first: &2 + nested: + stringValue: it's a value + intValue: 52 + second: *2 + + """ ) } - + /// A type used to contain values used during testing private struct SimplePair: Hashable, Codable { let first: First let second: Second } - + } // MARK: - Types used for Anchor encoding tests. -fileprivate struct NestedStruct: Codable, Hashable { +private struct NestedStruct: Codable, Hashable { let stringValue: String } -fileprivate protocol SimpleProtocol: Codable, Hashable { +private protocol SimpleProtocol: Codable, Hashable { + // swiftlint:disable unused_declaration var nested: NestedStruct { get } + // swiftlint:disable unused_declaration var intValue: Int { get } } -fileprivate struct SimpleWithAnchor: SimpleProtocol, YamlAnchorProviding { +private struct SimpleWithAnchor: SimpleProtocol, YamlAnchorProviding { let nested: NestedStruct let intValue: Int var yamlAnchor: Anchor? = "simple" } -fileprivate struct SimpleWithoutAnchor: SimpleProtocol { +private struct SimpleWithoutAnchor: SimpleProtocol { let nested: NestedStruct let intValue: Int } -fileprivate struct SimpleWithoutAnchor2: SimpleProtocol { +private struct SimpleWithoutAnchor2: SimpleProtocol { let nested: NestedStruct let intValue: Int + // swiftlint:disable unused_declaration var unrelatedValue: String? } -fileprivate struct SimpleWithStringTypeAnchorName: SimpleProtocol { +private struct SimpleWithStringTypeAnchorName: SimpleProtocol { let nested: NestedStruct let intValue: Int var yamlAnchor: String? = "StringTypeAnchor" } - - - diff --git a/Tests/YamsTests/AnchorTolerancesTests.swift b/Tests/YamsTests/AnchorTolerancesTests.swift index 60d126cb..4886a959 100644 --- a/Tests/YamsTests/AnchorTolerancesTests.swift +++ b/Tests/YamsTests/AnchorTolerancesTests.swift @@ -10,70 +10,70 @@ import XCTest import Yams class AnchorTolerancesTests: XCTestCase { - + struct Example: Codable, Hashable { var myCustomAnchorDeclaration: Anchor var extraneousValue: Int } - - /// Any type that is Encodable and contains an `Anchor`value but with a coding key different from YamlAnchorProviding - /// will not encode to a yaml anchor + + /// Any type that is Encodable and contains an `Anchor`value but with a coding key different from + /// YamlAnchorProviding will not encode to a yaml anchor /// This may be unexpected func testAnchorEncoding_undeclaredBehavior() throws { let expectedYAML = """ myCustomAnchorDeclaration: I-did-it-myyyyy-way extraneousValue: 3 - + """ - - let value = Example(myCustomAnchorDeclaration: "I-did-it-myyyyy-way", + + let value = Example(myCustomAnchorDeclaration: "I-did-it-myyyyy-way", extraneousValue: 3) - + let encoder = YAMLEncoder() let producedYAML = try encoder.encode(value) XCTAssertEqual(producedYAML, expectedYAML, "Produced YAML not identical to expected YAML.") } - - /// Any type that is Encodable and contains an `Anchor`value with the same coding key as YamlAnchorProviding - /// will encode to a yaml anchor even though the type does not conform to YamlAnchorProviding - /// This may be unexpected - func testAnchorEncoding_undeclaredBehavior_7() throws { - struct Example: Codable, Hashable { - var yamlAnchor: Anchor - var extraneousValue: Int - } - - let expectedYAML = """ - &I-did-it-myyyyy-way - extraneousValue: 3 - - """ - - let value = Example(yamlAnchor: "I-did-it-myyyyy-way", - extraneousValue: 3) - - let encoder = YAMLEncoder() - let producedYAML = try encoder.encode(value) - XCTAssertEqual(producedYAML, expectedYAML, "Produced YAML not identical to expected YAML.") + + /// Any type that is Encodable and contains an `Anchor`value with the same coding key as + /// YamlAnchorProviding will encode to a yaml anchor even though the type does not conform to + /// YamlAnchorProviding + /// This may be unexpected + func testAnchorEncoding_undeclaredBehavior_7() throws { + struct Example: Codable, Hashable { + var yamlAnchor: Anchor + var extraneousValue: Int } - /// Any type that is Decodable and contains an `Anchor` value but with a coding key different from YamlAnchorProviding - /// will not decode an anchor from the text representation. + let expectedYAML = """ + &I-did-it-myyyyy-way + extraneousValue: 3 + + """ + + let value = Example(yamlAnchor: "I-did-it-myyyyy-way", + extraneousValue: 3) + + let encoder = YAMLEncoder() + let producedYAML = try encoder.encode(value) + XCTAssertEqual(producedYAML, expectedYAML, "Produced YAML not identical to expected YAML.") + } + + /// Any type that is Decodable and contains an `Anchor` value but with a coding key different from + /// YamlAnchorProviding will not decode an anchor from the text representation. /// In this case a key not found error will be thrown during decoding /// This may be unexpected func testAnchorDecoding_undeclaredBehavior_1() throws { let sourceYAML = """ &a-different-tag extraneousValue: 3 - """ let decoder = YAMLDecoder() XCTAssertThrowsError(try decoder.decode(Example.self, from: sourceYAML)) // error is ^^ key not found, "myCustomAnchorDeclaration" } - - /// Any type that is Decodable and contains an `Anchor` value but with a coding key different from YamlAnchorProviding - /// will not decode an anchor from the text representation. + + /// Any type that is Decodable and contains an `Anchor` value but with a coding key different from + /// YamlAnchorProviding will not decode an anchor from the text representation. /// In this case the decoding is successful and the anchor is respected by the parser. /// This may be unexpected func testAnchorDecoding_undeclaredBehavior_6() throws { @@ -84,19 +84,20 @@ class AnchorTolerancesTests: XCTestCase { let sourceYAML = """ &a-different-tag extraneousValue: 3 - + """ - + let expectedValue = Example(myCustomAnchorDeclaration: nil, extraneousValue: 3) - + let decoder = YAMLDecoder() let decodedValue = try decoder.decode(Example.self, from: sourceYAML) XCTAssertEqual(decodedValue, expectedValue, "\(Example.self) did not round-trip to an equal value.") } - - /// Any type that is Decodable and contains an `Anchor` value with the same coding key as YamlAnchorProviding - /// will decode an anchor from the text representation even though the type does not conform to YamlAnchorCoding + + /// Any type that is Decodable and contains an `Anchor` value with the same coding key as + /// YamlAnchorProviding will decode an anchor from the text representation even though the type does + /// not conform to YamlAnchorCoding /// This may be unexpected func testAnchorDecoding_undeclaredBehavior_8() throws { struct Example: Codable, Hashable { @@ -106,19 +107,19 @@ class AnchorTolerancesTests: XCTestCase { let sourceYAML = """ &a-different-tag extraneousValue: 3 - + """ - + let expectedValue = Example(yamlAnchor: "a-different-tag", extraneousValue: 3) - + let decoder = YAMLDecoder() let decodedValue = try decoder.decode(Example.self, from: sourceYAML) XCTAssertEqual(decodedValue, expectedValue, "\(Example.self) did not round-trip to an equal value.") } - - /// Any type that is Decodable and contains an `Anchor` value but with a coding key different from YamlAnchorProviding - /// will not decode an anchor from the text representation. + + /// Any type that is Decodable and contains an `Anchor` value but with a coding key different from + /// YamlAnchorProviding will not decode an anchor from the text representation. /// In this case the decoding is successful and the anchor is respected by the parser. /// This is expected behavior, but in a strange situation. func testAnchorDecoding_undeclaredBehavior_3() throws { @@ -126,37 +127,37 @@ class AnchorTolerancesTests: XCTestCase { &a-different-tag extraneousValue: 3 myCustomAnchorDeclaration: deliver-us-from-evil - + """ let expectedValue = Example(myCustomAnchorDeclaration: "deliver-us-from-evil", extraneousValue: 3) - + let decoder = YAMLDecoder() let decodedValue = try decoder.decode(Example.self, from: sourceYAML) XCTAssertEqual(decodedValue, expectedValue, "\(Example.self) did not round-trip to an equal value.") - + } - - /// Any type that is Decodable and contains an `Anchor` value but with a coding key different from YamlAnchorProviding - /// will not decode an anchor from the text representation. - /// In this case the decoding is successful even though and the `Anchor` was initialized with unsupported characters. - /// The anchor is respected by the parser. + + /// Any type that is Decodable and contains an `Anchor` value but with a coding key different from + /// YamlAnchorProviding will not decode an anchor from the text representation. + /// In this case the decoding is successful even though and the `Anchor` was initialized with + /// unsupported characters. The anchor is respected by the parser. /// This is expected behavior, but in a strange situation. func testAnchorDecoding_undeclaredBehavior_2() throws { let sourceYAML = """ &a-different-tag extraneousValue: 3 myCustomAnchorDeclaration: "deliver us from |()evil" - + """ - + let expectedValue = Example(myCustomAnchorDeclaration: "deliver us from |()evil", extraneousValue: 3) - + let decoder = YAMLDecoder() let decodedValue = try decoder.decode(Example.self, from: sourceYAML) XCTAssertEqual(decodedValue, expectedValue, "\(Example.self) did not round-trip to an equal value.") - + } - + } diff --git a/Tests/YamsTests/EncoderTests.swift b/Tests/YamsTests/EncoderTests.swift index 284cfacf..0d85a3a2 100644 --- a/Tests/YamsTests/EncoderTests.swift +++ b/Tests/YamsTests/EncoderTests.swift @@ -440,10 +440,11 @@ class EncoderTests: XCTestCase { // swiftlint:disable:this type_body_length } internal func _testRoundTrip(of value: T, - with options: YAMLEncoder.Options = .init(), - expectedYAML yamlString: String? = nil, - file: StaticString = #file, - line: UInt = #line) where T: Codable, T: Equatable { + with options: YAMLEncoder.Options = .init(), + expectedYAML yamlString: String? = nil, + file: StaticString = #file, + line: UInt = #line) +where T: Codable, T: Equatable { do { let encoder = YAMLEncoder() encoder.options = options diff --git a/Tests/YamsTests/TagCodingTests.swift b/Tests/YamsTests/TagCodingTests.swift index 1b3e0e6f..6f85828d 100644 --- a/Tests/YamsTests/TagCodingTests.swift +++ b/Tests/YamsTests/TagCodingTests.swift @@ -10,38 +10,40 @@ import XCTest import Yams class TagCodingTests: XCTestCase { - + /// Test the encoding of a yaml tag using a type that conforms to YamlTagProviding func testYamlTagProviding_valuePresent() throws { let simpleStruct = SimpleWithTag(nested: .init(stringValue: "it's a value"), intValue: 52) - + _testRoundTrip(of: simpleStruct, - expectedYAML:""" - ! - nested: - stringValue: it's a value - intValue: 52 - - """ ) // ^ the Yams.Tag is encoded as a yaml tag + expectedYAML: """ + ! + nested: + stringValue: it's a value + intValue: 52 + + """ ) // ^ the Yams.Tag is encoded as a yaml tag } - - /// Test the encoding of a a type that does not conform to YamlTagProviding but none the less declares a coding member with the same name + + /// Test the encoding of a a type that does not conform to YamlTagProviding but none the less declares + /// a coding member with the same name func testStringTypeTagName_valuePresent() throws { let simpleStruct = SimpleWithStringTypeTagName(nested: .init(stringValue: "it's a value"), intValue: 52, yamlTag: "but typed as a string") - + _testRoundTrip(of: simpleStruct, - expectedYAML:""" - nested: - stringValue: it's a value - intValue: 52 - yamlTag: but typed as a string - - """ ) // ^ the member is _not_ treated as an tag + expectedYAML: """ + nested: + stringValue: it's a value + intValue: 52 + yamlTag: but typed as a string + + """ ) // ^ the member is _not_ treated as an tag } - - /// Nothing interesting happens when a type does not conform to YamlTagProviding none the less declares a coding member with the same name but that value is nil + + /// Nothing interesting happens when a type does not conform to YamlTagProviding none the less + /// declares a coding member with the same name but that value is nil func testStringTypeTagName_valueNotPresent() throws { let expectedStruct = SimpleWithStringTypeTagName(nested: .init(stringValue: "it's a value"), intValue: 52, @@ -51,15 +53,18 @@ class TagCodingTests: XCTestCase { nested: stringValue: it's a value intValue: 52 - + """) } - + /// This test documents some undesirable behavior, but in an unlikely circumstance. - /// If the decoded type does not conform to YamlTagProviding it can still have a coding key called `yamlTag` - /// If Yams tries to decode such a type AND the document has a nil value for `yamlTag` AND the parent context is a mapping AND that mapping has an actual tag (in the document) + /// If the decoded type does not conform to YamlTagProviding it can still have a coding key called + /// `yamlTag` + /// If Yams tries to decode such a type AND the document has a nil value for `yamlTag` AND the + /// parent context is a mapping AND that mapping has an actual tag (in the document) /// THEN Yams wrongly tries to decode the tag as the declared type of key `yamlTag`. - /// If that declared type can be decoded from a scalar string value (like String and RawRepresentable where RawValue == String) then the decoding will actually succeed. + /// If that declared type can be decoded from a scalar string value (like String and RawRepresentable + /// where RawValue == String) then the decoding will actually succeed. /// Which effectively injects an unexpected value into the decoded type. func testStringTypeTagName_withTagPresent_valueNil() throws { let expectedStruct = SimpleWithStringTypeTagName(nested: .init(stringValue: "it's a value"), @@ -71,19 +76,18 @@ class TagCodingTests: XCTestCase { nested: stringValue: it's a value intValue: 52 - + """.data(using: decoder.encoding.swiftStringEncoding)! - + let decodedStruct = try decoder.decode(SimpleWithStringTypeTagName.self, from: data) - + let fixBulletin = "YESS!!! YOU FIXED IT! See \(#file):\(#line) for explanation." - + // begin assertions of known-but-undesirable behavior XCTAssertNotEqual(decodedStruct, expectedStruct, fixBulletin) // We wish this was equal XCTAssertEqual(decodedStruct.yamlTag, "An:Actual:Tag", fixBulletin) // we wish .yamlTag was nil // end assertions of known-but-undesirable behavior - - + // Check the remainder of the properties that the above confusion did not involve XCTAssertEqual(decodedStruct.nested, expectedStruct.nested) XCTAssertEqual(decodedStruct.intValue, expectedStruct.intValue) @@ -91,150 +95,166 @@ class TagCodingTests: XCTestCase { } class TagWithAnchorCodingTests: XCTestCase { - + /// If types conform to YamlTagProviding and are Hashable-Equal then HashableAliasingStrategy aliases them func testEncoderAutoAlias_Hashable_duplicateValue_commonTag() throws { let simpleStruct = SimpleWithTag(nested: .init(stringValue: "it's a value"), intValue: 52) let duplicatedStructArray = [simpleStruct, simpleStruct] - + let options = YAMLEncoder.Options(redundancyAliasingStrategy: HashableAliasingStrategy()) _testRoundTrip(of: duplicatedStructArray, with: options, - expectedYAML:""" - - &2 ! - nested: - stringValue: it's a value - intValue: 52 - - *2 - - """ ) + expectedYAML: """ + - &2 ! + nested: + stringValue: it's a value + intValue: 52 + - *2 + + """ ) } - - /// If types conform to YamlTagProviding and are NOT Hashable-Equal then HashableAliasingStrategy does not alias them + + /// If types conform to YamlTagProviding and are NOT Hashable-Equal then HashableAliasingStrategy + /// does not alias them /// even though their members may still be Hashable-Equal and therefor maybe aliased. func testEncoderAutoAlias_Hashable_uniqueTag() throws { - let differentTypesOneTags = SimplePair(first: SimpleWithTag(nested: .init(stringValue: "it's a value"), intValue: 52), - second: SimpleWithoutTag(nested: .init(stringValue: "it's a value"), intValue: 52)) - + let differentTypesOneTags = SimplePair(first: + SimpleWithTag(nested: .init(stringValue: "it's a value"), + intValue: 52), + second: + SimpleWithoutTag(nested: .init(stringValue: "it's a value"), + intValue: 52)) + let options = YAMLEncoder.Options(redundancyAliasingStrategy: HashableAliasingStrategy()) _testRoundTrip(of: differentTypesOneTags, with: options, - expectedYAML:""" - first: ! - nested: &3 - stringValue: it's a value - intValue: &5 52 - second: - nested: *3 - intValue: *5 - - """ ) + expectedYAML: """ + first: ! + nested: &3 + stringValue: it's a value + intValue: &5 52 + second: + nested: *3 + intValue: *5 + + """ ) } - - /// If types conform to YamlTagProviding can declare to have the same tag and still be NOT Hashable-Equal then HashableAliasingStrategy does not alias them + + /// If types conform to YamlTagProviding can declare to have the same tag and still be NOT + /// Hashable-Equal then HashableAliasingStrategy does not alias them /// even though their members may still be Hashable-Equal and therefor maybe aliased. func testEncoderAutoAlias_Hashable_distinctValues_commonTag() throws { - let differentTypesOneTags = SimplePair(first: SimpleWithTag(nested: .init(stringValue: "it's a value"), intValue: 52), - second: SimpleWithTag2(nested: .init(stringValue: "it's a value"), intValue: 52)) - + let differentTypesOneTags = SimplePair(first: + SimpleWithTag(nested: .init(stringValue: "it's a value"), + intValue: 52), + second: + SimpleWithTag2(nested: .init(stringValue: "it's a value"), + intValue: 52)) + let options = YAMLEncoder.Options(redundancyAliasingStrategy: HashableAliasingStrategy()) _testRoundTrip(of: differentTypesOneTags, with: options, - expectedYAML:""" - first: ! - nested: &3 - stringValue: it's a value - intValue: &5 52 - second: ! - nested: *3 - intValue: *5 - - """ ) + expectedYAML: """ + first: ! + nested: &3 + stringValue: it's a value + intValue: &5 52 + second: ! + nested: *3 + intValue: *5 + + """ ) } - - /// If different types conform to YamlTagProviding they can declare to have the same tag and further, have exactly the same encoded representation. - /// In thisi case StrictEncodableAliasingStrategy will still alias them even though they are encoded and decoded from different types. + + /// If different types conform to YamlTagProviding they can declare to have the same tag and further, + /// have exactly the same encoded representation. + /// In thisi case StrictEncodableAliasingStrategy will still alias them even though they are encoded and + /// decoded from different types. func testEncoderAutoAlias_StrictEncodable_distinctValues_commonTag() throws { - let differentTypesOneTags = SimplePair(first: SimpleWithTag(nested: .init(stringValue: "it's a value"), intValue: 52), - second: SimpleWithTag2(nested: .init(stringValue: "it's a value"), intValue: 52)) - + let differentTypesOneTags = SimplePair(first: + SimpleWithTag(nested: .init(stringValue: "it's a value"), + intValue: 52), + second: + SimpleWithTag2(nested: .init(stringValue: "it's a value"), + intValue: 52)) + var options = YAMLEncoder.Options() options.redundancyAliasingStrategy = StrictEncodableAliasingStrategy() _testRoundTrip(of: differentTypesOneTags, with: options, - expectedYAML:""" - first: &2 ! - nested: - stringValue: it's a value - intValue: 52 - second: *2 - - """ ) + expectedYAML: """ + first: &2 ! + nested: + stringValue: it's a value + intValue: 52 + second: *2 + + """ ) } - + /// If types conform to YamlTagProviding and YamlAnchorProviding, both are respected. func testEncoderAutoAlias_Hashable_commonTagAndAnchor() throws { let simpleStruct = SimpleWithTagAndAnchor(nested: .init(stringValue: "it's a value"), intValue: 52) let duplicatedStructArray = [simpleStruct, simpleStruct] - + let options = YAMLEncoder.Options(redundancyAliasingStrategy: HashableAliasingStrategy()) _testRoundTrip(of: duplicatedStructArray, with: options, - expectedYAML:""" - - &simple-Anchor ! - nested: - stringValue: it's a value - intValue: 52 - - *simple-Anchor - - """ ) + expectedYAML: """ + - &simple-Anchor ! + nested: + stringValue: it's a value + intValue: 52 + - *simple-Anchor + + """ ) } - + /// A type used to contain values used during testing private struct SimplePair: Hashable, Codable { let first: First let second: Second } - + } // MARK: - Types used for Tag encoding tests. -fileprivate struct NestedStruct: Codable, Hashable { +private struct NestedStruct: Codable, Hashable { let stringValue: String } -fileprivate protocol SimpleProtocol: Codable, Hashable { +private protocol SimpleProtocol: Codable, Hashable { + // swiftlint:disable unused_declaration var nested: NestedStruct { get } + // swiftlint:disable unused_declaration var intValue: Int { get } } -fileprivate struct SimpleWithTag: SimpleProtocol, YamlTagProviding { +private struct SimpleWithTag: SimpleProtocol, YamlTagProviding { let nested: NestedStruct let intValue: Int var yamlTag: Tag? = "simple" } -fileprivate struct SimpleWithTag2: SimpleProtocol, YamlTagProviding { +private struct SimpleWithTag2: SimpleProtocol, YamlTagProviding { let nested: NestedStruct let intValue: Int var yamlTag: Tag? = "simple" } -fileprivate struct SimpleWithoutTag: SimpleProtocol { +private struct SimpleWithoutTag: SimpleProtocol { let nested: NestedStruct let intValue: Int } -fileprivate struct SimpleWithStringTypeTagName: SimpleProtocol { +private struct SimpleWithStringTypeTagName: SimpleProtocol { let nested: NestedStruct let intValue: Int var yamlTag: String? = "StringTypeTag" } -fileprivate struct SimpleWithTagAndAnchor: SimpleProtocol, YamlTagProviding, YamlAnchorProviding { +private struct SimpleWithTagAndAnchor: SimpleProtocol, YamlTagProviding, YamlAnchorProviding { let nested: NestedStruct let intValue: Int var yamlTag: Tag? = "simple:Tag" var yamlAnchor: Anchor? = "simple-Anchor" } - - diff --git a/Tests/YamsTests/TagTolerancesTests.swift b/Tests/YamsTests/TagTolerancesTests.swift index 62a73e1a..b91109bb 100644 --- a/Tests/YamsTests/TagTolerancesTests.swift +++ b/Tests/YamsTests/TagTolerancesTests.swift @@ -10,32 +10,33 @@ import XCTest import Yams class TagTolerancesTests: XCTestCase { - + struct Example: Codable, Hashable { var myCustomTagDeclaration: Tag var extraneousValue: Int } - - /// Any type that is Encodable and contains an `Tag`value but with a coding key different from YamlTagProviding - /// will not encode to a yaml tag + + /// Any type that is Encodable and contains an `Tag`value but with a coding key different from + /// YamlTagProviding will not encode to a yaml tag /// This may be unexpected func testTagEncoding_undeclaredBehavior() throws { let expectedYAML = """ myCustomTagDeclaration: I-did-it-myyyyy-way extraneousValue: 3 - + """ - - let value = Example(myCustomTagDeclaration: "I-did-it-myyyyy-way", + + let value = Example(myCustomTagDeclaration: "I-did-it-myyyyy-way", extraneousValue: 3) - + let encoder = YAMLEncoder() let producedYAML = try encoder.encode(value) XCTAssertEqual(producedYAML, expectedYAML, "Produced YAML not identical to expected YAML.") } - - /// Any type that is Encodable and contains an `Tag`value with the same coding key as YamlTagProviding - /// will encode to a yaml tag even though the type does not conform to YamlTagProviding + + /// Any type that is Encodable and contains an `Tag`value with the same coding key as + /// YamlTagProviding will encode to a yaml tag even though the type does not conform to + /// YamlTagProviding /// This may be unexpected func testTagEncoding_undeclaredBehavior_7() throws { struct Example: Codable, Hashable { @@ -45,7 +46,7 @@ class TagTolerancesTests: XCTestCase { let expectedYAML = """ ! extraneousValue: 3 - + """ let value = Example(yamlTag: "I-did-it-myyyyy-way", @@ -55,7 +56,7 @@ class TagTolerancesTests: XCTestCase { let producedYAML = try encoder.encode(value) XCTAssertEqual(producedYAML, expectedYAML, "Produced YAML not identical to expected YAML.") } - + /// Tags are oddly permissive, but some characters do get escaped /// This may be unexpected func testTagEncoding_undeclaredBehavior_4() throws { @@ -63,38 +64,38 @@ class TagTolerancesTests: XCTestCase { var yamlTag: Tag? var extraneousValue: Int } - + let expectedYAML = """ ! extraneousValue: 3 - + """ - + let value = Example(yamlTag: "I-did-it-[]-*-|-!-()way", extraneousValue: 3) - + let encoder = YAMLEncoder() let producedYAML = try encoder.encode(value) XCTAssertEqual(producedYAML, expectedYAML, "Produced YAML not identical to expected YAML.") } - /// Any type that is Decodable and contains an `Tag` value but with a coding key different from YamlTagProviding - /// will not decode an tag from the text representation. + /// Any type that is Decodable and contains an `Tag` value but with a coding key different from + /// YamlTagProviding will not decode an tag from the text representation. /// In this case a key not found error will be thrown during decoding /// This may be unexpected func testTagDecoding_undeclaredBehavior_1() throws { let sourceYAML = """ ! extraneousValue: 3 - + """ let decoder = YAMLDecoder() XCTAssertThrowsError(try decoder.decode(Example.self, from: sourceYAML)) // error is ^^ key not found, "myCustomTagDeclaration" } - - /// Any type that is Decodable and contains an `Tag` value but with a coding key different from YamlTagProviding - /// will not decode an tag from the text representation. + + /// Any type that is Decodable and contains an `Tag` value but with a coding key different from + /// YamlTagProviding will not decode an tag from the text representation. /// This may be unexpected func testTagDecoding_undeclaredBehavior_6() throws { struct Example: Codable, Hashable { @@ -104,17 +105,17 @@ class TagTolerancesTests: XCTestCase { let sourceYAML = """ ! extraneousValue: 3 - + """ - + let expectedValue = Example(myCustomTagDeclaration: nil, extraneousValue: 3) - + let decoder = YAMLDecoder() let decodedValue = try decoder.decode(Example.self, from: sourceYAML) XCTAssertEqual(decodedValue, expectedValue, "\(Example.self) did not round-trip to an equal value.") } - + /// Any type that is Decodable and contains an `Tag` value with the same coding key as YamlTagProviding /// will decode an tag from the text representatio even though the type does not conform to YamlTagCoding. /// This may be unexpected @@ -126,17 +127,17 @@ class TagTolerancesTests: XCTestCase { let sourceYAML = """ ! extraneousValue: 3 - + """ - + let expectedValue = Example(yamlTag: "a-different-tag", extraneousValue: 3) - + let decoder = YAMLDecoder() let decodedValue = try decoder.decode(Example.self, from: sourceYAML) XCTAssertEqual(decodedValue, expectedValue, "\(Example.self) did not round-trip to an equal value.") } - + /// Any type that is Decodable and contains an `Tag` value but with a coding key different from YamlTagProviding /// will not decode an tag from the text representation. /// This is expected behavior, but in a strange situation. @@ -145,17 +146,17 @@ class TagTolerancesTests: XCTestCase { ! extraneousValue: 3 myCustomTagDeclaration: deliver-us-from-evil - + """ let expectedValue = Example(myCustomTagDeclaration: "deliver-us-from-evil", extraneousValue: 3) - + let decoder = YAMLDecoder() let decodedValue = try decoder.decode(Example.self, from: sourceYAML) XCTAssertEqual(decodedValue, expectedValue, "\(Example.self) did not round-trip to an equal value.") - + } - + /// Any type that is Decodable and contains an `Tag` value but with a coding key different from YamlTagProviding /// will not decode an tag from the text representation. /// This is expected behavior, but in a strange situation. @@ -164,16 +165,16 @@ class TagTolerancesTests: XCTestCase { ! extraneousValue: 3 myCustomTagDeclaration: "deliver us from |()evil" - + """ - + let expectedValue = Example(myCustomTagDeclaration: "deliver us from |()evil", extraneousValue: 3) - + let decoder = YAMLDecoder() let decodedValue = try decoder.decode(Example.self, from: sourceYAML) XCTAssertEqual(decodedValue, expectedValue, "\(Example.self) did not round-trip to an equal value.") - + } - + }