PureYAML/Tests/StringCharactersTests.swift
Mihaela Mihaljevic be81f6358d fix(emitting): write the characters a reader cannot give back as escapes
Control characters, DEL, the C1 controls, U+2028, U+2029, U+FEFF, U+FFFE and U+FFFF were written as they are, where the reader refused them, folded U+0085, U+2028 and U+2029 to a space, or dropped a leading U+FEFF. They are now written as the escape that stands for each, in a quoted string, and never plain or in a literal block.
2026-11-15 12:12:38 +01:00

42 lines
2.9 KiB
Swift

@testable import PureYAML
import Testing
/// What a string holds is what it comes back as. The meaning of each text here is written down (YAML 1.2.2, section 5.7: an escape in a double-quoted scalar stands for
/// any character, including a control character), and not taken from what the reader says of it.
@Suite("String characters")
struct StringCharactersTests {
/// Characters that are not printable in a YAML stream and so are not written bare, with the escape that stands for each.
static let escaped: [(source: String, character: String)] = [
("\\0", "\u{0}"), ("\\x01", "\u{1}"), ("\\u0001", "\u{1}"), ("\\U00000001", "\u{1}"), ("\\x1f", "\u{1F}"), ("\\u001F", "\u{1F}"),
("\\x7f", "\u{7F}"), ("\\u007F", "\u{7F}"), ("\\x80", "\u{80}"), ("\\x9f", "\u{9F}"), ("\\N", "\u{85}"), ("\\x85", "\u{85}"),
("\\L", "\u{2028}"), ("\\u2028", "\u{2028}"), ("\\P", "\u{2029}"), ("\\u2029", "\u{2029}"), ("\\uFEFF", "\u{FEFF}"),
]
@Test("An escape in a double-quoted scalar stands for its character, whatever the character is", arguments: escaped)
func test_escapeStandsForItsCharacter(_ escape: (source: String, character: String)) throws {
#expect(try PureYAML.parse("\"\(escape.source)\"") == .string(escape.character))
#expect(try PureYAML.parse("\"a\(escape.source)b\"") == .string("a\(escape.character)b"))
#expect(try PureYAML.parse("k: \"\(escape.source)\"") == .mapping(.init([.init(key: "k", value: .string(escape.character))])))
}
/// Strings a writer has to quote or escape, so that the reader gets the same string back.
static let strings: [String] = [
"", " ", "x", "a b", "true", "null", "1", "1.5", "- x", "a: b", "# c", "\u{0}", "\u{1}", "\u{1F}", "\u{7F}", "\u{80}", "\u{85}", "\u{9F}", "\u{A0}",
"\u{2028}", "\u{2029}", "a\u{2028}b", "a\u{85}b", "\u{FEFF}", "\u{FEFF}x", "x\u{FEFF}", "a\u{FEFF}b", "\u{FFFE}", "\u{FFFF}", "tab\there", "line\nbreak",
"cr\rlf", "\r\n", "\u{200B}", "é", "日本語", "😀",
]
@Test("A string that is dumped is read back as the same string", arguments: strings)
func test_dumpedStringIsReadBack(_ string: String) throws {
let value = PureYAML.Model.Value.string(string)
let dumped = PureYAML.dump(value)
#expect(try PureYAML.parse(dumped) == value, "\(string.unicodeScalars.map(\.value)) dumped as \(dumped.debugDescription)")
let inside = PureYAML.Model.Value.mapping(.init([.init(key: "k", value: value)]))
let dumpedInside = PureYAML.dump(inside)
#expect(try PureYAML.parse(dumpedInside) == inside, "\(string.unicodeScalars.map(\.value)) dumped as \(dumpedInside.debugDescription)")
let asKey = PureYAML.Model.Value.mapping(.init([.init(key: string, value: .int(1))]))
let dumpedKey = PureYAML.dump(asKey)
#expect(try PureYAML.parse(dumpedKey) == asKey, "\(string.unicodeScalars.map(\.value)) dumped as \(dumpedKey.debugDescription)")
}
}