@testable import PureYAML import Testing /// End-to-end event production through the ported parser state machine. Events are /// rendered to the YAML test-suite "yeast" format (mark-independent) so the /// expectations read like the canonical `test.event` files. @Suite("LibYAML Parser") struct LibYAMLParserTests { private func events(_ input: String) throws -> [String] { var parser = try PureYAML.Parsing.LibYAML.Parser(input) return try parser.parse().map(render) } private func render(_ event: PureYAML.Parsing.Event) -> String { switch event { case .streamStart: "+STR" case .streamEnd: "-STR" case let .documentStart(explicit, _): explicit ? "+DOC ---" : "+DOC" case let .documentEnd(explicit, _): explicit ? "-DOC ..." : "-DOC" case let .sequenceStart(anchor, tag, style, _): "+SEQ" + (style == .flow ? " []" : "") + properties(anchor, tag) case .sequenceEnd: "-SEQ" case let .mappingStart(anchor, tag, style, _): "+MAP" + (style == .flow ? " {}" : "") + properties(anchor, tag) case .mappingEnd: "-MAP" case let .scalar(value, anchor, tag, style, _, _): "=VAL" + properties(anchor, tag) + " " + styleChar(style) + escape(value) case let .alias(anchor, _): "=ALI *" + anchor } } private func properties(_ anchor: String?, _ tag: String?) -> String { (anchor.map { " &" + $0 } ?? "") + (tag.map { " <" + $0 + ">" } ?? "") } private func styleChar(_ style: PureYAML.Parsing.ScalarStyle) -> String { switch style { case .plain: ":" case .singleQuoted: "'" case .doubleQuoted: "\"" case .literal: "|" case .folded: ">" } } private func escape(_ value: String) -> String { var output = "" for scalar in value.unicodeScalars { switch scalar { case "\\": output += "\\\\" case "\n": output += "\\n" case "\t": output += "\\t" case "\r": output += "\\r" default: output.unicodeScalars.append(scalar) } } return output } @Test("a bare root scalar") func test_rootScalar() throws { #expect(try events("x\n") == ["+STR", "+DOC", "=VAL :x", "-DOC", "-STR"]) } @Test("a block sequence") func test_blockSequence() throws { #expect(try events("- a\n- b\n") == [ "+STR", "+DOC", "+SEQ", "=VAL :a", "=VAL :b", "-SEQ", "-DOC", "-STR", ]) } @Test("a block mapping") func test_blockMapping() throws { #expect(try events("a: b\n") == [ "+STR", "+DOC", "+MAP", "=VAL :a", "=VAL :b", "-MAP", "-DOC", "-STR", ]) } @Test("a mapping nested in a sequence entry with an empty value") func test_nestedMappingEmptyValue() throws { // The original block-mapping bug: `- a:` / ` b: c` is {a: null, b: c}. #expect(try events("- a:\n b: c\n") == [ "+STR", "+DOC", "+SEQ", "+MAP", "=VAL :a", "=VAL :", "=VAL :b", "=VAL :c", "-MAP", "-SEQ", "-DOC", "-STR", ]) } @Test("a flow sequence") func test_flowSequence() throws { #expect(try events("[a, b]\n") == [ "+STR", "+DOC", "+SEQ []", "=VAL :a", "=VAL :b", "-SEQ", "-DOC", "-STR", ]) } @Test("a flow mapping") func test_flowMapping() throws { #expect(try events("{a: b}\n") == [ "+STR", "+DOC", "+MAP {}", "=VAL :a", "=VAL :b", "-MAP", "-DOC", "-STR", ]) } @Test("an anchor and an alias") func test_anchorAlias() throws { #expect(try events("a: &x 1\nb: *x\n") == [ "+STR", "+DOC", "+MAP", "=VAL :a", "=VAL &x :1", "=VAL :b", "=ALI *x", "-MAP", "-DOC", "-STR", ]) } @Test("a shorthand tag resolves against the default !! handle") func test_shorthandTag() throws { #expect(try events("!!str x\n") == [ "+STR", "+DOC", "=VAL :x", "-DOC", "-STR", ]) } @Test("a custom %TAG directive resolves a shorthand") func test_tagDirective() throws { #expect(try events("%TAG !e! tag:example.com,2000:\n---\n!e!foo x\n") == [ "+STR", "+DOC ---", "=VAL :x", "-DOC", "-STR", ]) } @Test("a version directive opens an explicit document") func test_versionDirective() throws { #expect(try events("%YAML 1.2\n---\nx\n") == [ "+STR", "+DOC ---", "=VAL :x", "-DOC", "-STR", ]) } @Test("multiple explicit documents") func test_multipleDocuments() throws { #expect(try events("---\na\n---\nb\n") == [ "+STR", "+DOC ---", "=VAL :a", "-DOC", "+DOC ---", "=VAL :b", "-DOC", "-STR", ]) } @Test("a quoted scalar is not plain-implicit") func test_quotedScalar() throws { #expect(try events("\"x\"\n") == [ "+STR", "+DOC", "=VAL \"x", "-DOC", "-STR", ]) } }