Closes two of the four emitter gaps a feature-parity audit against Yams found (PureYAMLYamsComparison's FEATURE-PARITY.md): no configurable indentation and no sorted-key output. indentWidth replaces every hardcoded indent + 2 in Dumper.swift/ Dumper.Mapping.swift, verified with round-trip tests since indentation is purely a rendering choice and must never change what a document means. sortKeys sorts a mapping's pairs by their string key in both block and flow style, but only when every key in that mapping is a plain string; a mapping with any complex (mapping/sequence) key stays in insertion order, since there is no well-defined ordering across mixed key kinds. Off by default, since insertion order is what makes a duplicate-key-preserving Mapping round-trip meaningfully in the first place. width-based line wrapping and canonical mode are each a correctness surface of their own and are deliberately left for later, not folded into this.
118 lines
4.0 KiB
Swift
118 lines
4.0 KiB
Swift
@testable import PureYAML
|
|
import Testing
|
|
|
|
/// Coverage for `Emitting.Options.indentWidth` and `.sortKeys` (issue #10's
|
|
/// first slice: `width`-based line wrapping and canonical mode are separate,
|
|
/// larger correctness surfaces of their own and remain open, not silently
|
|
/// dropped).
|
|
@Suite("Emitter indent width and sortKeys")
|
|
struct EmitterIndentAndSortKeysTests {
|
|
@Test("A custom indent width nests block sequences and mappings consistently")
|
|
func test_customIndentWidth() throws {
|
|
let value = PureYAML.Model.Value.mapping(.init([
|
|
.init(key: "servers", value: .sequence([
|
|
.mapping(.init([
|
|
.init(key: "url", value: .string("/")),
|
|
])),
|
|
])),
|
|
]))
|
|
|
|
let dumped = PureYAML.dump(value, options: .init(indentWidth: 4))
|
|
#expect(dumped == """
|
|
servers:
|
|
-
|
|
url: "/"
|
|
|
|
""")
|
|
|
|
// Indentation is purely a rendering choice: reparsing must recover
|
|
// the identical value regardless of indent width.
|
|
let reparsed = try PureYAML.parse(dumped)
|
|
#expect(reparsed == value)
|
|
}
|
|
|
|
@Test("Indent width 1 is the tightest that still round-trips")
|
|
func test_indentWidthOne() throws {
|
|
let value = PureYAML.Model.Value.mapping(.init([
|
|
.init(key: "a", value: .mapping(.init([
|
|
.init(key: "b", value: .int(1)),
|
|
]))),
|
|
]))
|
|
|
|
let dumped = PureYAML.dump(value, options: .init(indentWidth: 1))
|
|
let reparsed = try PureYAML.parse(dumped)
|
|
#expect(reparsed == value)
|
|
}
|
|
|
|
@Test("sortKeys orders string-keyed mapping pairs lexicographically")
|
|
func test_sortKeysOrdersStringKeys() {
|
|
let value = PureYAML.Model.Value.mapping(.init([
|
|
.init(key: "zebra", value: .int(1)),
|
|
.init(key: "apple", value: .int(2)),
|
|
.init(key: "mango", value: .int(3)),
|
|
]))
|
|
|
|
#expect(PureYAML.dump(value, options: .init(sortKeys: true)) == """
|
|
apple: 2
|
|
mango: 3
|
|
zebra: 1
|
|
|
|
""")
|
|
// Off by default: insertion order is preserved unless opted in.
|
|
#expect(PureYAML.dump(value) == """
|
|
zebra: 1
|
|
apple: 2
|
|
mango: 3
|
|
|
|
""")
|
|
}
|
|
|
|
@Test("sortKeys applies to nested mappings and flow mappings too")
|
|
func test_sortKeysAppliesToNestedAndFlowMappings() {
|
|
let value = PureYAML.Model.Value.mapping(.init([
|
|
.init(key: "outer", value: .mapping(.init([
|
|
.init(key: "z", value: .int(1)),
|
|
.init(key: "a", value: .int(2)),
|
|
]))),
|
|
]))
|
|
|
|
#expect(PureYAML.dump(value, options: .init(sortKeys: true)) == """
|
|
outer:
|
|
a: 2
|
|
z: 1
|
|
|
|
""")
|
|
|
|
let flowOptions = PureYAML.Emitting.Options(collectionStyle: .flow, sortKeys: true)
|
|
#expect(PureYAML.dump(.mapping(.init([
|
|
.init(key: "z", value: .int(1)),
|
|
.init(key: "a", value: .int(2)),
|
|
])), options: flowOptions) == "{\"a\": 2, \"z\": 1}\n")
|
|
}
|
|
|
|
@Test("sortKeys leaves a mapping with any non-string key in insertion order")
|
|
func test_sortKeysSkipsMixedKeyMappings() {
|
|
let value = PureYAML.Model.Value.mapping(.init([
|
|
.init(keyNode: .string("zebra"), value: .int(1)),
|
|
.init(keyNode: .sequence([.int(1), .int(2)]), value: .int(2)),
|
|
.init(keyNode: .string("apple"), value: .int(3)),
|
|
]))
|
|
|
|
let sorted = PureYAML.dump(value, options: .init(sortKeys: true))
|
|
let unsorted = PureYAML.dump(value)
|
|
#expect(sorted == unsorted)
|
|
}
|
|
|
|
@Test("sortKeys still round-trips a mapping with duplicate keys")
|
|
func test_sortKeysRoundTripsDuplicateKeys() throws {
|
|
let value = PureYAML.Model.Value.mapping(.init([
|
|
.init(key: "a", value: .int(1)),
|
|
.init(key: "a", value: .int(2)),
|
|
]))
|
|
|
|
let dumped = PureYAML.dump(value, options: .init(sortKeys: true))
|
|
let reparsed = try PureYAML.parse(dumped)
|
|
#expect(reparsed == value)
|
|
}
|
|
}
|