645 lines
21 KiB
PowerShell
645 lines
21 KiB
PowerShell
param(
|
|
[string]$PackageRoot = 'C:\HyperTwist\packaged\classic-cube',
|
|
[string]$MapUrl = '/Game/HyperTwistTraining/Maps/L_HyperTwist_ClassicTraining',
|
|
[int]$SmokeSeconds = 30,
|
|
[int]$ResX = 1600,
|
|
[int]$ResY = 900,
|
|
[string]$ReportPath = '',
|
|
[switch]$KeepRunning,
|
|
[switch]$UseNullRHI,
|
|
[switch]$NoSound
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Write-Utf8JsonFile {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Path,
|
|
[Parameter(Mandatory = $true)]
|
|
[object]$Value
|
|
)
|
|
|
|
$ParentPath = Split-Path -Parent $Path
|
|
if (-not [string]::IsNullOrWhiteSpace($ParentPath))
|
|
{
|
|
New-Item -ItemType Directory -Force -Path $ParentPath | Out-Null
|
|
}
|
|
|
|
$Json = $Value | ConvertTo-Json -Depth 8
|
|
$Utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
|
[System.IO.File]::WriteAllText($Path, $Json, $Utf8NoBom)
|
|
}
|
|
|
|
function Get-FatalRuntimeLogMatches {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$RuntimeLogPath
|
|
)
|
|
|
|
if (-not (Test-Path -LiteralPath $RuntimeLogPath))
|
|
{
|
|
return @()
|
|
}
|
|
|
|
$FatalPatterns = @(
|
|
'Fatal error:',
|
|
'LowLevelFatalError',
|
|
'Assertion failed:',
|
|
'Critical error:',
|
|
'appError called:',
|
|
'DXGI_ERROR_NOT_CURRENTLY_AVAILABLE',
|
|
'CreateSwapChainResult failed',
|
|
'Unhandled Exception:',
|
|
'StaticShutdownAfterError'
|
|
)
|
|
|
|
return @(Get-Content -LiteralPath $RuntimeLogPath | Where-Object {
|
|
$Line = $_
|
|
foreach ($Pattern in $FatalPatterns)
|
|
{
|
|
if ($Line -like "*$Pattern*")
|
|
{
|
|
return $true
|
|
}
|
|
}
|
|
|
|
return $false
|
|
})
|
|
}
|
|
|
|
function Get-OwnedPackageProcessIds {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ResolvedPackageRoot,
|
|
[int[]]$ExcludedProcessIds = @()
|
|
)
|
|
|
|
return @(
|
|
Get-OwnedPackageProcessRecords `
|
|
-ResolvedPackageRoot $ResolvedPackageRoot `
|
|
-ExcludedProcessIds $ExcludedProcessIds |
|
|
Select-Object -ExpandProperty ProcessId
|
|
)
|
|
}
|
|
|
|
function Get-OwnedPackageProcessRecords {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ResolvedPackageRoot,
|
|
[int[]]$ExcludedProcessIds = @()
|
|
)
|
|
|
|
$RootPrefix = $ResolvedPackageRoot.TrimEnd(
|
|
[System.IO.Path]::DirectorySeparatorChar,
|
|
[System.IO.Path]::AltDirectorySeparatorChar
|
|
) + [System.IO.Path]::DirectorySeparatorChar
|
|
|
|
return @(
|
|
Get-CimInstance Win32_Process -Filter "Name = 'UnrealHyperTwist.exe'" -ErrorAction SilentlyContinue |
|
|
Where-Object {
|
|
-not [string]::IsNullOrWhiteSpace([string]$_.ExecutablePath) `
|
|
-and ([string]$_.ExecutablePath).StartsWith(
|
|
$RootPrefix,
|
|
[System.StringComparison]::OrdinalIgnoreCase
|
|
) `
|
|
-and $ExcludedProcessIds -notcontains [int]$_.ProcessId
|
|
}
|
|
)
|
|
}
|
|
|
|
function Get-OwnedListeningTcpEndpoints {
|
|
param(
|
|
[int[]]$ProcessIds
|
|
)
|
|
|
|
if ($ProcessIds.Count -eq 0)
|
|
{
|
|
return @()
|
|
}
|
|
|
|
return @(
|
|
Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue |
|
|
Where-Object { $ProcessIds -contains [int]$_.OwningProcess } |
|
|
Sort-Object OwningProcess, LocalPort |
|
|
ForEach-Object {
|
|
[pscustomobject][ordered]@{
|
|
processId = [int]$_.OwningProcess
|
|
localAddress = [string]$_.LocalAddress
|
|
localPort = [int]$_.LocalPort
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
function Stop-OwnedPackageProcesses {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ResolvedPackageRoot,
|
|
[int[]]$ExcludedProcessIds = @()
|
|
)
|
|
|
|
$StoppedProcessIds = New-Object 'System.Collections.Generic.HashSet[int]'
|
|
for ($Attempt = 0; $Attempt -lt 5; $Attempt += 1)
|
|
{
|
|
$OwnedProcessIds = @(
|
|
Get-OwnedPackageProcessIds `
|
|
-ResolvedPackageRoot $ResolvedPackageRoot `
|
|
-ExcludedProcessIds $ExcludedProcessIds
|
|
)
|
|
if ($OwnedProcessIds.Count -eq 0)
|
|
{
|
|
break
|
|
}
|
|
|
|
foreach ($OwnedProcessId in $OwnedProcessIds)
|
|
{
|
|
Stop-Process -Id $OwnedProcessId -Force -ErrorAction SilentlyContinue
|
|
[void]$StoppedProcessIds.Add([int]$OwnedProcessId)
|
|
}
|
|
Start-Sleep -Milliseconds 200
|
|
}
|
|
|
|
return @($StoppedProcessIds)
|
|
}
|
|
|
|
$CandidateExecutablePaths = @(
|
|
(Join-Path $PackageRoot 'Windows\UnrealHyperTwist.exe'),
|
|
(Join-Path $PackageRoot 'WindowsNoEditor\UnrealHyperTwist.exe'),
|
|
(Join-Path $PackageRoot 'UnrealHyperTwist.exe')
|
|
)
|
|
|
|
$LauncherExecutablePath = $CandidateExecutablePaths |
|
|
Where-Object { Test-Path $_ } |
|
|
Select-Object -First 1
|
|
if ($null -eq $LauncherExecutablePath)
|
|
{
|
|
throw "No packaged UnrealHyperTwist executable was found beneath '$PackageRoot'."
|
|
}
|
|
|
|
$PackagedExecutableDirectory = Split-Path -Parent $LauncherExecutablePath
|
|
$ClassicDefaultMap = '/Game/HyperTwistTraining/Maps/L_HyperTwist_ClassicTraining'
|
|
$StartupRouteByMap = @{
|
|
$ClassicDefaultMap = ''
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_FollowAlongTraining' = 'follow-along-training'
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube4D_2x2x2x2Training' = 'magic-cube-4d-2x2x2x2'
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube4D_3x3x3x3Training' = 'magic-cube-4d-3x3x3x3'
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube4D_4x4x4x4Training' = 'magic-cube-4d-4x4x4x4'
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube4D_5x5x5x5Training' = 'magic-cube-4d-5x5x5x5'
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube4D_6x6x6x6Training' = 'magic-cube-4d-6x6x6x6'
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining' = 'magic-120-cell-training'
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining' = 'magic-cube-5d-training'
|
|
}
|
|
$FourDimensionalOrderByMap = @{
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube4D_2x2x2x2Training' = 2
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube4D_3x3x3x3Training' = 3
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube4D_4x4x4x4Training' = 4
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube4D_5x5x5x5Training' = 5
|
|
'/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube4D_6x6x6x6Training' = 6
|
|
}
|
|
if (-not $StartupRouteByMap.ContainsKey($MapUrl))
|
|
{
|
|
throw "No first-party packaged startup route is registered for smoke map '$MapUrl'."
|
|
}
|
|
$StartupRouteId = [string]$StartupRouteByMap[$MapUrl]
|
|
$LaunchMode = if ($MapUrl -eq $ClassicDefaultMap)
|
|
{
|
|
'bootstrap-default-map'
|
|
}
|
|
else
|
|
{
|
|
'first-party-startup-route'
|
|
}
|
|
$ExecutablePath = $LauncherExecutablePath
|
|
|
|
$LogDirectory = Join-Path $PackageRoot 'validation\logs'
|
|
New-Item -ItemType Directory -Force -Path $LogDirectory | Out-Null
|
|
$LogToken = if ([string]::IsNullOrWhiteSpace($MapUrl))
|
|
{
|
|
'classic-cube-runtime'
|
|
}
|
|
else
|
|
{
|
|
($MapUrl -replace '[\\/:*?"<>| ]', '_')
|
|
}
|
|
$RuntimeLogPath = Join-Path $LogDirectory ("{0}.log" -f $LogToken)
|
|
$RuntimeDiagnosticsPath = Join-Path $LogDirectory ("{0}.hyperdiagnostics.log" -f $LogToken)
|
|
Remove-Item -LiteralPath $RuntimeLogPath -Force -ErrorAction SilentlyContinue
|
|
Remove-Item -LiteralPath $RuntimeDiagnosticsPath -Force -ErrorAction SilentlyContinue
|
|
|
|
$ArgumentList = @()
|
|
if (-not [string]::IsNullOrWhiteSpace($StartupRouteId))
|
|
{
|
|
# Nondefault smokes use the same registered product route as the launch
|
|
# menu. Generic startup URLs are overridden by the packaged first-run
|
|
# game-mode authority and therefore are not valid route evidence.
|
|
$ArgumentList += "-HyperTwistStartupRoute=$StartupRouteId"
|
|
}
|
|
$ArgumentList += @(
|
|
"-ResX=$ResX",
|
|
"-ResY=$ResY",
|
|
'-windowed',
|
|
'-log',
|
|
'-FORCELOGFLUSH',
|
|
"-abslog=$RuntimeLogPath",
|
|
"-HyperTwistDiagnosticsLog=`"$RuntimeDiagnosticsPath`""
|
|
)
|
|
|
|
if ($UseNullRHI)
|
|
{
|
|
$ArgumentList += '-NullRHI'
|
|
}
|
|
|
|
if ($NoSound)
|
|
{
|
|
$ArgumentList += '-nosound'
|
|
}
|
|
|
|
Write-Host "Launching packaged classic-cube validation lane from '$ExecutablePath'..."
|
|
$ResolvedPackageRoot = (Resolve-Path -LiteralPath $PackageRoot).Path
|
|
$ExistingPackageProcessIds = @(
|
|
Get-OwnedPackageProcessIds -ResolvedPackageRoot $ResolvedPackageRoot
|
|
)
|
|
$GeneratedAtUtc = [DateTime]::UtcNow.ToString('o')
|
|
$Process = $null
|
|
$RequiresClassicPresentation = $MapUrl -match '/L_HyperTwist_(Classic|FollowAlong)Training$'
|
|
$HigherDimensionalFamily = if ($MapUrl -match '/L_HyperTwist_Magic120CellTraining$')
|
|
{
|
|
'magic120cell'
|
|
}
|
|
elseif ($MapUrl -match '/L_HyperTwist_MagicCube5DTraining$')
|
|
{
|
|
'magiccube5d'
|
|
}
|
|
else
|
|
{
|
|
''
|
|
}
|
|
$ExpectedHigherDimensionalElementCount = if ($HigherDimensionalFamily -eq 'magic120cell')
|
|
{
|
|
120
|
|
}
|
|
elseif ($HigherDimensionalFamily -eq 'magiccube5d')
|
|
{
|
|
242
|
|
}
|
|
else
|
|
{
|
|
0
|
|
}
|
|
$FourDimensionalOrder = if ($FourDimensionalOrderByMap.ContainsKey($MapUrl))
|
|
{
|
|
[int]$FourDimensionalOrderByMap[$MapUrl]
|
|
}
|
|
else
|
|
{
|
|
0
|
|
}
|
|
$ExpectedFourDimensionalStatePieceCount = if ($FourDimensionalOrder -gt 0)
|
|
{
|
|
[int][Math]::Pow($FourDimensionalOrder, 4)
|
|
}
|
|
else
|
|
{
|
|
0
|
|
}
|
|
$ExpectedFourDimensionalVisiblePieceViewCount = if ($FourDimensionalOrder -eq 2)
|
|
{
|
|
64
|
|
}
|
|
elseif ($FourDimensionalOrder -gt 0)
|
|
{
|
|
[int][Math]::Pow($FourDimensionalOrder, 3)
|
|
}
|
|
else
|
|
{
|
|
0
|
|
}
|
|
$Report = [ordered]@{
|
|
reportVersion = 'ht-desktop-map-package-smoke/v8'
|
|
generatedAtUtc = $GeneratedAtUtc
|
|
packageRoot = $ResolvedPackageRoot
|
|
launchMode = $LaunchMode
|
|
startupRouteId = $StartupRouteId
|
|
launcherExecutablePath = $LauncherExecutablePath
|
|
executablePath = $ExecutablePath
|
|
mapUrl = $MapUrl
|
|
runtimeLogPath = $RuntimeLogPath
|
|
runtimeLogExists = $false
|
|
runtimeDiagnosticsPath = $RuntimeDiagnosticsPath
|
|
runtimeDiagnosticsExists = $false
|
|
detectedFatalLogLines = @()
|
|
mapLoadLogLines = @()
|
|
requiresClassicPresentation = $RequiresClassicPresentation
|
|
classicPresentationLogLines = @()
|
|
classicScrambleSettlementLogLines = @()
|
|
higherDimensionalFamily = $HigherDimensionalFamily
|
|
expectedHigherDimensionalElementCount = $ExpectedHigherDimensionalElementCount
|
|
higherDimensionalPresentationLogLines = @()
|
|
higherDimensionalPaletteLogLines = @()
|
|
fourDimensionalOrder = $FourDimensionalOrder
|
|
expectedFourDimensionalStatePieceCount = $ExpectedFourDimensionalStatePieceCount
|
|
expectedFourDimensionalVisiblePieceViewCount = $ExpectedFourDimensionalVisiblePieceViewCount
|
|
fourDimensionalPresentationLogLines = @()
|
|
smokeSeconds = $SmokeSeconds
|
|
resolution = [ordered]@{
|
|
width = $ResX
|
|
height = $ResY
|
|
}
|
|
keepRunning = [bool]$KeepRunning
|
|
useNullRhi = [bool]$UseNullRHI
|
|
noSound = [bool]$NoSound
|
|
result = 'failed'
|
|
processId = $null
|
|
processIds = @()
|
|
processCommandLines = @()
|
|
ownedListeningTcpEndpoints = @()
|
|
traceControlListenerLines = @()
|
|
traceControlListeningEndpoints = @()
|
|
startupRouteCommandLineEvidence = @()
|
|
startupRouteEvidenceLines = @()
|
|
processStopped = $false
|
|
stoppedProcessIds = @()
|
|
exitCode = $null
|
|
error = $null
|
|
}
|
|
|
|
try
|
|
{
|
|
$Process = Start-Process `
|
|
-FilePath $ExecutablePath `
|
|
-ArgumentList $ArgumentList `
|
|
-WorkingDirectory (Split-Path -Parent $ExecutablePath) `
|
|
-PassThru
|
|
Start-Sleep -Seconds $SmokeSeconds
|
|
|
|
$Process.Refresh()
|
|
$Report.processId = $Process.Id
|
|
$OwnedProcessRecords = @(
|
|
Get-OwnedPackageProcessRecords `
|
|
-ResolvedPackageRoot $ResolvedPackageRoot `
|
|
-ExcludedProcessIds $ExistingPackageProcessIds
|
|
)
|
|
$Report.processIds = @($OwnedProcessRecords | Select-Object -ExpandProperty ProcessId)
|
|
$Report.processCommandLines = @(
|
|
$OwnedProcessRecords |
|
|
ForEach-Object { [string]$_.CommandLine } |
|
|
Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
|
|
)
|
|
$Report.ownedListeningTcpEndpoints = @(
|
|
Get-OwnedListeningTcpEndpoints -ProcessIds $Report.processIds
|
|
)
|
|
$Report.traceControlListeningEndpoints = @(
|
|
$Report.ownedListeningTcpEndpoints |
|
|
Where-Object { [int]$_.localPort -eq 1985 }
|
|
)
|
|
|
|
Start-Sleep -Milliseconds 500
|
|
$Report.runtimeLogExists = Test-Path -LiteralPath $RuntimeLogPath
|
|
$Report.runtimeDiagnosticsExists = Test-Path -LiteralPath $RuntimeDiagnosticsPath
|
|
$Report.detectedFatalLogLines = @(
|
|
@(Get-FatalRuntimeLogMatches -RuntimeLogPath $RuntimeLogPath) +
|
|
@(Get-FatalRuntimeLogMatches -RuntimeLogPath $RuntimeDiagnosticsPath)
|
|
)
|
|
if (-not $Report.runtimeDiagnosticsExists)
|
|
{
|
|
throw 'Packaged launch did not write its direct HyperTwist diagnostics log.'
|
|
}
|
|
if ($Report.detectedFatalLogLines.Count -gt 0)
|
|
{
|
|
throw (
|
|
"Packaged classic-cube runtime log recorded fatal startup evidence: " +
|
|
($Report.detectedFatalLogLines -join ' | ')
|
|
)
|
|
}
|
|
|
|
$CurrentRuntimeLogLines = @(
|
|
if ($Report.runtimeLogExists)
|
|
{
|
|
Get-Content -LiteralPath $RuntimeLogPath
|
|
}
|
|
)
|
|
$CurrentRuntimeDiagnosticsLines = @(
|
|
if ($Report.runtimeDiagnosticsExists)
|
|
{
|
|
Get-Content -LiteralPath $RuntimeDiagnosticsPath
|
|
}
|
|
)
|
|
$CurrentEvidenceLines = @($CurrentRuntimeLogLines + $CurrentRuntimeDiagnosticsLines)
|
|
$Report.traceControlListenerLines = @(
|
|
$CurrentEvidenceLines |
|
|
Where-Object {
|
|
$_.ToString().IndexOf(
|
|
'Control listening on port',
|
|
[System.StringComparison]::OrdinalIgnoreCase
|
|
) -ge 0
|
|
} |
|
|
ForEach-Object { $_.ToString() }
|
|
)
|
|
if ($Report.traceControlListenerLines.Count -gt 0 `
|
|
-or $Report.traceControlListeningEndpoints.Count -gt 0)
|
|
{
|
|
throw 'Packaged runtime opened an Unreal trace-control listener.'
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($StartupRouteId))
|
|
{
|
|
$ExpectedStartupRouteArgument = "-HyperTwistStartupRoute=$StartupRouteId"
|
|
$Report.startupRouteCommandLineEvidence = @(
|
|
$Report.processCommandLines |
|
|
Where-Object {
|
|
$_.IndexOf(
|
|
$ExpectedStartupRouteArgument,
|
|
[System.StringComparison]::OrdinalIgnoreCase
|
|
) -ge 0
|
|
}
|
|
)
|
|
if ($Report.startupRouteCommandLineEvidence.Count -eq 0)
|
|
{
|
|
throw (
|
|
"Packaged bootstrap did not propagate first-party startup route argument " +
|
|
"'$ExpectedStartupRouteArgument'."
|
|
)
|
|
}
|
|
|
|
$ExpectedStartupRouteMarker = "Accepted packaged startup route '$StartupRouteId'"
|
|
$Report.startupRouteEvidenceLines = @(
|
|
$CurrentEvidenceLines |
|
|
Where-Object { $_ -like "*$ExpectedStartupRouteMarker*" } |
|
|
ForEach-Object { $_.ToString() }
|
|
)
|
|
if ($Report.startupRouteEvidenceLines.Count -eq 0)
|
|
{
|
|
throw (
|
|
"Packaged runtime did not accept first-party startup route '$StartupRouteId' " +
|
|
'through the launch controller.'
|
|
)
|
|
}
|
|
}
|
|
|
|
$ExpectedMapLeaf = ($MapUrl -split '/')[-1]
|
|
$Report.mapLoadLogLines = @(
|
|
$CurrentEvidenceLines |
|
|
Where-Object {
|
|
$_ -like "*LogLoad: LoadMap: $MapUrl*" `
|
|
-or $_ -like "*Bringing World $MapUrl*up for play*" `
|
|
-or $_ -like "*Runtime map ready:*$ExpectedMapLeaf*"
|
|
} |
|
|
ForEach-Object { $_.ToString() }
|
|
)
|
|
if ($Report.mapLoadLogLines.Count -eq 0)
|
|
{
|
|
throw "Packaged launch stayed alive but did not load target map '$MapUrl' before the smoke interval ended."
|
|
}
|
|
|
|
if ($FourDimensionalOrder -gt 0)
|
|
{
|
|
$ExpectedFourDimensionalMarker = (
|
|
'Four-dimensional presentation initialized for order {0} with {1} exact state pieces ' +
|
|
'and {2} visible projected piece views; state valid, projection valid.'
|
|
) -f (
|
|
$FourDimensionalOrder,
|
|
$ExpectedFourDimensionalStatePieceCount,
|
|
$ExpectedFourDimensionalVisiblePieceViewCount
|
|
)
|
|
$Report.fourDimensionalPresentationLogLines = @(
|
|
$CurrentEvidenceLines |
|
|
Where-Object {
|
|
$_.ToString().IndexOf(
|
|
'[FourDimensionalPresentation]',
|
|
[System.StringComparison]::Ordinal
|
|
) -ge 0
|
|
} |
|
|
ForEach-Object { $_.ToString() }
|
|
)
|
|
$MatchingFourDimensionalPresentationLines = @(
|
|
$Report.fourDimensionalPresentationLogLines |
|
|
Where-Object { $_ -like "*$ExpectedFourDimensionalMarker*" }
|
|
)
|
|
if ($MatchingFourDimensionalPresentationLines.Count -eq 0)
|
|
{
|
|
throw (
|
|
"Packaged map '$MapUrl' loaded, but the runtime did not confirm order " +
|
|
"$FourDimensionalOrder with $ExpectedFourDimensionalStatePieceCount exact state pieces " +
|
|
"and $ExpectedFourDimensionalVisiblePieceViewCount visible projected piece views."
|
|
)
|
|
}
|
|
}
|
|
|
|
$Report.classicPresentationLogLines = @(
|
|
$CurrentEvidenceLines |
|
|
Where-Object {
|
|
$_ -like '*Classic cube presentation initialized with 26 renderable pieces*'
|
|
} |
|
|
ForEach-Object { $_.ToString() }
|
|
)
|
|
if ($RequiresClassicPresentation -and $Report.classicPresentationLogLines.Count -eq 0)
|
|
{
|
|
throw (
|
|
"Packaged map '$MapUrl' loaded, but the runtime did not confirm its complete 26-cubie " +
|
|
'Classic cube presentation.'
|
|
)
|
|
}
|
|
|
|
$Report.classicScrambleSettlementLogLines = @(
|
|
$CurrentEvidenceLines |
|
|
Where-Object {
|
|
$_ -match 'Classic cube scramble settled with \d+ completed quarter turns, 0 queued rotations, and 26 renderable pieces; renderable state valid\.'
|
|
} |
|
|
ForEach-Object { $_.ToString() }
|
|
)
|
|
if ($RequiresClassicPresentation -and $Report.classicScrambleSettlementLogLines.Count -eq 0)
|
|
{
|
|
throw (
|
|
"Packaged map '$MapUrl' loaded its Classic presentation, but the initial scramble " +
|
|
'did not settle into a coherent 26-cubie state before the smoke interval ended.'
|
|
)
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($HigherDimensionalFamily))
|
|
{
|
|
$ExpectedPresentationMarker = (
|
|
'Higher-dimensional presentation initialized with {0} renderable elements ' +
|
|
'(canonical={0}) for family {1}.'
|
|
) -f $ExpectedHigherDimensionalElementCount, $HigherDimensionalFamily
|
|
$Report.higherDimensionalPresentationLogLines = @(
|
|
$CurrentEvidenceLines |
|
|
Where-Object { $_ -like "*$ExpectedPresentationMarker*" } |
|
|
ForEach-Object { $_.ToString() }
|
|
)
|
|
if ($Report.higherDimensionalPresentationLogLines.Count -eq 0)
|
|
{
|
|
throw (
|
|
"Packaged map '$MapUrl' loaded, but the runtime did not confirm its " +
|
|
"$ExpectedHigherDimensionalElementCount-element $HigherDimensionalFamily presentation."
|
|
)
|
|
}
|
|
|
|
$Report.higherDimensionalPaletteLogLines = @(
|
|
$CurrentEvidenceLines |
|
|
Where-Object {
|
|
$_ -like '*Higher-dimensional projection palette initialized with 6 distinct layer materials.*'
|
|
} |
|
|
ForEach-Object { $_.ToString() }
|
|
)
|
|
if ($Report.higherDimensionalPaletteLogLines.Count -eq 0)
|
|
{
|
|
throw (
|
|
"Packaged map '$MapUrl' loaded its projection, but the runtime did not confirm " +
|
|
'six distinct first-party layer materials.'
|
|
)
|
|
}
|
|
}
|
|
|
|
if ($Report.processIds.Count -eq 0)
|
|
{
|
|
if ($Process.HasExited)
|
|
{
|
|
$Report.exitCode = $Process.ExitCode
|
|
}
|
|
throw "Packaged classic-cube executable exited before the smoke interval completed."
|
|
}
|
|
|
|
$Report.result = 'passed'
|
|
}
|
|
catch
|
|
{
|
|
$Report.error = $_.Exception.Message
|
|
if ($null -ne $Process -and $Process.HasExited)
|
|
{
|
|
$Report.exitCode = $Process.ExitCode
|
|
}
|
|
if (-not $KeepRunning)
|
|
{
|
|
$Report.stoppedProcessIds = @(
|
|
Stop-OwnedPackageProcesses `
|
|
-ResolvedPackageRoot $ResolvedPackageRoot `
|
|
-ExcludedProcessIds $ExistingPackageProcessIds
|
|
)
|
|
$Report.processStopped = $Report.stoppedProcessIds.Count -gt 0
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($ReportPath))
|
|
{
|
|
Write-Utf8JsonFile -Path $ReportPath -Value $Report
|
|
}
|
|
|
|
throw
|
|
}
|
|
|
|
Write-Host "Packaged classic-cube smoke launch succeeded (PID $($Process.Id))."
|
|
if (-not $KeepRunning)
|
|
{
|
|
$Report.stoppedProcessIds = @(
|
|
Stop-OwnedPackageProcesses `
|
|
-ResolvedPackageRoot $ResolvedPackageRoot `
|
|
-ExcludedProcessIds $ExistingPackageProcessIds
|
|
)
|
|
$Report.processStopped = $Report.stoppedProcessIds.Count -gt 0
|
|
Write-Host 'Stopped the complete packaged classic-cube smoke process tree after successful launch validation.'
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($ReportPath))
|
|
{
|
|
Write-Utf8JsonFile -Path $ReportPath -Value $Report
|
|
}
|