PureYAML/Tests/ParsingTests.swift

160 lines
5.1 KiB
Swift

@testable import PureYAML
import Testing
@Suite("Parsing")
struct ParsingTests {
struct ScalarCase {
var yaml: String
var expected: PureYAML.Model.Value
}
@Test("Parses block mappings with common scalars")
func parsesBlockMappingWithCommonScalars() throws {
let root = try requireMapping(PureYAML.parse(
"""
openapi: 3.1.0
title: "Example API"
active: true
retries: 3
ratio: 3.1
missing: null
""",
))
#expect(root?["openapi"] == .string("3.1.0"))
#expect(root?["title"] == .string("Example API"))
#expect(root?["active"] == .bool(true))
#expect(root?["retries"] == .int(3))
#expect(root?["ratio"] == .double(3.1))
#expect(root?["missing"] == .null)
}
@Test("Parses scalar spellings", arguments: [
ScalarCase(yaml: "value:", expected: .null),
ScalarCase(yaml: "value: ~", expected: .null),
ScalarCase(yaml: "value: NULL", expected: .null),
ScalarCase(yaml: "value: True", expected: .bool(true)),
ScalarCase(yaml: "value: FALSE", expected: .bool(false)),
ScalarCase(yaml: "value: -7", expected: .int(-7)),
ScalarCase(yaml: "value: 1e3", expected: .double(1000)),
ScalarCase(yaml: "value: plain text", expected: .string("plain text")),
])
func parsesScalarSpellings(testCase: ScalarCase) throws {
let root = try requireMapping(PureYAML.parse(testCase.yaml))
#expect(root?["value"] == testCase.expected)
}
@Test("Parses quoted strings and escapes")
func parsesQuotedStringsAndEscapes() throws {
let root = try requireMapping(PureYAML.parse(
"""
double: "line\\nnext\\tend"
single: 'it''s'
slash: "a\\/b"
""",
))
#expect(root?["double"] == .string("line\nnext\tend"))
#expect(root?["single"] == .string("it's"))
#expect(root?["slash"] == .string("a/b"))
}
@Test("Ignores comments outside quoted strings")
func ignoresCommentsOutsideQuotedStrings() throws {
let root = try requireMapping(PureYAML.parse(
"""
title: "Keep # inside" # remove outside
single: 'Keep # inside' # remove outside
plain: value#kept # remove outside
""",
))
#expect(root?["title"] == .string("Keep # inside"))
#expect(root?["single"] == .string("Keep # inside"))
#expect(root?["plain"] == .string("value#kept"))
}
@Test("Parses nested mappings and sequences")
func parsesNestedMappingsAndSequences() throws {
let root = try requireMapping(PureYAML.parse(
"""
paths:
/users:
get:
tags:
- Users
- Public
""",
))
guard
case let .mapping(paths)? = root?["paths"],
case let .mapping(users)? = paths["/users"],
case let .mapping(get)? = users["get"],
case let .sequence(tags)? = get["tags"]
else {
recordIssue("expected nested tags")
return
}
#expect(tags == [.string("Users"), .string("Public")])
}
@Test("Parses sequences of mappings")
func parsesSequenceOfMappings() throws {
let root = try requireMapping(PureYAML.parse(
"""
servers:
- url: /
description: Default
- url: https://example.com
description: Production
""",
))
guard
let servers = requireSequence(root?["servers"]),
servers.count == 2,
case let .mapping(first) = servers[0],
case let .mapping(second) = servers[1]
else {
recordIssue("expected server mappings")
return
}
#expect(first["url"] == .string("/"))
#expect(first["description"] == .string("Default"))
#expect(second["url"] == .string("https://example.com"))
#expect(second["description"] == .string("Production"))
}
@Test("Parses top-level sequences")
func parsesTopLevelSequences() throws {
let value = try PureYAML.parse(
"""
- one
- 2
- false
""",
)
#expect(value == .sequence([
.string("one"),
.int(2),
.bool(false),
]))
}
@Test("Reports exact parser errors")
func reportsExactParserErrors() {
expectParseError("", .emptyDocument)
expectParseError("# comment only", .emptyDocument)
expectParseError("\tkey: value", .tabIndentation(line: 1))
expectParseError("root:\n child: yes\n wrong: no", .unexpectedIndentation(line: 3))
expectParseError("- one\nkey: value", .mixedCollectionStyles(line: 2))
expectParseError("root:\n child: value\n dangling", .expectedMappingKey(line: 3))
expectParseError("name: \"open", .unterminatedQuotedString(line: 1))
expectParseError("name: 'open", .unterminatedQuotedString(line: 1))
}
}