fixed Vagrant tests for Windows
This commit is contained in:
@@ -20,6 +20,10 @@
|
||||
AppId={{8F2B3A7E-4D1C-4E8F-9A2B-6C7D8E9F0A1B}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
; AppVerName controls the registry DisplayName value. Without it Inno Setup
|
||||
; defaults to "AppName version AppVersion" which breaks exact-name lookups.
|
||||
; Windows convention: DisplayName = product name, DisplayVersion = version.
|
||||
AppVerName={#MyAppName}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
@@ -31,7 +35,8 @@ OutputDir=..\..\dist
|
||||
OutputBaseFilename=android-file-handler-setup
|
||||
SetupIconFile=..\..\icon_media\robot_files_256.ico
|
||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||
Compression=lzma2/ultra64
|
||||
Compression=lzma2/normal
|
||||
LZMADictionarySize=16384
|
||||
SolidCompression=yes
|
||||
WizardStyle=modern
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
# 1. Build the installer locally (PyInstaller + Inno Setup)
|
||||
# 2. Ensure the Vagrant VM is running and has a "clean" snapshot
|
||||
# 3. Restore to the clean snapshot
|
||||
# 4. Push the installer to the VM
|
||||
# 5. Run the test script inside the VM via WinRM
|
||||
# 4. Transfer build assets to the VM and compile the installer (Inno Setup)
|
||||
# 5. Run the test script inside the VM via PSSession
|
||||
# 6. Retrieve screenshots and results to the host
|
||||
# 7. Print results summary to CLI
|
||||
# 8. Revert VM back to clean snapshot
|
||||
@@ -17,10 +17,10 @@
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Vagrant and Hyper-V enabled on the host
|
||||
# - Inno Setup 6 installed (default path or in PATH)
|
||||
# - Poetry environment set up
|
||||
# - The VM created and "clean" snapshot saved:
|
||||
# cd vagrant && vagrant up --provider=hyperv && vagrant snapshot save clean
|
||||
# Note: Inno Setup 6.7.1 is installed inside the VM by provision.ps1 — no host install needed.
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
@@ -33,7 +33,8 @@ param(
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
|
||||
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
|
||||
$vmSession = $null # PSSession — closed in finally block
|
||||
$VagrantDir = Join-Path $RepoRoot "vagrant"
|
||||
$Timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
|
||||
@@ -51,21 +52,22 @@ function Write-Step {
|
||||
Push-Location $VagrantDir
|
||||
try {
|
||||
|
||||
# ── Step 1: Build the installer locally ──────────────────────────────────
|
||||
# ── Step 1: Build the Windows executable (host, PyInstaller only) ────────
|
||||
# Inno Setup runs inside the VM — no host install of Inno Setup required.
|
||||
|
||||
$installerPath = Join-Path $RepoRoot "dist\android-file-handler-setup.exe"
|
||||
$pyinstallerExe = Join-Path $RepoRoot "dist\android-file-handler-windows.exe"
|
||||
$AppVersion = ""
|
||||
|
||||
if ($SkipBuild) {
|
||||
Write-Step "Skipping build (-SkipBuild specified)"
|
||||
if (-not (Test-Path $installerPath)) {
|
||||
Write-Host "ERROR: -SkipBuild was specified but installer not found at $installerPath" -ForegroundColor Red
|
||||
Write-Step "Skipping PyInstaller build (-SkipBuild specified)"
|
||||
if (-not (Test-Path $pyinstallerExe)) {
|
||||
Write-Host "ERROR: -SkipBuild was specified but exe not found at $pyinstallerExe" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Step "Building installer locally (PyInstaller + Inno Setup)"
|
||||
Write-Step "Building Windows executable (PyInstaller)"
|
||||
|
||||
# PyInstaller — compile the exe
|
||||
Push-Location $RepoRoot
|
||||
try {
|
||||
Write-Host " Running PyInstaller..." -ForegroundColor Yellow
|
||||
@@ -75,45 +77,27 @@ try {
|
||||
exit 1
|
||||
}
|
||||
|
||||
$pyinstallerExe = Join-Path $RepoRoot "dist\android-file-handler-windows.exe"
|
||||
if (-not (Test-Path $pyinstallerExe)) {
|
||||
Write-Host "ERROR: PyInstaller output not found at $pyinstallerExe" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Write-Host " PyInstaller output: $pyinstallerExe" -ForegroundColor Green
|
||||
|
||||
# Inno Setup — compile the installer
|
||||
Write-Host " Running Inno Setup..." -ForegroundColor Yellow
|
||||
$isccPaths = @(
|
||||
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe",
|
||||
"C:\Program Files\Inno Setup 6\ISCC.exe"
|
||||
)
|
||||
$iscc = $isccPaths | Where-Object { Test-Path $_ } | Select-Object -First 1
|
||||
if (-not $iscc) {
|
||||
$iscc = Get-Command ISCC.exe -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source
|
||||
}
|
||||
if (-not $iscc) {
|
||||
Write-Host "ERROR: Inno Setup (ISCC.exe) not found. Install Inno Setup 6 or add it to PATH." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
& $iscc "scripts\windows\android-file-handler-setup.iss"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "ERROR: Inno Setup failed with exit code $LASTEXITCODE" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not (Test-Path $installerPath)) {
|
||||
Write-Host "ERROR: Installer not found at $installerPath after Inno Setup" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Write-Host " Installer built: $installerPath" -ForegroundColor Green
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
}
|
||||
}
|
||||
|
||||
# Capture version to pass to Inno Setup inside the VM
|
||||
Push-Location $RepoRoot
|
||||
try {
|
||||
$AppVersion = (poetry version -s).Trim()
|
||||
Write-Host " App version: $AppVersion" -ForegroundColor DarkGray
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# ── Step 2: Verify VM and snapshot exist ─────────────────────────────────
|
||||
|
||||
Write-Step "Checking Vagrant VM status"
|
||||
@@ -137,51 +121,98 @@ try {
|
||||
Write-Step "Restoring VM to '$SnapshotName' snapshot"
|
||||
vagrant snapshot restore $SnapshotName
|
||||
|
||||
# ── Step 4: Push installer to the VM ─────────────────────────────────────
|
||||
# ── Establish direct PSSession ────────────────────────────────────────────
|
||||
# vagrant winrm uses a subprocess whose args are subject to the Windows
|
||||
# command-line length limit (~32 KB). Large file transfers exceed that limit.
|
||||
# A direct PSSession has no such restriction and supports
|
||||
# Copy-Item -ToSession / -FromSession for binary files of any size.
|
||||
|
||||
Write-Step "Pushing installer to the VM"
|
||||
Write-Step "Establishing PSSession to VM"
|
||||
|
||||
$remoteInstallerDir = "C:\vagrant_installer"
|
||||
$remoteInstallerPath = "$remoteInstallerDir\android-file-handler-setup.exe"
|
||||
|
||||
vagrant winrm --shell powershell --command `
|
||||
"New-Item -ItemType Directory -Path '$remoteInstallerDir' -Force | Out-Null"
|
||||
|
||||
# Base64-encode the installer, transfer via WinRM, decode on the VM
|
||||
$installerBytes = [IO.File]::ReadAllBytes($installerPath)
|
||||
$installerSizeMB = [math]::Round($installerBytes.Length / 1MB, 1)
|
||||
Write-Host " Installer size: ${installerSizeMB} MB — transferring via WinRM..." -ForegroundColor Yellow
|
||||
|
||||
# Split into chunks to avoid WinRM message size limits (< 2MB per command)
|
||||
$chunkSize = 1 * 1024 * 1024 # 1 MB chunks
|
||||
$totalChunks = [math]::Ceiling($installerBytes.Length / $chunkSize)
|
||||
|
||||
vagrant winrm --shell powershell --command `
|
||||
"if (Test-Path '$remoteInstallerPath') { Remove-Item '$remoteInstallerPath' -Force }"
|
||||
|
||||
for ($idx = 0; $idx -lt $totalChunks; $idx++) {
|
||||
$offset = $idx * $chunkSize
|
||||
$length = [math]::Min($chunkSize, $installerBytes.Length - $offset)
|
||||
$chunk = New-Object byte[] $length
|
||||
[Array]::Copy($installerBytes, $offset, $chunk, 0, $length)
|
||||
$b64Chunk = [Convert]::ToBase64String($chunk)
|
||||
|
||||
$appendScript = if ($idx -eq 0) {
|
||||
"[IO.File]::WriteAllBytes('$remoteInstallerPath', [Convert]::FromBase64String('$b64Chunk'))"
|
||||
}
|
||||
else {
|
||||
"`$bytes = [Convert]::FromBase64String('$b64Chunk'); " +
|
||||
"`$stream = [IO.File]::Open('$remoteInstallerPath', 'Append'); " +
|
||||
"`$stream.Write(`$bytes, 0, `$bytes.Length); `$stream.Close()"
|
||||
}
|
||||
vagrant winrm --shell powershell --command $appendScript
|
||||
Write-Host " Chunk $($idx + 1)/$totalChunks transferred" -ForegroundColor DarkGray
|
||||
# Ensure host WinRM client is configured to allow Basic authentication
|
||||
# This is required for the connection to succeed when using -Authentication Basic
|
||||
if ((Get-Item WSMan:\localhost\Client\Auth\Basic).Value -ne "true") {
|
||||
Write-Host " Enabling WinRM Client Basic authentication on host..." -ForegroundColor Yellow
|
||||
Set-Item WSMan:\localhost\Client\Auth\Basic -Value $true -Force
|
||||
}
|
||||
|
||||
# Verify transfer
|
||||
$remoteSize = vagrant winrm --shell powershell --command `
|
||||
"(Get-Item '$remoteInstallerPath').Length"
|
||||
Write-Host " Remote file size: $($remoteSize.Trim()) bytes (expected: $($installerBytes.Length))" -ForegroundColor DarkGray
|
||||
$remoteBuildRoot = "C:\vagrant_build"
|
||||
$remoteInstallerPath = "$remoteBuildRoot\dist\android-file-handler-setup.exe"
|
||||
$remoteResultsDir = "C:\vagrant_test_results"
|
||||
|
||||
# vagrant winrm-config reports the plaintext port (5985) used by Vagrant itself.
|
||||
# We extract the host and port, then derive the HTTPS port (typically +1).
|
||||
# If the direct IP fails, we fallback to the forwarded port on localhost.
|
||||
$winrmConfig = & vagrant winrm-config 2>$null | Out-String
|
||||
$vmHost = if ($winrmConfig -match 'HostName\s+(\S+)') { $Matches[1] } else { '127.0.0.1' }
|
||||
$vmPort = if ($winrmConfig -match 'Port\s+(\d+)') { [int]$Matches[1] + 1 } else { 5986 }
|
||||
|
||||
# If we have a direct IP but it's not reachable, fallback to the forwarded port
|
||||
if ($vmHost -ne '127.0.0.1' -and -not (Test-NetConnection -ComputerName $vmHost -Port $vmPort -InformationLevel Quiet)) {
|
||||
Write-Host " Direct IP $vmHost`:$vmPort unreachable. Falling back to localhost..." -ForegroundColor Yellow
|
||||
$vmHost = '127.0.0.1'
|
||||
$vmPort = 55986 # The host-side forwarded port for 5986
|
||||
}
|
||||
|
||||
# Verify connectivity before trying PSSession
|
||||
if (-not (Test-NetConnection -ComputerName $vmHost -Port $vmPort -InformationLevel Quiet)) {
|
||||
Write-Host "ERROR: Could not reach VM at $vmHost`:$vmPort. Ensure 'vagrant up' and provisioning completed successfully." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$vagrantCred = New-Object PSCredential 'vagrant',
|
||||
(ConvertTo-SecureString 'vagrant' -AsPlainText -Force)
|
||||
# SkipCACheck / SkipCNCheck accept the self-signed cert created by provision.ps1.
|
||||
$sessionOpts = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
|
||||
$vmSession = New-PSSession -ComputerName $vmHost -Port $vmPort `
|
||||
-Credential $vagrantCred -Authentication Basic `
|
||||
-UseSSL -SessionOption $sessionOpts
|
||||
|
||||
Write-Host " Connected to VM at $vmHost`:$vmPort" -ForegroundColor DarkGray
|
||||
|
||||
# ── Step 4: Transfer build assets to VM and compile installer ────────────
|
||||
|
||||
Write-Step "Transferring build assets to VM"
|
||||
|
||||
Invoke-Command -Session $vmSession -ScriptBlock {
|
||||
param($root)
|
||||
@("$root\dist", "$root\scripts\windows", "$root\icon_media") |
|
||||
ForEach-Object { New-Item -ItemType Directory -Force -Path $_ | Out-Null }
|
||||
} -ArgumentList $remoteBuildRoot
|
||||
|
||||
$transfers = @(
|
||||
@{ L = Join-Path $RepoRoot "dist\android-file-handler-windows.exe"; R = "$remoteBuildRoot\dist\android-file-handler-windows.exe" },
|
||||
@{ L = Join-Path $RepoRoot "scripts\windows\android-file-handler-setup.iss"; R = "$remoteBuildRoot\scripts\windows\android-file-handler-setup.iss" },
|
||||
@{ L = Join-Path $RepoRoot "LICENSE.txt"; R = "$remoteBuildRoot\LICENSE.txt" },
|
||||
@{ L = Join-Path $RepoRoot "icon_media\robot_files_256.ico"; R = "$remoteBuildRoot\icon_media\robot_files_256.ico" },
|
||||
@{ L = Join-Path $RepoRoot "vagrant\test_installer.ps1"; R = "$remoteBuildRoot\test_installer.ps1" }
|
||||
)
|
||||
foreach ($t in $transfers) {
|
||||
Copy-Item -Path $t.L -Destination $t.R -ToSession $vmSession -Force
|
||||
$sizeMB = [math]::Round((Get-Item $t.L).Length / 1MB, 1)
|
||||
Write-Host " Sent: $(Split-Path -Leaf $t.L) (${sizeMB} MB)" -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
# Unblock transferred files — Copy-Item -ToSession can mark files with a
|
||||
# Zone.Identifier ADS that execution policy may block even under -Scope Process Bypass.
|
||||
Invoke-Command -Session $vmSession -ScriptBlock {
|
||||
param($root)
|
||||
Get-ChildItem -Path $root -Recurse -File |
|
||||
ForEach-Object { Unblock-File -Path $_.FullName -ErrorAction SilentlyContinue }
|
||||
} -ArgumentList $remoteBuildRoot
|
||||
Write-Host " Files unblocked on VM." -ForegroundColor DarkGray
|
||||
|
||||
Write-Step "Building installer inside the VM (Inno Setup 6.7.1)"
|
||||
|
||||
Invoke-Command -Session $vmSession -ScriptBlock {
|
||||
param($buildRoot, $version)
|
||||
& 'C:\Program Files (x86)\Inno Setup 6\ISCC.exe' `
|
||||
"$buildRoot\scripts\windows\android-file-handler-setup.iss" `
|
||||
"/DMyAppVersion=$version"
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
} -ArgumentList $remoteBuildRoot, $AppVersion
|
||||
|
||||
Write-Host " Installer built: $remoteInstallerPath" -ForegroundColor Green
|
||||
|
||||
# ── Step 5: Run tests inside the VM ──────────────────────────────────────
|
||||
|
||||
@@ -189,12 +220,34 @@ try {
|
||||
|
||||
$testExitCode = 0
|
||||
try {
|
||||
vagrant winrm --shell powershell --command `
|
||||
"Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force; & C:\vagrant_project\vagrant\test_installer.ps1 -InstallerPath 'C:\vagrant_installer\android-file-handler-setup.exe'"
|
||||
Invoke-Command -Session $vmSession -ScriptBlock {
|
||||
param($buildRoot, $installer)
|
||||
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
|
||||
& "$buildRoot\test_installer.ps1" -InstallerPath $installer
|
||||
# Propagate the test script's exit code as output so the host can inspect it.
|
||||
$LASTEXITCODE
|
||||
} -ArgumentList $remoteBuildRoot, $remoteInstallerPath |
|
||||
ForEach-Object {
|
||||
if ($_ -is [int]) { $testExitCode = $_ }
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$testExitCode = 1
|
||||
Write-Host "Test script returned failures." -ForegroundColor Yellow
|
||||
Write-Host "Test script encountered an error:" -ForegroundColor Yellow
|
||||
Write-Host " $($_.Exception.Message)" -ForegroundColor Red
|
||||
Write-Host " Category : $($_.CategoryInfo.Category)" -ForegroundColor Red
|
||||
Write-Host " FullError: $_" -ForegroundColor Red
|
||||
}
|
||||
|
||||
# Re-establish the PSSession if the test run left it in a non-Opened state.
|
||||
# An unhandled exception inside Invoke-Command can terminate the remote runspace.
|
||||
if ($vmSession.State -ne 'Opened') {
|
||||
Write-Host " PSSession is '$($vmSession.State)' — re-establishing for results retrieval..." -ForegroundColor Yellow
|
||||
Remove-PSSession $vmSession -ErrorAction SilentlyContinue
|
||||
$vmSession = New-PSSession -ComputerName $vmHost -Port $vmPort `
|
||||
-Credential $vagrantCred -Authentication Basic `
|
||||
-UseSSL -SessionOption $sessionOpts
|
||||
Write-Host " Session re-established." -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
# ── Step 6: Retrieve results and screenshots ─────────────────────────────
|
||||
@@ -205,36 +258,18 @@ try {
|
||||
New-Item -ItemType Directory -Path $LocalResultsDir -Force | Out-Null
|
||||
}
|
||||
|
||||
# Copy results from shared folder on VM via vagrant scp or direct file copy
|
||||
# The test writes to C:\vagrant_test_results inside the VM.
|
||||
# We retrieve via WinRM by reading files.
|
||||
try {
|
||||
# Get list of result files
|
||||
$remoteFiles = vagrant winrm --shell powershell --command `
|
||||
"if (Test-Path 'C:\vagrant_test_results') { Get-ChildItem 'C:\vagrant_test_results' -Name }" 2>&1 |
|
||||
Where-Object { $_ -is [string] -and $_.Trim() -ne "" }
|
||||
$hasResults = Invoke-Command -Session $vmSession -ScriptBlock {
|
||||
param($dir) Test-Path $dir
|
||||
} -ArgumentList $remoteResultsDir
|
||||
|
||||
foreach ($fileName in $remoteFiles) {
|
||||
$trimmedName = $fileName.Trim()
|
||||
if (-not $trimmedName) { continue }
|
||||
|
||||
$remotePath = "C:\vagrant_test_results\$trimmedName"
|
||||
$localPath = Join-Path $LocalResultsDir $trimmedName
|
||||
|
||||
if ($trimmedName -match '\.csv$') {
|
||||
# Text file — read content and save
|
||||
$content = vagrant winrm --shell powershell --command `
|
||||
"Get-Content -Path '$remotePath' -Raw"
|
||||
$content | Out-File -FilePath $localPath -Encoding utf8
|
||||
Write-Host " Retrieved: $trimmedName" -ForegroundColor DarkGray
|
||||
}
|
||||
elseif ($trimmedName -match '\.(png|jpg|bmp)$') {
|
||||
# Binary file — base64 encode, transfer, decode
|
||||
$b64 = vagrant winrm --shell powershell --command `
|
||||
"[Convert]::ToBase64String([IO.File]::ReadAllBytes('$remotePath'))"
|
||||
[IO.File]::WriteAllBytes($localPath, [Convert]::FromBase64String($b64.Trim()))
|
||||
Write-Host " Retrieved: $trimmedName" -ForegroundColor DarkGray
|
||||
}
|
||||
if ($hasResults) {
|
||||
Copy-Item -Path "$remoteResultsDir\*" -Destination $LocalResultsDir `
|
||||
-FromSession $vmSession -Recurse -Force
|
||||
Write-Host " Retrieved results from $remoteResultsDir" -ForegroundColor DarkGray
|
||||
}
|
||||
else {
|
||||
Write-Host " No results directory found on VM." -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
catch {
|
||||
@@ -250,8 +285,8 @@ try {
|
||||
$results = Import-Csv $csvPath
|
||||
$results | Format-Table -AutoSize
|
||||
|
||||
$passCount = ($results | Where-Object { $_.Status -eq "PASS" }).Count
|
||||
$failCount = ($results | Where-Object { $_.Status -eq "FAIL" }).Count
|
||||
$passCount = @($results | Where-Object { $_.Status -eq "PASS" }).Count
|
||||
$failCount = @($results | Where-Object { $_.Status -eq "FAIL" }).Count
|
||||
Write-Host " Passed: $passCount | Failed: $failCount | Total: $($passCount + $failCount)" -ForegroundColor $(if ($failCount -gt 0) { "Red" } else { "Green" })
|
||||
}
|
||||
else {
|
||||
@@ -294,5 +329,6 @@ try {
|
||||
|
||||
}
|
||||
finally {
|
||||
if ($vmSession) { Remove-PSSession $vmSession -ErrorAction SilentlyContinue }
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user