The scalar event now carries the scanner token's end mark, so a located scalar's range spans the whole token (end exclusive, one past the last character) instead of being start-anchored. This gives consumers exact highlight widths and is the prerequisite for surgical, position-based text edits. Only the scalar event case gained a field; the value tree, its equality, and the event dump (which compares value and style, not marks) are unchanged, so the canonical-event and whole-suite gates stay green (343 tests).
85 lines
3.5 KiB
Swift
85 lines
3.5 KiB
Swift
import Foundation
|
|
@testable import PureYAML
|
|
import Testing
|
|
|
|
/// Proves the opt-in located parse (``PureYAML/parseLocated(_:)`` and
|
|
/// ``PureYAML/parseStreamLocated(_:)``) is correct in two ways: its projected value is identical
|
|
/// to a plain parse for every case in the official suite (so its tree shape never drifts from the
|
|
/// value tree, even through merge keys and aliases), and it reports exact source coordinates on a
|
|
/// hand-checked document. The plain value path is untouched, so the conformance gates are unaffected.
|
|
@Suite("Located Value")
|
|
struct LocatedValueConformanceTests {
|
|
@Test("A located parse projects back to exactly the plain value")
|
|
func test_locatedProjectsToPlainValue() throws {
|
|
let yaml = """
|
|
name: web
|
|
port: 8080
|
|
items:
|
|
- first
|
|
- second
|
|
nested:
|
|
key: value
|
|
"""
|
|
let located = try PureYAML.parseLocated(yaml)
|
|
let plain = try PureYAML.parse(yaml)
|
|
#expect(located.value == plain)
|
|
}
|
|
|
|
@Test("A located parse reports exact line and column for each node")
|
|
func test_locatedReportsExactCoordinates() throws {
|
|
let yaml = """
|
|
name: web
|
|
port: 8080
|
|
items:
|
|
- first
|
|
- second
|
|
"""
|
|
guard case let .mapping(pairs, mappingRange) = try PureYAML.parseLocated(yaml) else {
|
|
Issue.record("expected a mapping at the document root")
|
|
return
|
|
}
|
|
// The mapping spans from its first key.
|
|
#expect(mappingRange.start.line == 1)
|
|
#expect(mappingRange.start.column == 1)
|
|
|
|
// name: web -> key at 1:1, value spanning columns 7..10 (end exclusive, one past 'web')
|
|
let name = pairs[0]
|
|
#expect(name.keyNode.stringValue == "name")
|
|
#expect(name.keyRange.start == .init(line: 1, column: 1))
|
|
#expect(name.value.range.start == .init(line: 1, column: 7))
|
|
#expect(name.value.range.end == .init(line: 1, column: 10))
|
|
|
|
// port: 8080 -> key at 2:1, value at 2:7
|
|
let port = pairs[1]
|
|
#expect(port.keyRange.start == .init(line: 2, column: 1))
|
|
#expect(port.value.range.start == .init(line: 2, column: 7))
|
|
|
|
// items: a sequence whose items sit at 4:5 and 5:5
|
|
guard case let .sequence(items, _) = pairs[2].value else {
|
|
Issue.record("expected a sequence for 'items'")
|
|
return
|
|
}
|
|
#expect(items.count == 2)
|
|
#expect(items[0].range.start == .init(line: 4, column: 5))
|
|
#expect(items[1].range.start == .init(line: 5, column: 5))
|
|
#expect(items[0].value == .string("first"))
|
|
}
|
|
|
|
@Test("Every passing suite case projects to the same value as a plain parse")
|
|
func test_locatedMatchesPlainParseAcrossSuite() throws {
|
|
guard ProcessInfo.processInfo.environment["PUREYAML_RUN_YAML_TEST_SUITE"] == "1" else {
|
|
return
|
|
}
|
|
let manifest = try YAMLTestSuiteManifest.load()
|
|
for testCase in manifest.cases where testCase.expected == "parse-ok" && testCase.status == "passing" {
|
|
let yaml = try testCase.loadInput()
|
|
// A passing parse-ok case parses cleanly; the located projection must match exactly.
|
|
guard let plain = try? PureYAML.parseStream(yaml).map(\.value) else {
|
|
continue
|
|
}
|
|
let located = try PureYAML.parseStreamLocated(yaml).map(\.value)
|
|
#expect(located == plain, "\(testCase.id) \"\(testCase.name)\": located projection diverged from plain parse")
|
|
}
|
|
}
|
|
}
|