[CmdletBinding()] param( [string]$Installer ) $ErrorActionPreference = "Stop" Add-Type -AssemblyName System.Drawing $ProjectRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path function Get-AssociatedIconHash { param([Parameter(Mandatory = $true)][string]$FilePath) $Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($FilePath) if (-not $Icon) { throw "Unable to extract the Windows icon from: $FilePath" } $Bitmap = New-Object System.Drawing.Bitmap 32, 32 $Graphics = [System.Drawing.Graphics]::FromImage($Bitmap) $Stream = New-Object System.IO.MemoryStream $Hasher = [System.Security.Cryptography.SHA256]::Create() try { $Graphics.Clear([System.Drawing.Color]::Transparent) $Graphics.DrawIcon($Icon, 0, 0) $Bitmap.Save($Stream, [System.Drawing.Imaging.ImageFormat]::Png) $Hash = $Hasher.ComputeHash($Stream.ToArray()) return ([System.BitConverter]::ToString($Hash)).Replace("-", "") } finally { $Hasher.Dispose() $Stream.Dispose() $Graphics.Dispose() $Bitmap.Dispose() $Icon.Dispose() } } if (-not $Installer) { $Installer = Get-ChildItem ` -LiteralPath (Join-Path $ProjectRoot "dist") ` -Filter "DoctorWorkstation-Setup-Windows-x64-*.exe" ` -File | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName } if (-not $Installer -or -not (Test-Path -LiteralPath $Installer -PathType Leaf)) { throw "Windows installer was not found: $Installer" } $Installer = (Resolve-Path -LiteralPath $Installer).Path $InstallerIconHash = Get-AssociatedIconHash -FilePath $Installer $TempBase = [System.IO.Path]::GetFullPath([System.IO.Path]::GetTempPath()) $SmokeRoot = [System.IO.Path]::GetFullPath((Join-Path $TempBase ( "doctor-workstation-installer-smoke-" + [guid]::NewGuid().ToString("N") ))) if (-not $SmokeRoot.StartsWith($TempBase, [System.StringComparison]::OrdinalIgnoreCase) -or -not ([System.IO.Path]::GetFileName($SmokeRoot)).StartsWith( "doctor-workstation-installer-smoke-" )) { throw "Refusing to use an unsafe installer smoke directory: $SmokeRoot" } $InstallDirectory = Join-Path $SmokeRoot "install" $SetupLog = Join-Path $SmokeRoot "setup.log" $UninstallLog = Join-Path $SmokeRoot "uninstall.log" New-Item -ItemType Directory -Path $SmokeRoot -Force | Out-Null $SetupProcess = Start-Process ` -FilePath $Installer ` -ArgumentList @( "/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/CURRENTUSER", "/DIR=$InstallDirectory", "/MERGETASKS=!desktopicon", "/LOG=$SetupLog" ) ` -Wait ` -PassThru ` -WindowStyle Hidden if ($SetupProcess.ExitCode -ne 0) { throw "Installer exited with code $($SetupProcess.ExitCode). See $SetupLog" } $InstalledExecutable = Join-Path $InstallDirectory "DoctorWorkstation.exe" if (-not (Test-Path -LiteralPath $InstalledExecutable -PathType Leaf)) { throw "Installed executable is missing: $InstalledExecutable" } $Uninstaller = Join-Path $InstallDirectory "unins000.exe" if (-not (Test-Path -LiteralPath $Uninstaller -PathType Leaf)) { throw "Uninstaller is missing: $Uninstaller" } $InstalledIconHash = Get-AssociatedIconHash -FilePath $InstalledExecutable $UninstallerIconHash = Get-AssociatedIconHash -FilePath $Uninstaller if ($InstalledIconHash -ne $InstallerIconHash) { throw "Installed executable icon does not match the installer brand icon" } if ($UninstallerIconHash -ne $InstallerIconHash) { throw "Uninstaller icon does not match the installer brand icon" } $Environment = @{ "DOCTOR_CONFIG_DIR" = (Join-Path $SmokeRoot "config") "DOCTOR_LOG_DIR" = (Join-Path $SmokeRoot "logs") "DOCTOR_API_BASE_URL" = "https://127.0.0.1:9" "DOCTOR_DEMO_MODE" = "true" "DOCTOR_VIDEO_MODE" = "embedded" "DOCTOR_SMOKE_TEST" = "1" "QT_QPA_PLATFORM" = "offscreen" } $PreviousEnvironment = @{} $ApplicationExitCode = $null $UninstallExitCode = $null try { foreach ($Name in $Environment.Keys) { $PreviousEnvironment[$Name] = [Environment]::GetEnvironmentVariable($Name, "Process") [Environment]::SetEnvironmentVariable($Name, $Environment[$Name], "Process") } $ApplicationProcess = Start-Process ` -FilePath $InstalledExecutable ` -ArgumentList "--smoke-test" ` -Wait ` -PassThru ` -WindowStyle Hidden $ApplicationExitCode = $ApplicationProcess.ExitCode } finally { foreach ($Name in $Environment.Keys) { [Environment]::SetEnvironmentVariable($Name, $PreviousEnvironment[$Name], "Process") } if (Test-Path -LiteralPath $Uninstaller -PathType Leaf) { $UninstallProcess = Start-Process ` -FilePath $Uninstaller ` -ArgumentList @( "/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/LOG=$UninstallLog" ) ` -Wait ` -PassThru ` -WindowStyle Hidden $UninstallExitCode = $UninstallProcess.ExitCode } } if ($ApplicationExitCode -ne 0) { throw "Installed application smoke test exited with code $ApplicationExitCode" } if ($UninstallExitCode -ne 0) { throw "Uninstaller exited with code $UninstallExitCode. See $UninstallLog" } Start-Sleep -Milliseconds 500 if (Test-Path -LiteralPath $InstalledExecutable) { throw "Uninstaller left the installed executable behind: $InstalledExecutable" } Write-Host "Installer icon/install/start/uninstall smoke test passed." -ForegroundColor Green Write-Host "Smoke logs and isolated user data: $SmokeRoot"