Modules/businessdev.ALbuild.RuntimePackages/Private/ConvertTo-BcRuntimeIssueJUnit.ps1
|
function ConvertTo-BcRuntimeIssueJUnit { <# .SYNOPSIS JUnit results with one test case per (app, CAUSE) instead of per (app, platform version). .DESCRIPTION The per-version results answer "is the catalogue complete?". This answers the other question - "what is blocking a product, and who has to fix it?" - and it is a different shape. Run 27383 failed 274 times. Read per version that is 274 findings; read per cause it is four: ERiC cannot compile below AL runtime 14, Banking subscribes to events its older Base Applications do not have, E-Invoice references namespaces they do not have, and one container refused a command. Four is a report a product team can act on. 274 is a wall they have to read first. The minimum versions in the catalogue are NOT raised to make these go away: what a product supports is the product team's decision, not the build's. So the build's job ends at stating the blockage precisely - which platform versions, which compiler errors, first diagnostics - and handing it over. Published as its own test run, so the Tests tab shows one red entry per real problem, with history across runs: a cause that disappears was fixed, a cause that appears is new. .PARAMETER Result The per-platform-version results from Invoke-BcRuntimeFactory. .PARAMETER MaxDiagnostic Diagnostics to quote per cause. .OUTPUTS System.String - the JUnit XML. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)] [AllowEmptyCollection()] [object[]] $Result, [ValidateRange(1, 50)] [int] $MaxDiagnostic = 6 ) $rows = [System.Collections.Generic.List[object]]::new() foreach ($v in @($Result)) { $pv = "$($v.PlatformVersion)" $sortable = try { [version](ConvertTo-BcVersion $pv) } catch { [version]'0.0.0.0' } $country = "$(Get-BcRuntimeProperty -InputObject $v -Name 'Country' -Default '')" foreach ($p in @(Get-BcRuntimeProperty -InputObject $v -Name 'Products' -Default @())) { if ("$($p.Status)" -ne 'Failed') { continue } $diag = @(Get-BcRuntimeProperty -InputObject $p -Name 'Diagnostics' -Default @()) $class = Get-BcRuntimeIssueClass -Message "$(Get-BcRuntimeProperty -InputObject $p -Name 'Error' -Default '')" -Diagnostic $diag $rows.Add([PSCustomObject]@{ App = "$($p.Name)" Version = $pv Sort = $sortable Country = $country Key = $class.Key Label = $class.Label Diag = $diag }) } } $esc = { param([string] $Text) [System.Security.SecurityElement]::Escape("$Text") } $sb = [System.Text.StringBuilder]::new() [void]$sb.AppendLine('<?xml version="1.0" encoding="utf-8"?>') [void]$sb.AppendLine('<testsuites>') foreach ($app in (@($rows | Select-Object -ExpandProperty App -Unique) | Sort-Object)) { $mine = @($rows | Where-Object { $_.App -eq $app }) $causes = @($mine | Group-Object Key) [void]$sb.AppendLine((' <testsuite name="{0}" tests="{1}" failures="{1}" skipped="0" time="0">' -f (& $esc $app), $causes.Count)) foreach ($g in ($causes | Sort-Object { $_.Count } -Descending)) { $versions = @($g.Group | Sort-Object Sort -Descending) $label = "$($g.Group[0].Label)" $newest = $versions[0] $oldest = $versions[-1] $body = [System.Collections.Generic.List[string]]::new() $body.Add("$app cannot be built on $($versions.Count) platform version(s): $label.") $body.Add('') $body.Add("Newest affected: BC $($newest.Version)$(if ($newest.Country) { " ($($newest.Country))" })") $body.Add("Oldest affected: BC $($oldest.Version)$(if ($oldest.Country) { " ($($oldest.Country))" })") $body.Add('') # EVERY affected version, not a sample: the team reading this decides whether to guard the # source or move the minimum, and both need the actual list. A capped list turned the # 27409 report into a lower bound nobody could act on. Wrapped so it stays readable. $body.Add('Affected:') $all = @($versions | ForEach-Object { "BC $($_.Version)" }) for ($i = 0; $i -lt $all.Count; $i += 6) { $chunk = @($all[$i..([Math]::Min($i + 5, $all.Count - 1))]) $body.Add(" $($chunk -join ', ')") } if (@($g.Group[0].Diag).Count -gt 0) { $body.Add('') $body.Add('First diagnostics:') foreach ($d in @($g.Group[0].Diag | Select-Object -First $MaxDiagnostic)) { $body.Add(" $d") } } $body.Add('') $body.Add('The catalogue minimum for this product is deliberately NOT raised to hide this - what a') $body.Add('product supports is the product team''s call. Either the source gains a guard for these') $body.Add('versions, or the minimum moves. The full compiler output is in the worker-logs artifact.') # classname carries the app so the Tests tab groups by product; name carries the cause - # and NOTHING that changes between runs. The count used to sit here, so a blocker that # spread from 12 versions to 13 became a different test and lost its history in the Tests # tab. The count belongs in the message, which is free to change. [void]$sb.AppendLine((' <testcase classname="{0}" name="{1}" time="0">' -f (& $esc $app), (& $esc "$app - $label"))) [void]$sb.AppendLine((' <failure message="{0}">{1}</failure>' -f (& $esc $body[0]), (& $esc ($body -join [Environment]::NewLine)))) [void]$sb.AppendLine(' </testcase>') } [void]$sb.AppendLine(' </testsuite>') } [void]$sb.AppendLine('</testsuites>') return $sb.ToString() } |