The ported engine wins every conflict: main's post-fork patches to the hand-rolled Scanner and TokenEventParser land on deleted files and are dropped; their behaviors are carried by the port and guarded by the whole-suite conformance gate (402/402 passing). Merge adaptations, all gates green (style, namespacing, forbidden patterns, changelog, roadmap, swiftformat, swiftlint strict, build, full test suite, whole-suite conformance sweep): - PureYAMLJSON representability rules move onto the Check algebra, replacing the retired legacy Rule type. - The permissive tag-handle pre-pass is removed; the port rejects an undefined handle at parse time (suite case QLJ7) and the validation report carries it as a parse diagnostic. - EventStreamComposer's nested types split one-per-file per the namespacing gate. - Tests pinned to old-engine behavior were re-verified against the reference and updated: libyaml rejects an explicit-key continuation ending at a value colon, and suite case QB6E requires rejecting a quoted continuation at column 0. - CHANGELOG unreleased entries from both lines are preserved in two groups with a provenance note. Closes #1, closes #3
250 lines
11 KiB
Swift
250 lines
11 KiB
Swift
@testable import PureYAML
|
|
import Testing
|
|
|
|
@Suite("Parsing Multiline Scalars")
|
|
struct ParsingMultilineScalarTests {
|
|
@Test("Parses multiline plain scalars in mappings")
|
|
func test_multilinePlainScalarsInMappings() throws {
|
|
let root = try requireMapping(PureYAML.parse(
|
|
"""
|
|
columns:
|
|
- description: CreationTimestamp is a timestamp representing the server time when
|
|
this object was created. It is not guaranteed to be set in happens-before
|
|
order across separate operations.
|
|
jsonPath: .metadata.creationTimestamp
|
|
""",
|
|
))
|
|
|
|
let expectedDescription = [
|
|
"CreationTimestamp is a timestamp representing the server time when this object was created.",
|
|
"It is not guaranteed to be set in happens-before order across separate operations.",
|
|
].joined(separator: " ")
|
|
let column = root?.sequence("columns")?.first?.mapping
|
|
#expect(column?["description"] == .string(expectedDescription))
|
|
#expect(column?["jsonPath"] == .string(".metadata.creationTimestamp"))
|
|
#expect(column?["missing"] == nil)
|
|
}
|
|
|
|
@Test("Parses multiline plain scalars before the next sequence entry")
|
|
func test_multilinePlainScalarsBeforeNextSequenceEntry() throws {
|
|
let value = try PureYAML.parse(
|
|
"""
|
|
- name: code-quality
|
|
description: Insights into reliability, maintainability, and efficiency of your
|
|
codebase.
|
|
- name: codespaces
|
|
description: Endpoints to manage Codespaces using the REST API.
|
|
""",
|
|
)
|
|
|
|
let tags = requireSequence(value)
|
|
let first = tags?.first?.mapping
|
|
let second = tags?.dropFirst().first?.mapping
|
|
|
|
#expect(first?["name"] == .string("code-quality"))
|
|
#expect(first?["description"] == .string("Insights into reliability, maintainability, and efficiency of your codebase."))
|
|
#expect(second?["name"] == .string("codespaces"))
|
|
#expect(second?["description"] == .string("Endpoints to manage Codespaces using the REST API."))
|
|
#expect(first?["missing"] == nil)
|
|
}
|
|
|
|
@Test("Parses multiline plain scalar sequence items")
|
|
func test_multilinePlainScalarSequenceItems() throws {
|
|
let root = try requireMapping(PureYAML.parse(
|
|
"""
|
|
examples:
|
|
- Updated instructions to help the team with planning and coordination
|
|
tasks
|
|
next: done
|
|
""",
|
|
))
|
|
|
|
#expect(root?.sequence("examples") == [
|
|
.string("Updated instructions to help the team with planning and coordination tasks"),
|
|
])
|
|
#expect(root?["next"] == .string("done"))
|
|
#expect(root?["missing"] == nil)
|
|
}
|
|
|
|
@Test("Parses multiline plain scalar continuations that start with quotes")
|
|
func test_multilinePlainScalarContinuationsThatStartWithQuotes() throws {
|
|
let root = try requireMapping(PureYAML.parse(
|
|
"""
|
|
get:
|
|
description: Gets a team using the team's slug. To create the slug, GitHub replaces
|
|
special characters in the name string, changes all words to lowercase, and
|
|
replaces spaces with a `-` separator and adds the "ent:" prefix. For example,
|
|
"My TEam Näme" would become `ent:my-team-name`.
|
|
tags:
|
|
- enterprise-teams
|
|
""",
|
|
))
|
|
|
|
let expectedDescription = [
|
|
"Gets a team using the team's slug.",
|
|
"To create the slug, GitHub replaces special characters in the name string,",
|
|
"changes all words to lowercase, and replaces spaces with a `-` separator",
|
|
"and adds the \"ent:\" prefix. For example,",
|
|
"\"My TEam Näme\" would become `ent:my-team-name`.",
|
|
].joined(separator: " ")
|
|
let get = root?.mapping("get")
|
|
#expect(get?["description"] == .string(expectedDescription))
|
|
#expect(get?.sequence("tags") == [.string("enterprise-teams")])
|
|
#expect(get?["missing"] == nil)
|
|
}
|
|
|
|
@Test("Parses multiline plain scalar continuations that start with flow markers")
|
|
func test_multilinePlainScalarContinuationsThatStartWithFlowMarkers() throws {
|
|
let root = try requireMapping(PureYAML.parse(
|
|
"""
|
|
selected_repository_ids:
|
|
description: An array of repository ids that can access the organization
|
|
secret. You can manage the list of
|
|
selected repositories using the [List selected repositories for
|
|
an organization secret](https://docs.example.com/list),
|
|
[Set selected repositories for an organization secret](https://docs.example.com/set),
|
|
and [Remove selected repository from an organization secret](https://docs.example.com/remove)
|
|
endpoints.
|
|
items:
|
|
type: integer
|
|
""",
|
|
))
|
|
|
|
let expectedDescription = [
|
|
"An array of repository ids that can access the organization secret.",
|
|
"You can manage the list of selected repositories using the",
|
|
"[List selected repositories for an organization secret](https://docs.example.com/list),",
|
|
"[Set selected repositories for an organization secret](https://docs.example.com/set),",
|
|
"and [Remove selected repository from an organization secret](https://docs.example.com/remove) endpoints.",
|
|
].joined(separator: " ")
|
|
let selected = root?.mapping("selected_repository_ids")
|
|
#expect(selected?["description"] == .string(expectedDescription))
|
|
#expect(selected?.mapping("items")?["type"] == .string("integer"))
|
|
#expect(selected?["missing"] == nil)
|
|
}
|
|
|
|
@Test("Parses flow-marker continuations after previous sequence indentation")
|
|
func test_flowMarkerContinuationsAfterPreviousSequenceIndentation() throws {
|
|
let root = try requireMapping(PureYAML.parse(
|
|
"""
|
|
schema:
|
|
enum:
|
|
- all
|
|
- private
|
|
selected_repository_ids:
|
|
description: An array of repository ids that can access the organization
|
|
[Set selected repositories](https://docs.example.com/set),
|
|
and more.
|
|
items:
|
|
type: integer
|
|
""",
|
|
))
|
|
|
|
let selected = root?.mapping("schema")?.mapping("selected_repository_ids")
|
|
#expect(
|
|
selected?["description"] ==
|
|
.string("An array of repository ids that can access the organization [Set selected repositories](https://docs.example.com/set), and more."),
|
|
)
|
|
#expect(selected?.mapping("items")?["type"] == .string("integer"))
|
|
#expect(selected?["missing"] == nil)
|
|
#expect(root?.mapping("schema")?.sequence("enum") == [.string("all"), .string("private")])
|
|
}
|
|
|
|
@Test("Parses colon-style markup inside multiline plain scalars")
|
|
func test_colonStyleMarkupInsideMultilinePlainScalars() throws {
|
|
let root = try requireMapping(PureYAML.parse(
|
|
"""
|
|
label:
|
|
description: The name of the label. Emoji can be added to label
|
|
names, using either native emoji or colon-style markup. For example,
|
|
typing `:strawberry:` will render the emoji . For a full list of available emoji and codes.
|
|
color: f29513
|
|
""",
|
|
))
|
|
|
|
let expectedDescription = [
|
|
"The name of the label. Emoji can be added to label names,",
|
|
"using either native emoji or colon-style markup. For example,",
|
|
"typing `:strawberry:` will render the emoji",
|
|
".",
|
|
"For a full list of available emoji and codes.",
|
|
].joined(separator: " ")
|
|
#expect(root?.mapping("label")?["description"] == .string(expectedDescription))
|
|
#expect(root?.mapping("label")?["color"] == .string("f29513"))
|
|
#expect(root?.mapping("label")?["missing"] == nil)
|
|
}
|
|
|
|
@Test("A bare multi-line plain scalar at the document root folds")
|
|
func test_rootMultilinePlainScalarFolds() throws {
|
|
// YAML test suite 9YRD: a plain scalar is the document's root node and spans
|
|
// several lines. A single line break folds to a space, a blank line folds to
|
|
// a newline, and per-line leading/trailing whitespace is stripped.
|
|
#expect(try PureYAML.parse("a\nb \n c\nd\n\ne\n") == .string("a b c d\ne"))
|
|
// EX5H: the same content after an explicit `---` document-start marker.
|
|
#expect(try PureYAML.parse("---\na\nb \n c\nd\n\ne\n") == .string("a b c d\ne"))
|
|
// 82AN: `---word1` is plain content (the `---` is not a marker because it is
|
|
// not followed by a space), and the scalar continues onto the next line.
|
|
#expect(try PureYAML.parse("---word1\nword2\n") == .string("---word1 word2"))
|
|
}
|
|
|
|
@Test("A multi-line plain scalar as a mapping value at root folds")
|
|
func test_rootMappingValueMultilinePlainScalarFolds() throws {
|
|
// YAML test suite 4CQQ: the value indented on its own lines folds even though
|
|
// each continuation line sits at the same column as the first content line.
|
|
let root = try requireMapping(PureYAML.parse(
|
|
"""
|
|
plain:
|
|
This unquoted scalar
|
|
spans many lines.
|
|
""",
|
|
))
|
|
#expect(root?["plain"] == .string("This unquoted scalar spans many lines."))
|
|
}
|
|
|
|
@Test("A genuine trailing comment after a plain value is still ignored")
|
|
func test_plainValueTrailingCommentStillIgnored() throws {
|
|
let root = try requireMapping(PureYAML.parse(
|
|
"""
|
|
kind: widget # this is a comment
|
|
# whole-line comment
|
|
count: 3
|
|
""",
|
|
))
|
|
|
|
#expect(root?["kind"] == .string("widget"))
|
|
#expect(root?["count"] == .int(3))
|
|
}
|
|
|
|
@Test("Folds a plain scalar value that begins on its own line under a key")
|
|
func test_plainScalarValueOnOwnLineFolds() throws {
|
|
// The value sits one line below the key and spans two lines at the same indent. Its
|
|
// continuation lines are indented more than the mapping (not more than the value's own
|
|
// first line), so both must fold into one scalar.
|
|
let root = try requireMapping(PureYAML.parse(
|
|
"""
|
|
plain:
|
|
This unquoted scalar
|
|
spans many lines.
|
|
""",
|
|
))
|
|
|
|
#expect(root?["plain"] == .string("This unquoted scalar spans many lines."))
|
|
}
|
|
|
|
@Test("Folds a multi-line plain scalar nested two levels deep")
|
|
func test_plainScalarValueOnOwnLineFoldsWhenNested() throws {
|
|
let root = try requireMapping(PureYAML.parse(
|
|
"""
|
|
outer:
|
|
inner:
|
|
first part
|
|
second part
|
|
""",
|
|
))
|
|
|
|
let inner = root?.mapping("outer")
|
|
#expect(inner?["inner"] == .string("first part second part"))
|
|
}
|
|
}
|