Skip to content

Commit 7dc2079

Browse files
Verify formatter against the YAML corpus
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 4b542ba commit 7dc2079

3 files changed

Lines changed: 146 additions & 16 deletions

File tree

src/functions/private/Get-YamlEmissionImplicitKeyLength.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ function Get-YamlEmissionImplicitKeyLength {
77
[OutputType([int])]
88
param (
99
[Parameter(Mandatory)]
10+
[AllowEmptyString()]
1011
[string] $RenderedText
1112
)
1213

tests/Conformance.Tests.ps1

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ BeforeAll {
3434
-CompareEvents `
3535
-CompareOutYaml `
3636
-CompareEmitYaml `
37-
-CompareSelfRoundTrip
37+
-CompareSelfRoundTrip `
38+
-CompareFormatter
3839
)
3940
$emptySuitePath = Join-Path $TestDrive 'empty-suite'
4041
$null = New-Item -Path $emptySuitePath -ItemType Directory
@@ -47,6 +48,7 @@ Describe 'Released yaml-test-suite corpus accounting' {
4748
$conformanceYamlModule | Should -Not -BeNullOrEmpty
4849
$command = Get-Command -Name ConvertFrom-Yaml
4950
$command.Module | Should -Be $conformanceYamlModule
51+
(Get-Command -Name Format-Yaml).Module | Should -Be $conformanceYamlModule
5052
$conformanceYamlModule.ModuleBase |
5153
Should -Not -Be (Join-Path $repositoryRoot 'src')
5254
$conformanceYamlModule.PowerShellVersion | Should -Be '7.6'
@@ -405,6 +407,46 @@ ship-to:
405407
)
406408
}
407409

410+
It 'formats every loadable case with representation and idempotence preserved' {
411+
@($suiteResults | Where-Object FormatterResult -EQ 'Pass').Count |
412+
Should -Be 400
413+
@($suiteResults | Where-Object FormatterResult -EQ 'PolicyDifference').Count |
414+
Should -Be 0
415+
@($suiteResults | Where-Object FormatterResult -EQ 'Fail').Count |
416+
Should -Be 0
417+
@($suiteResults | Where-Object FormatterResult -EQ 'NotApplicable').Count |
418+
Should -Be 2
419+
420+
$excluded = @(
421+
$suiteResults |
422+
Where-Object FormatterResult -EQ 'NotApplicable' |
423+
Sort-Object Case
424+
)
425+
@($excluded.Case) | Should -Be @('2JQS', 'X38W')
426+
@($excluded.FormatterReason | Select-Object -Unique) |
427+
Should -Be @('RepresentationMappingKeyUniqueness')
428+
429+
$formatted = @(
430+
$suiteResults |
431+
Where-Object { -not $_.ExpectsError -and $_.FormatterResult -eq 'Pass' }
432+
)
433+
$formatted.Count | Should -Be 306
434+
@($formatted | Where-Object FormatterReason).Count | Should -Be 0
435+
@($formatted | Where-Object { $_.FormatterText -cne $_.FormatterSecond }).Count |
436+
Should -Be 0
437+
@($formatted | Where-Object { $_.FormatterExpected -cne $_.FormatterActual }).Count |
438+
Should -Be 0
439+
}
440+
441+
It 'keeps every invalid corpus input rejected by the formatter' {
442+
$invalid = @($suiteResults | Where-Object ExpectsError)
443+
444+
$invalid.Count | Should -Be 94
445+
@($invalid | Where-Object FormatterResult -NE 'Pass').Count | Should -Be 0
446+
@($invalid.FormatterReason | Select-Object -Unique) |
447+
Should -Be @('InvalidInputRejected')
448+
}
449+
408450
It 'keeps the previously failing multi-document JSON cases green' {
409451
$jsonRegressions = @(
410452
$suiteResults |

tests/tools/Invoke-YamlTestSuite.ps1

Lines changed: 102 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ param (
1212
[switch] $CompareEvents,
1313
[switch] $CompareOutYaml,
1414
[switch] $CompareEmitYaml,
15-
[switch] $CompareSelfRoundTrip
15+
[switch] $CompareSelfRoundTrip,
16+
[switch] $CompareFormatter
1617
)
1718

1819
. (Join-Path $PSScriptRoot '..\TestBootstrap.ps1')
@@ -21,12 +22,14 @@ if (-not $PSBoundParameters.ContainsKey('CompareJson') -and
2122
-not $PSBoundParameters.ContainsKey('CompareEvents') -and
2223
-not $PSBoundParameters.ContainsKey('CompareOutYaml') -and
2324
-not $PSBoundParameters.ContainsKey('CompareEmitYaml') -and
24-
-not $PSBoundParameters.ContainsKey('CompareSelfRoundTrip')) {
25+
-not $PSBoundParameters.ContainsKey('CompareSelfRoundTrip') -and
26+
-not $PSBoundParameters.ContainsKey('CompareFormatter')) {
2527
$CompareJson = $true
2628
$CompareEvents = $true
2729
$CompareOutYaml = $true
2830
$CompareEmitYaml = $true
2931
$CompareSelfRoundTrip = $true
32+
$CompareFormatter = $true
3033
}
3134

3235
function Invoke-InYamlModule {
@@ -591,13 +594,10 @@ function ConvertFrom-YamlSuiteEventText {
591594
ConvertFrom-YamlSuiteEventEscape -Value $value
592595
)
593596
}
594-
595597
$anchorToken = ''
596598
if ($anchor) {
597-
if (-not $anchorMap.ContainsKey($anchor)) {
598-
$anchorCounter++
599-
$anchorMap[$anchor] = 'a{0:d3}' -f $anchorCounter
600-
}
599+
$anchorCounter++
600+
$anchorMap[$anchor] = 'a{0:d3}' -f $anchorCounter
601601
$anchorToken = $anchorMap[$anchor]
602602
}
603603
$parts = [System.Collections.Generic.List[string]]::new()
@@ -675,11 +675,7 @@ function ConvertTo-YamlSuiteActualEvent {
675675

676676
$node = $frame.Node
677677
if ($node.Kind -eq 'Alias') {
678-
$targetAnchorKey = if ([string]::IsNullOrEmpty($node.Target.Anchor)) {
679-
'id:{0}' -f $node.Target.Id
680-
} else {
681-
$node.Target.Anchor
682-
}
678+
$targetAnchorKey = 'id:{0}' -f $node.Target.Id
683679
$targetAnchor = Get-YamlSuiteAnchorToken -Key $targetAnchorKey `
684680
-AnchorMap $anchorMap -AnchorCounter ([ref] $anchorCounter)
685681
$events.Add("=ALI|target=$targetAnchor")
@@ -691,7 +687,7 @@ function ConvertTo-YamlSuiteActualEvent {
691687
if ($node.Tag -and $node.Tag -ne '!') { $parts.Add("tag=$($node.Tag)") }
692688
if ($node.Anchor) {
693689
$parts.Add("anchor=$(
694-
Get-YamlSuiteAnchorToken -Key $node.Anchor -AnchorMap $anchorMap `
690+
Get-YamlSuiteAnchorToken -Key ('id:{0}' -f $node.Id) -AnchorMap $anchorMap `
695691
-AnchorCounter ([ref] $anchorCounter)
696692
)")
697693
}
@@ -708,7 +704,7 @@ function ConvertTo-YamlSuiteActualEvent {
708704
if ($node.Tag -and $node.Tag -ne '!') { $startParts.Add("tag=$($node.Tag)") }
709705
if ($node.Anchor) {
710706
$startParts.Add("anchor=$(
711-
Get-YamlSuiteAnchorToken -Key $node.Anchor -AnchorMap $anchorMap `
707+
Get-YamlSuiteAnchorToken -Key ('id:{0}' -f $node.Id) -AnchorMap $anchorMap `
712708
-AnchorCounter ([ref] $anchorCounter)
713709
)")
714710
}
@@ -831,6 +827,12 @@ $testYamlSuiteText = {
831827
-MaxScalarLength 1048576 -MaxTagLength 1024 -MaxTotalTagLength 65536 `
832828
-MaxNumericLength 4096
833829
}
830+
$formatYamlSuiteText = {
831+
param ([string] $YamlText)
832+
Format-Yaml -InputObject $YamlText -Depth 128 -MaxNodes 100000 -MaxAliases 1000 `
833+
-MaxScalarLength 1048576 -MaxTagLength 1024 -MaxTotalTagLength 65536 `
834+
-MaxNumericLength 4096
835+
}
834836

835837
$suiteRoot = (Resolve-Path -LiteralPath $Path).Path
836838
$inputFiles = @(
@@ -872,6 +874,8 @@ foreach ($inputFile in $inputFiles) {
872874
$emitYamlReason = ''
873875
$selfRoundTripResult = 'NotApplicable'
874876
$selfRoundTripReason = ''
877+
$formatterResult = 'NotApplicable'
878+
$formatterReason = ''
875879

876880
$representation = $null
877881
$stream = $null
@@ -881,6 +885,7 @@ foreach ($inputFile in $inputFiles) {
881885
$projectedJsonCanonical = $null
882886
$projectedReference = ''
883887
$projectionError = ''
888+
$streamError = ''
884889
$jsonOracleValues = $null
885890
$jsonOracleCanonical = $null
886891
$eventExpected = $null
@@ -895,6 +900,10 @@ foreach ($inputFile in $inputFiles) {
895900
$emitYamlActualReference = $null
896901
$selfRoundTripCanonical = $null
897902
$selfRoundTripReference = $null
903+
$formatterExpected = $null
904+
$formatterActual = $null
905+
$formatterText = $null
906+
$formatterSecond = $null
898907

899908
try {
900909
$representation = Invoke-InYamlModule -ScriptBlock $readYamlSuiteRepresentation -Arguments @($yaml)
@@ -923,14 +932,15 @@ foreach ($inputFile in $inputFiles) {
923932
}
924933
if ($expectsError) {
925934
$syntaxResult = 'Pass'
926-
} elseif ($casePath -cin @('2JQS', 'X38W') -and
935+
} elseif ($null -ne $representation -and
927936
$_.Exception.Data['YamlErrorId'] -eq 'YamlDuplicateKey') {
928937
$syntaxResult = 'PolicyDifference'
929938
$syntaxReason = 'RepresentationMappingKeyUniqueness'
930939
} else {
931940
$syntaxResult = 'Fail'
932941
$syntaxReason = [string] $_.Exception.Data['YamlErrorId']
933942
}
943+
$streamError = [string] $_.Exception.Data['YamlErrorId']
934944
}
935945
}
936946

@@ -1201,6 +1211,7 @@ foreach ($inputFile in $inputFiles) {
12011211
$selfRoundTripReason = 'SelfRoundTripMismatch'
12021212
}
12031213
}
1214+
12041215
}
12051216
} catch [System.NotSupportedException] {
12061217
$selfRoundTripResult = 'Fail'
@@ -1216,6 +1227,76 @@ foreach ($inputFile in $inputFiles) {
12161227
}
12171228
}
12181229

1230+
if ($CompareFormatter) {
1231+
if ($expectsError) {
1232+
try {
1233+
$formatterText = Invoke-InYamlModule -ScriptBlock $formatYamlSuiteText `
1234+
-Arguments @($yaml)
1235+
$formatterResult = 'Fail'
1236+
$formatterReason = 'InvalidInputAccepted'
1237+
} catch {
1238+
if (-not $_.Exception.Data.Contains('IsYamlException')) {
1239+
throw
1240+
}
1241+
$formatterResult = 'Pass'
1242+
$formatterReason = 'InvalidInputRejected'
1243+
}
1244+
} elseif ($null -eq $stream) {
1245+
if ($null -ne $representation -and $streamError -ceq 'YamlDuplicateKey') {
1246+
$formatterResult = 'NotApplicable'
1247+
$formatterReason = 'RepresentationMappingKeyUniqueness'
1248+
} else {
1249+
$formatterResult = 'Fail'
1250+
$formatterReason = if ($streamError) { $streamError } else { 'RepresentationUnavailable' }
1251+
}
1252+
} else {
1253+
try {
1254+
$formatterText = [string] (
1255+
Invoke-InYamlModule -ScriptBlock $formatYamlSuiteText -Arguments @($yaml)
1256+
)
1257+
$isValidFormat = Invoke-InYamlModule -ScriptBlock $testYamlSuiteText `
1258+
-Arguments @($formatterText)
1259+
if (-not $isValidFormat) {
1260+
$formatterResult = 'Fail'
1261+
$formatterReason = 'FormattedYamlInvalid'
1262+
} else {
1263+
$formattedRepresentation = Invoke-InYamlModule `
1264+
-ScriptBlock $readYamlSuiteRepresentation -Arguments @($formatterText)
1265+
$originalEvents = ConvertTo-YamlSuiteActualEvent -Documents $representation.Value
1266+
$formattedEvents = ConvertTo-YamlSuiteActualEvent `
1267+
-Documents $formattedRepresentation.Value
1268+
$formatterExpected = $originalEvents -join "`n"
1269+
$formatterActual = $formattedEvents -join "`n"
1270+
if (-not (
1271+
Compare-YamlSuiteCanonicalList `
1272+
-Left $formattedEvents -Right $originalEvents
1273+
)) {
1274+
$formatterResult = 'Fail'
1275+
$formatterReason = 'RepresentationMismatch'
1276+
} else {
1277+
$formatterSecond = [string] (
1278+
Invoke-InYamlModule -ScriptBlock $formatYamlSuiteText `
1279+
-Arguments @($formatterText)
1280+
)
1281+
if ($formatterSecond -cne $formatterText) {
1282+
$formatterResult = 'Fail'
1283+
$formatterReason = 'FormatterNotIdempotent'
1284+
} else {
1285+
$formatterResult = 'Pass'
1286+
}
1287+
}
1288+
}
1289+
} catch {
1290+
if ($_.Exception.Data.Contains('IsYamlException')) {
1291+
$formatterResult = 'Fail'
1292+
$formatterReason = [string] $_.Exception.Data['YamlErrorId']
1293+
} else {
1294+
throw
1295+
}
1296+
}
1297+
}
1298+
}
1299+
12191300
[pscustomobject]@{
12201301
Case = $casePath
12211302
ExpectsError = $expectsError
@@ -1235,6 +1316,8 @@ foreach ($inputFile in $inputFiles) {
12351316
EmitYamlReason = $emitYamlReason
12361317
SelfRoundTripResult = $selfRoundTripResult
12371318
SelfRoundTripReason = $selfRoundTripReason
1319+
FormatterResult = $formatterResult
1320+
FormatterReason = $formatterReason
12381321
EventExpected = $eventExpected
12391322
EventActual = $eventActual
12401323
JsonExpected = $jsonExpected
@@ -1247,6 +1330,10 @@ foreach ($inputFile in $inputFiles) {
12471330
EmitYamlActualRefs = $emitYamlActualReference
12481331
SelfRoundTripActual = $selfRoundTripCanonical
12491332
SelfRoundTripRefs = $selfRoundTripReference
1333+
FormatterExpected = $formatterExpected
1334+
FormatterActual = $formatterActual
1335+
FormatterText = $formatterText
1336+
FormatterSecond = $formatterSecond
12501337
ProjectedActual = $projectedCanonical
12511338
ProjectedRefs = $projectedReference
12521339
}

0 commit comments

Comments
 (0)