# Pure file/log readers. No executable is started by these helpers. function Get-GateBinary([string]$Path,[string]$Target) { $resolved=[IO.Path]::GetFullPath($Path) $root=[IO.Path]::GetFullPath($Target).TrimEnd('\','/')+[IO.Path]::DirectorySeparatorChar if(-not $resolved.StartsWith($root,[StringComparison]::OrdinalIgnoreCase)){throw 'Executable path escapes the parent-owned target.'} $item=Get-Item -LiteralPath $resolved [ordered]@{path=$resolved;bytes=$item.Length;sha256=(Get-FileHash -LiteralPath $resolved -Algorithm SHA256).Hash.ToLowerInvariant();mtimeUtc=$item.LastWriteTimeUtc.ToString('o')} } function Get-GateTestBinaries([string]$Log,[string]$Target) { $paths=@([regex]::Matches($Log,'(?m)^\s*Running unittests .+? \(([^\r\n]+\.exe)\)\s*$')|ForEach-Object {$_.Groups[1].Value}|Sort-Object -Unique) foreach($path in $paths){Get-GateBinary $path $Target} } function Same-GateBinaries($Left,$Right) { $a=@($Left|Sort-Object path);$b=@($Right|Sort-Object path) if($a.Count -eq 0 -or $a.Count -ne $b.Count){return $false} for($i=0;$i -lt $a.Count;$i++){ if($a[$i].path -ine $b[$i].path -or $a[$i].bytes -ne $b[$i].bytes -or $a[$i].sha256 -cne $b[$i].sha256){return $false} } return $true }