Private/Get-QAOpsTrxResult.ps1

function Get-QAOpsTrxResult {
    <# Parses VSTest/MTP TRX UnitTestResult rows with encoding-aware, DTD-disabled XML loading and bounded result/message handling. #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)][string]$ResultsPath,
        [Parameter(Mandatory=$true)][string]$AssemblyName,
        [Parameter()][int]$MaxTrxBytes = 10485760,
        [Parameter()][int]$MaxResults = 50000,
        [Parameter()][int]$MaxMessageCharacters = 2000
    )
    $item = Get-Item -LiteralPath $ResultsPath -ErrorAction Stop
    if ($item.Length -gt $MaxTrxBytes) { throw "TRX file exceeds the supported size limit of $MaxTrxBytes bytes: $ResultsPath" }
    $settings = New-Object System.Xml.XmlReaderSettings
    $settings.DtdProcessing = [System.Xml.DtdProcessing]::Prohibit
    $settings.XmlResolver = $null
    $settings.MaxCharactersInDocument = [Math]::Max($MaxTrxBytes * 4, 1024)
    $stream = [System.IO.File]::Open($ResultsPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read)
    try {
        $reader = [System.Xml.XmlReader]::Create($stream, $settings)
        try { $trx = New-Object System.Xml.XmlDocument; $trx.XmlResolver = $null; $trx.Load($reader) }
        finally { $reader.Close() }
    }
    finally { $stream.Dispose() }

    $ns = New-Object System.Xml.XmlNamespaceManager($trx.NameTable)
    $ns.AddNamespace('t','http://microsoft.com/schemas/VisualStudio/TeamTest/2010')
    $unitTests = New-Object 'System.Collections.Generic.Dictionary[string,object]' ([System.StringComparer]::Ordinal)
    foreach ($ut in @($trx.SelectNodes('//t:UnitTest', $ns))) {
        $id = $ut.GetAttribute('id')
        $tm = $ut.SelectSingleNode('t:TestMethod', $ns)
        $class = if ($tm) { $tm.GetAttribute('className') } else { '' }
        $name = if ($tm) { $tm.GetAttribute('name') } else { $ut.GetAttribute('name') }
        $methodName = $name
        $methodDataCase = ''
        if ($name -match '^(.+?)(\(.+\))$') { $methodName = $Matches[1]; $methodDataCase = $Matches[2] }
        $fqn = if ([string]::IsNullOrWhiteSpace($class)) { $methodName } elseif ($methodName.StartsWith($class + '.', [System.StringComparison]::Ordinal)) { $methodName } else { $class + '.' + $methodName }
        $codeBase = if ($tm) { $tm.GetAttribute('codeBase') } else { '' }
        $unitAssembly = $AssemblyName
        if (-not [string]::IsNullOrWhiteSpace($codeBase)) {
            $leaf = [System.IO.Path]::GetFileName(($codeBase -replace '\\', '/'))
            if (-not [string]::IsNullOrWhiteSpace($leaf) -and $leaf.EndsWith('.dll', [System.StringComparison]::OrdinalIgnoreCase)) { $unitAssembly = $leaf }
        }
        if (-not [string]::IsNullOrWhiteSpace($id)) { $unitTests[$id] = [pscustomobject]@{ FullyQualifiedName=$fqn; MethodName=$methodName; MethodDataCase=$methodDataCase; AssemblyName=$unitAssembly } }
    }
    $nodes = @($trx.SelectNodes('//t:UnitTestResult', $ns))
    if ($nodes.Count -gt $MaxResults) { throw "TRX result count $($nodes.Count) exceeds the supported limit of $MaxResults." }
    $results = New-Object System.Collections.ArrayList
    foreach ($r in $nodes) {
        $testId = $r.GetAttribute('testId')
        $display = $r.GetAttribute('testName')
        $unit = if ($unitTests.ContainsKey($testId)) { $unitTests[$testId] } else { $null }
        $fqn = if ($unit) { $unit.FullyQualifiedName } else { $testId }
        $methodName = if ($unit) { $unit.MethodName } else { $fqn }
        $rowAssembly = if ($unit) { $unit.AssemblyName } else { $AssemblyName }
        $dataCase = if ($unit) { [string]$unit.MethodDataCase } else { '' }
        $diagnostics = @()
        $comparable = $true
        foreach ($attrName in @('dataCaseId','dataRowInfo','testCaseId')) {
            $attrValue = $r.GetAttribute($attrName)
            if (-not [string]::IsNullOrWhiteSpace($attrValue)) { $dataCase = $attrValue; break }
        }
        $hadStableDataCase = -not [string]::IsNullOrWhiteSpace($dataCase)
        if ([string]::IsNullOrWhiteSpace($dataCase) -and -not [string]::IsNullOrWhiteSpace($display)) {
            if ($display.StartsWith($fqn, [System.StringComparison]::Ordinal) -and $display.Length -gt $fqn.Length) {
                $suffix = $display.Substring($fqn.Length).Trim()
                if ($suffix.StartsWith('(') -and $suffix.EndsWith(')')) { $dataCase = $suffix }
            }
            elseif (-not [string]::IsNullOrWhiteSpace($methodName) -and $display.StartsWith($methodName, [System.StringComparison]::Ordinal) -and $display.Length -gt $methodName.Length) {
                $suffix = $display.Substring($methodName.Length).Trim()
                if ($suffix.StartsWith('(') -and $suffix.EndsWith(')')) { $dataCase = $suffix }
            }
        }
        if ([string]::IsNullOrWhiteSpace($dataCase) -and -not [string]::IsNullOrWhiteSpace($display) -and -not [string]::IsNullOrWhiteSpace($methodName)) {
            if (-not $display.Equals($methodName, [System.StringComparison]::Ordinal) -and -not $display.Equals($fqn, [System.StringComparison]::Ordinal) -and -not $display.StartsWith($fqn + ' ', [System.StringComparison]::Ordinal)) {
                $diagnostics = @([pscustomobject]@{ code='data-case-identity-unavailable' })
                $comparable = $false
            }
        }
        $duration = [TimeSpan]::Zero
        $rawDuration = $r.GetAttribute('duration')
        if (-not [string]::IsNullOrWhiteSpace($rawDuration)) { [TimeSpan]::TryParse($rawDuration, [ref]$duration) | Out-Null }
        $msg = ''
        $messageNode = $r.SelectSingleNode('t:Output/t:ErrorInfo/t:Message', $ns)
        $stackNode = $r.SelectSingleNode('t:Output/t:ErrorInfo/t:StackTrace', $ns)
        if ($messageNode -and -not [string]::IsNullOrWhiteSpace($messageNode.InnerText)) { $msg = Limit-String -stringToLimit $messageNode.InnerText.Trim() -maxCharacters $MaxMessageCharacters }
        if ($stackNode -and -not [string]::IsNullOrWhiteSpace($stackNode.InnerText)) { $msg = Limit-String -stringToLimit (($msg + "`n" + $stackNode.InnerText.Trim()).Trim()) -maxCharacters $MaxMessageCharacters }
        [void]$results.Add([pscustomobject]@{ Assembly=$rowAssembly; FullyQualifiedName=$fqn; DisplayName=$display; DataCaseId=$dataCase; Outcome=$r.GetAttribute('outcome'); Duration=$duration; Message=$msg; Diagnostics=$diagnostics; Comparable=$comparable })
    }
    return $results.ToArray()
}