Skip to content

Commit f5bdef1

Browse files
fix conformance event canonicalization
Preserve expanded tags in the representation graph for event comparisons and normalize yaml-test-suite event parsing/output so scalar escapes, style markers, flow markers, and anchor reuse are compared semantically instead of presentation text. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent aa41eb2 commit f5bdef1

2 files changed

Lines changed: 151 additions & 30 deletions

File tree

src/functions/private/Resolve-YamlTag.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ function Resolve-YamlTag {
9797
)
9898

9999
[pscustomobject]@{
100-
Tag = if ($known) { $expanded } else { '' }
100+
Tag = $expanded
101101
IsUnknown = -not $known
102102
}
103103
}

tests/tools/Invoke-YamlTestSuite.ps1

Lines changed: 150 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -277,13 +277,61 @@ function ConvertFrom-YamlSuiteEventText {
277277
[string] $Text
278278
)
279279

280+
function ConvertTo-YamlSuiteEventEscapedText {
281+
param ([AllowNull()][string] $Value)
282+
if ($null -eq $Value) {
283+
return ''
284+
}
285+
$builder = [System.Text.StringBuilder]::new()
286+
foreach ($character in $Value.ToCharArray()) {
287+
switch ($character) {
288+
'\' { [void] $builder.Append('\\') }
289+
"`n" { [void] $builder.Append('\n') }
290+
"`r" { [void] $builder.Append('\r') }
291+
"`t" { [void] $builder.Append('\t') }
292+
default { [void] $builder.Append($character) }
293+
}
294+
}
295+
$builder.ToString()
296+
}
297+
298+
function ConvertFrom-YamlSuiteEventEscapes {
299+
param ([AllowNull()][string] $Value)
300+
if ($null -eq $Value) {
301+
return ''
302+
}
303+
$builder = [System.Text.StringBuilder]::new()
304+
$index = 0
305+
while ($index -lt $Value.Length) {
306+
$current = $Value[$index]
307+
if ($current -eq '\' -and $index + 1 -lt $Value.Length) {
308+
$index++
309+
switch ($Value[$index]) {
310+
'n' { [void] $builder.Append("`n") }
311+
'r' { [void] $builder.Append("`r") }
312+
't' { [void] $builder.Append("`t") }
313+
'b' { [void] $builder.Append("`b") }
314+
'\' { [void] $builder.Append('\') }
315+
default {
316+
[void] $builder.Append($Value[$index])
317+
}
318+
}
319+
$index++
320+
continue
321+
}
322+
[void] $builder.Append($current)
323+
$index++
324+
}
325+
$builder.ToString()
326+
}
327+
280328
$anchorMap = [System.Collections.Generic.Dictionary[string, string]]::new([System.StringComparer]::Ordinal)
281329
$anchorCounter = 0
282330
$canonical = [System.Collections.Generic.List[string]]::new()
283331
$lines = $Text -split '\r?\n'
284332

285333
foreach ($rawLine in $lines) {
286-
$line = $rawLine.Trim()
334+
$line = $rawLine
287335
if ([string]::IsNullOrWhiteSpace($line)) {
288336
continue
289337
}
@@ -305,8 +353,14 @@ function ConvertFrom-YamlSuiteEventText {
305353
$anchor = ''
306354
$tag = ''
307355
$value = ''
356+
$style = ''
308357

309358
while ($rest.Length -gt 0) {
359+
if ($rest.StartsWith('[]', [System.StringComparison]::Ordinal) -or
360+
$rest.StartsWith('{}', [System.StringComparison]::Ordinal)) {
361+
$rest = $rest.Substring(2).TrimStart()
362+
continue
363+
}
310364
if ($rest[0] -eq '&') {
311365
$space = $rest.IndexOf(' ')
312366
if ($space -lt 0) {
@@ -330,11 +384,20 @@ function ConvertFrom-YamlSuiteEventText {
330384
}
331385

332386
if ($prefix -eq '=VAL') {
333-
if ($rest.Length -gt 0 -and ($rest[0] -eq ':' -or $rest[0] -eq '"' -or $rest[0] -eq "'")) {
387+
if ($rest.Length -gt 0 -and
388+
($rest[0] -eq ':' -or $rest[0] -eq '"' -or $rest[0] -eq "'" -or
389+
$rest[0] -eq '|' -or $rest[0] -eq '>')) {
390+
$style = [string] $rest[0]
334391
$value = $rest.Substring(1)
335392
} else {
336393
$value = $rest
337394
}
395+
if ($style -in @('|', '>') -and [string]::IsNullOrEmpty($value)) {
396+
$value = ''
397+
}
398+
$value = ConvertTo-YamlSuiteEventEscapedText -Value (
399+
ConvertFrom-YamlSuiteEventEscapes -Value $value
400+
)
338401
}
339402

340403
$anchorToken = ''
@@ -347,7 +410,7 @@ function ConvertFrom-YamlSuiteEventText {
347410
}
348411
$parts = [System.Collections.Generic.List[string]]::new()
349412
$parts.Add($prefix)
350-
if ($tag) { $parts.Add("tag=$tag") }
413+
if ($tag -and $tag -ne '!') { $parts.Add("tag=$tag") }
351414
if ($anchorToken) { $parts.Add("anchor=$anchorToken") }
352415
if ($prefix -eq '=VAL') { $parts.Add("value=$value") }
353416
$canonical.Add(($parts -join '|'))
@@ -382,21 +445,31 @@ function ConvertTo-YamlSuiteActualEvent {
382445
[object[]] $Documents
383446
)
384447

385-
$anchorMap = [System.Collections.Generic.Dictionary[int, string]]::new()
448+
function ConvertTo-YamlSuiteEventEscapedText {
449+
param ([AllowNull()][string] $Value)
450+
if ($null -eq $Value) {
451+
return ''
452+
}
453+
$builder = [System.Text.StringBuilder]::new()
454+
foreach ($character in $Value.ToCharArray()) {
455+
switch ($character) {
456+
'\' { [void] $builder.Append('\\') }
457+
"`n" { [void] $builder.Append('\n') }
458+
"`r" { [void] $builder.Append('\r') }
459+
"`t" { [void] $builder.Append('\t') }
460+
default { [void] $builder.Append($character) }
461+
}
462+
}
463+
$builder.ToString()
464+
}
465+
466+
$anchorMap = [System.Collections.Generic.Dictionary[string, string]]::new(
467+
[System.StringComparer]::Ordinal
468+
)
386469
$anchorCounter = 0
387470
$events = [System.Collections.Generic.List[string]]::new()
388471
$events.Add('+STR')
389472

390-
$getAnchor = {
391-
param ([pscustomobject] $Node)
392-
if ($null -eq $Node) { return '' }
393-
if (-not $anchorMap.ContainsKey($Node.Id)) {
394-
$anchorCounter++
395-
$anchorMap[$Node.Id] = 'a{0:d3}' -f $anchorCounter
396-
}
397-
return $anchorMap[$Node.Id]
398-
}
399-
400473
foreach ($document in $Documents) {
401474
$events.Add('+DOC')
402475
$stack = [System.Collections.Generic.Stack[object]]::new()
@@ -411,25 +484,43 @@ function ConvertTo-YamlSuiteActualEvent {
411484

412485
$node = $frame.Node
413486
if ($node.Kind -eq 'Alias') {
414-
$targetAnchor = & $getAnchor $node.Target
487+
$targetAnchorKey = if ([string]::IsNullOrEmpty($node.Target.Anchor)) {
488+
'id:{0}' -f $node.Target.Id
489+
} else {
490+
$node.Target.Anchor
491+
}
492+
$targetAnchor = Get-YamlSuiteAnchorToken -Key $targetAnchorKey `
493+
-AnchorMap $anchorMap -AnchorCounter ([ref] $anchorCounter)
415494
$events.Add("=ALI|target=$targetAnchor")
416495
continue
417496
}
418497
if ($node.Kind -eq 'Scalar') {
419498
$parts = [System.Collections.Generic.List[string]]::new()
420499
$parts.Add('=VAL')
421-
if ($node.Tag) { $parts.Add("tag=$($node.Tag)") }
422-
if ($node.Anchor) { $parts.Add("anchor=$(& $getAnchor $node)") }
423-
$parts.Add(("value={0}" -f [string] $node.Value))
500+
if ($node.Tag -and $node.Tag -ne '!') { $parts.Add("tag=$($node.Tag)") }
501+
if ($node.Anchor) {
502+
$parts.Add("anchor=$(
503+
Get-YamlSuiteAnchorToken -Key $node.Anchor -AnchorMap $anchorMap `
504+
-AnchorCounter ([ref] $anchorCounter)
505+
)")
506+
}
507+
$parts.Add(("value={0}" -f (
508+
ConvertTo-YamlSuiteEventEscapedText -Value ([string] $node.Value)
509+
)))
424510
$events.Add(($parts -join '|'))
425511
continue
426512
}
427513

428514
$startParts = [System.Collections.Generic.List[string]]::new()
429515
$startToken = if ($node.Kind -eq 'Sequence') { '+SEQ' } else { '+MAP' }
430516
$startParts.Add($startToken)
431-
if ($node.Tag) { $startParts.Add("tag=$($node.Tag)") }
432-
if ($node.Anchor) { $startParts.Add("anchor=$(& $getAnchor $node)") }
517+
if ($node.Tag -and $node.Tag -ne '!') { $startParts.Add("tag=$($node.Tag)") }
518+
if ($node.Anchor) {
519+
$startParts.Add("anchor=$(
520+
Get-YamlSuiteAnchorToken -Key $node.Anchor -AnchorMap $anchorMap `
521+
-AnchorCounter ([ref] $anchorCounter)
522+
)")
523+
}
433524
$events.Add(($startParts -join '|'))
434525

435526
if ($node.Kind -eq 'Sequence') {
@@ -471,6 +562,27 @@ function Compare-YamlSuiteCanonicalList {
471562
return $true
472563
}
473564

565+
function Get-YamlSuiteAnchorToken {
566+
[CmdletBinding()]
567+
[OutputType([string])]
568+
param (
569+
[Parameter(Mandatory)]
570+
[string] $Key,
571+
572+
[Parameter(Mandatory)]
573+
[System.Collections.Generic.Dictionary[string, string]] $AnchorMap,
574+
575+
[Parameter(Mandatory)]
576+
[ref] $AnchorCounter
577+
)
578+
579+
if (-not $AnchorMap.ContainsKey($Key)) {
580+
$AnchorCounter.Value++
581+
$AnchorMap[$Key] = 'a{0:d3}' -f $AnchorCounter.Value
582+
}
583+
$AnchorMap[$Key]
584+
}
585+
474586
$readYamlSuiteRepresentation = {
475587
param ([string] $YamlText)
476588
Read-YamlStreamCore -Yaml $YamlText -Depth 128 -MaxNodes 100000 `
@@ -492,6 +604,19 @@ $projectYamlSuiteStream = {
492604
}
493605
New-YamlValueBox -Value ([object[]] $values.ToArray())
494606
}
607+
$projectYamlSuiteText = {
608+
param ([string] $YamlText)
609+
610+
$stream = Read-YamlStream -Yaml $YamlText -Depth 128 -MaxNodes 100000 `
611+
-MaxAliases 1000 -MaxScalarLength 1048576 -MaxTagLength 1024 `
612+
-MaxTotalTagLength 65536 -MaxNumericLength 4096
613+
$values = [System.Collections.Generic.List[object]]::new()
614+
foreach ($node in $stream.Value) {
615+
$cache = [System.Collections.Generic.Dictionary[int, object]]::new()
616+
$values.Add((ConvertFrom-YamlNode -Node $node -Cache $cache -AsHashtable).Value)
617+
}
618+
New-YamlValueBox -Value ([object[]] $values.ToArray())
619+
}
495620

496621
$suiteRoot = (Resolve-Path -LiteralPath $Path).Path
497622
$inputFiles = @(
@@ -711,7 +836,7 @@ foreach ($inputFile in $inputFiles) {
711836
$emitted = Invoke-InYamlModule -ScriptBlock {
712837
param ($InputValue)
713838
ConvertTo-Yaml -InputObject $InputValue -ExplicitDocumentStart
714-
} -Arguments @($value)
839+
} -Arguments (, $value)
715840
$emittedDocuments.Add([string] $emitted)
716841
}
717842
$emittedText = ($emittedDocuments.ToArray() -join "`n")
@@ -725,14 +850,8 @@ foreach ($inputFile in $inputFiles) {
725850
$emitResult = 'Fail'
726851
$emitReason = 'EmittedYamlInvalid'
727852
} else {
728-
$roundTripValues = @(
729-
Invoke-InYamlModule -ScriptBlock {
730-
param ($YamlText)
731-
ConvertFrom-Yaml -Yaml $YamlText -AsHashtable -NoEnumerate -Depth 128 `
732-
-MaxNodes 100000 -MaxAliases 1000 -MaxScalarLength 1048576 `
733-
-MaxTagLength 1024 -MaxTotalTagLength 65536 -MaxNumericLength 4096
734-
} -Arguments @($emittedText)
735-
)
853+
$roundTripValues = (Invoke-InYamlModule -ScriptBlock $projectYamlSuiteText `
854+
-Arguments @($emittedText)).Value
736855
$roundCanonical = ConvertTo-YamlSuiteCanonicalValue -Value ([object[]] $roundTripValues)
737856
$roundReference = ConvertTo-YamlSuiteReferenceSignature -Value ([object[]] $roundTripValues)
738857
$emitCanonical = $roundCanonical
@@ -789,5 +908,7 @@ foreach ($inputFile in $inputFiles) {
789908
OutYamlActual = $outYamlCanonical
790909
EmitActual = $emitCanonical
791910
EmitReferences = $emitReference
911+
ProjectedActual = $projectedCanonical
912+
ProjectedRefs = $projectedReference
792913
}
793914
}

0 commit comments

Comments
 (0)