PureYAML/Tests/PlainScalarRoundTripTests.swift
Mihaela Mihaljevic ae85ec97c9 fix(emitting): quote every string the parser reads as another type
The emitter kept its own list of reserved words and number shapes,
apart from the parser's, and the two disagreed: the string yes was
written plain and read back as a boolean, as were 0x1F, 1_000 and
.inf. The parser's reading of a plain scalar is now one type that
both use, and the emitter quotes what does not read back as a string.

Also quoted: the merge key, the document end marker, and a string
with a CR+LF break, which was written raw.
2026-10-28 23:32:40 +01:00

57 lines
3.3 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@testable import PureYAML
import Testing
/// A string the emitter writes without quotes must be read back as that same string. The parser decides what a plain word means (a boolean, a number, null,
/// or a string), so the emitter quotes every string the parser would read as anything else.
@Suite("Plain scalar round trip")
struct PlainScalarRoundTripTests {
/// Words and shapes that a reader may take for something other than a string, and shapes that break plain syntax.
static let corpus: [String] = [
"yes", "Yes", "YES", "no", "No", "NO", "on", "On", "ON", "off", "Off", "OFF", "y", "n", "Y", "N", "true", "True", "TRUE", "false", "False", "FALSE",
"null", "Null", "NULL", "~", "0", "-0", "+0", "1", "+1", "-1", "017", "0123", "0o17", "0O17", "0x1F", "0X1f", "0b101", "0B1", "-0x1F", "+0x1F", "1_000",
"1__0", "0_1", "_1", "1_", "1e3", "1E3", "1e+3", "1.5", "1.", ".5", "-.5", "+.5", ".inf", ".Inf", ".INF", "-.inf", "+.inf", ".nan", ".NaN", ".NAN", "inf",
"nan", "Infinity", "1e400", "0x", "0b", "0o", "9223372036854775807", "9223372036854775808", "-9223372036854775808", "12345678901234567890",
"2001-01-01", "12:30:45", "1:30", "<<", "=", "---", "...", "?", "- x", "-", "a: b", "a:", "a :", ":a", "#c", "a #c", "x#c", "!x", "&a", "*a", "|", ">",
"%x", "@x", "`x", "'x", "\"x", " x", "x ", "a\tb", "a\nb", "a\r\nb", "héllo", "✓", "a,b", "[a]", "{a}", "a]", "k: v", "x:y", "x: ", "", " ", "word",
"two words", "a-b", "a?b", "?x", "-x", "- ", "1 2", "1e", "e1", ".", "..", "+", "0.", "00", "0e0", "1_0.5", "١٢٣", "٣.٥",
]
private static let styles: [PureYAML.Emitting.CollectionStyle] = [.block, .flow]
private func roundTrip(_ value: PureYAML.Model.Value, _ style: PureYAML.Emitting.CollectionStyle) throws -> PureYAML.Model.Value {
let yaml = PureYAML.dump(value, options: .init(scalarStyle: .plainWhenSafe, collectionStyle: style))
return try PureYAML.parse(yaml)
}
@Test("A string at the root reads back as itself", arguments: corpus)
func root(text: String) throws {
for style in Self.styles {
#expect(try roundTrip(.string(text), style) == .string(text), "\(style): \(text.debugDescription)")
}
}
@Test("A string as a mapping value reads back as itself", arguments: corpus)
func mappingValue(text: String) throws {
let value = PureYAML.Model.Value.mapping(.init([.init(key: "k", value: .string(text))]))
for style in Self.styles {
#expect(try roundTrip(value, style) == value, "\(style): \(text.debugDescription)")
}
}
@Test("A string as a mapping key reads back as itself", arguments: corpus)
func mappingKey(text: String) throws {
let value = PureYAML.Model.Value.mapping(.init([.init(key: text, value: .int(1))]))
for style in Self.styles {
#expect(try roundTrip(value, style) == value, "\(style): \(text.debugDescription)")
}
}
@Test("A string in a sequence reads back as itself", arguments: corpus)
func sequenceItem(text: String) throws {
let value = PureYAML.Model.Value.sequence([.string(text), .string("after")])
for style in Self.styles {
#expect(try roundTrip(value, style) == value, "\(style): \(text.debugDescription)")
}
}
}