79 lines
3.1 KiB
Swift
79 lines
3.1 KiB
Swift
import Foundation
|
|
@testable import PureYAML
|
|
import Testing
|
|
|
|
@Suite("Parsing Nesting Limit")
|
|
struct ParsingNestingLimitTests {
|
|
/// Result of parsing on a dedicated, generously sized stack.
|
|
///
|
|
/// Driving the parser to its nesting limit takes more stack than a default
|
|
/// test-runner thread may have, so these tests run the parse on their own
|
|
/// thread with a large stack. That isolates the assertion (does the parser
|
|
/// reject the input with a diagnostic?) from the runner's thread-stack size.
|
|
private final class ParseOutcome: @unchecked Sendable {
|
|
var value: PureYAML.Model.Value?
|
|
var parseError: PureYAML.Parsing.ParseError?
|
|
var otherError: (any Error)?
|
|
}
|
|
|
|
private func parseOnGenerousStack(_ yaml: String) -> ParseOutcome {
|
|
let outcome = ParseOutcome()
|
|
let done = DispatchSemaphore(value: 0)
|
|
let thread = Thread {
|
|
do {
|
|
outcome.value = try PureYAML.parse(yaml)
|
|
} catch let error as PureYAML.Parsing.ParseError {
|
|
outcome.parseError = error
|
|
} catch {
|
|
outcome.otherError = error
|
|
}
|
|
done.signal()
|
|
}
|
|
thread.stackSize = 16 * 1024 * 1024
|
|
thread.start()
|
|
done.wait()
|
|
return outcome
|
|
}
|
|
|
|
@Test("Deeply nested flow collections throw nestingTooDeep instead of overflowing the stack")
|
|
func test_deepFlowThrowsCleanly() {
|
|
let depth = 400
|
|
let yaml = "x: " + String(repeating: "[", count: depth) + String(repeating: "]", count: depth)
|
|
let outcome = parseOnGenerousStack(yaml)
|
|
#expect(outcome.otherError == nil)
|
|
#expect(outcome.value == nil)
|
|
#expect(outcome.parseError?.diagnosticCode == "nestingTooDeep")
|
|
}
|
|
|
|
@Test("Deeply nested block mappings throw nestingTooDeep instead of overflowing the stack")
|
|
func test_deepBlockThrowsCleanly() {
|
|
var yaml = "deep:\n"
|
|
for level in 0 ..< 400 {
|
|
yaml += String(repeating: " ", count: level + 1) + "k:\n"
|
|
}
|
|
yaml += String(repeating: " ", count: 401) + "v: 1\n"
|
|
let outcome = parseOnGenerousStack(yaml)
|
|
#expect(outcome.otherError == nil)
|
|
#expect(outcome.parseError?.diagnosticCode == "nestingTooDeep")
|
|
}
|
|
|
|
@Test("Nesting within real-world depth still parses to the expected structure")
|
|
func test_moderateNestingParses() throws {
|
|
// Real specs in the corpus nest no deeper than the high 20s; this stays
|
|
// well inside the limit and must round-trip.
|
|
let depth = 24
|
|
let yaml = "x: " + String(repeating: "[", count: depth) + "1" + String(repeating: "]", count: depth)
|
|
let outcome = parseOnGenerousStack(yaml)
|
|
#expect(outcome.parseError == nil)
|
|
#expect(outcome.otherError == nil)
|
|
var current = try #require(outcome.value).mapping?["x"]
|
|
var measured = 0
|
|
while case let .sequence(items)? = current, let first = items.first {
|
|
measured += 1
|
|
current = first
|
|
}
|
|
#expect(measured == depth)
|
|
#expect(current == .int(1))
|
|
}
|
|
}
|