PureYAML/Tests/YAMLDirectiveTests.swift
Mihaela M 27c423dbb9 fix(parsing): require an explicit document after a directive
The YAML grammar requires every directive to head an explicit document,
so a directive must be followed by a '---' marker. The scanner accepted a
directive with no document at all: '%YAML 1.2' alone, or '%YAML 1.2' then
a '...' document-end marker, parsed without error.

The scanner now records the line of a directive awaiting its document,
clears it at a document start, and rejects a still-pending directive at a
document-end marker and at stream end. Both scan drivers finalize through
the same stream-end path, so the check lives in one helper called from
each. The rule applies to any directive, including an unrecognized one
(which is otherwise still ignored); the directive test that relied on an
unrecognized directive parsing without a '---' is corrected to include
one, since that leniency is not permitted by the grammar.

Measured against the official YAML test-suite, parse-level conformance
rises from 253/333 (76.0%) to 255/333 (76.6%) with no regression.
2026-06-27 17:18:47 +02:00

41 lines
1.7 KiB
Swift

@testable import PureYAML
import Testing
@Suite("%YAML directive validation")
struct YAMLDirectiveTests {
@Test("A malformed or repeated %YAML directive is rejected", arguments: [
"%YAML 1.2 foo\n---\nx\n", // an extra parameter after the version
"%YAML 1.2\n%YAML 1.2\n---\nx\n", // a second %YAML directive
"%YAML 1.3\n---\nx\n", // an unsupported version
])
func test_invalidYAMLDirectiveIsRejected(_ yaml: String) throws {
#expect(throws: PureYAML.Parsing.ParseError.self) {
try PureYAML.parse(yaml)
}
}
@Test("A well-formed %YAML directive, with an optional trailing comment, is accepted")
func test_validYAMLDirectiveAccepted() throws {
let expected = PureYAML.Model.Value.string("x")
#expect(try PureYAML.parse("%YAML 1.2\n---\nx\n") == expected)
#expect(try PureYAML.parse("%YAML 1.2 # version\n---\nx\n") == expected)
}
@Test("A directive with no following document is rejected", arguments: [
"%YAML 1.2\n", // directive then end of stream
"%YAML 1.2\n...\n", // directive then a document-end marker
"%TAG !e! tag:example,0:\n", // any directive, not just %YAML
])
func test_directiveWithoutDocumentRejected(_ yaml: String) throws {
#expect(throws: PureYAML.Parsing.ParseError.self) {
try PureYAML.parse(yaml)
}
}
@Test("A directive followed by an explicit document start is accepted")
func test_directiveWithDocumentAccepted() throws {
#expect(try PureYAML.parse("%YAML 1.2\n---\n") == .null)
#expect(try PureYAML.parse("%TAG !e! tag:example,0:\n---\nx\n") == .string("x"))
}
}