Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/Test-config-behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Test configuration has the following options:
- **desc** - String description of the command
- **host_run** - Optional string command to run on the host
- **guest_run** - Optional string command to run on each client VM
- **guest_run_interactive** - Optional boolean (default: `false`). When `true`, `guest_run` is executed in the interactive desktop session (Session 1) via a UI agent instead of WinRM (Session 0). Useful for commands that need UI access. The command is synchronous with a default timeout of 120 seconds.
- **guest_reboot** - This option is only applicable to post-start commands and is ignored for pre and post test commands. It controls whether the guest VM should be rebooted after certain operations, but has no effect in this context.
- **files_action** - Array of file operations to perform

Expand All @@ -33,12 +34,12 @@ Test configuration has the following options:
Test configuration is executed in the following order:

1. **Pre test commands**
1. Guest command is executed on each client VM
1. Guest command is executed on each client VM (via WinRM or interactive session depending on `guest_run_interactive`)
2. Host command is executed on the host
3. Files action is executed
2. **Test execution**
3. **Post test commands**
1. Guest command is executed on each client VM
1. Guest command is executed on each client VM (via WinRM or interactive session depending on `guest_run_interactive`)
2. Host command is executed on the host
3. Files action is executed

Expand Down
1 change: 1 addition & 0 deletions lib/all.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,5 @@ module AutoHCK
autoload_relative :Tests, 'engines/hcktest/tests'
autoload_relative :Tools, 'engines/hcktest/tools'
autoload_relative :Trap, 'trap'
autoload_relative :UIExecutor, 'auxiliary/ui_agent/ui_executor'
end
115 changes: 115 additions & 0 deletions lib/auxiliary/ui_agent/auto_hck_ui_agent.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# UIAgent - Runs in the interactive Windows session (Session 1).
# Watches a queue directory for command files, executes them with
# full desktop access, and writes JSON results.
#
# Communication protocol:
# Request: C:\UIAgent\queue\<id>.ps1 (PowerShell script to execute)
# Response: C:\UIAgent\queue\<id>.json (JSON with exit_code, stdout, stderr)
# The agent deletes the .ps1 after execution.

$ErrorActionPreference = 'Stop'

$QueueDir = 'C:\UIAgent\queue'
$LogFile = 'C:\UIAgent\agent.log'
$PollIntervalMs = 500

function Write-AgentLog {
param([string]$Message)
$ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$line = "[$ts] $Message"
Add-Content -Path $LogFile -Value $line -ErrorAction SilentlyContinue
}

function Initialize-Agent {
if (-not (Test-Path $QueueDir)) {
New-Item -ItemType Directory -Path $QueueDir -Force | Out-Null
}
if (-not (Test-Path (Split-Path $LogFile))) {
New-Item -ItemType Directory -Path (Split-Path $LogFile) -Force | Out-Null
}

# Clean up stale requests from a previous boot
Get-ChildItem -Path $QueueDir -Filter '*.ps1' -ErrorAction SilentlyContinue |
Remove-Item -Force -ErrorAction SilentlyContinue

Write-AgentLog "Agent started in session $([System.Diagnostics.Process]::GetCurrentProcess().SessionId)"

# Signal to the host that initialization is complete and it's safe to submit commands
[System.IO.File]::WriteAllText('C:\UIAgent\agent.ready', (Get-Date -Format 'o'))
}

# Runs a queued .ps1 in a child process, captures output, and writes
# a .json result that the host (UIExecutor) polls for.
function Invoke-QueuedCommand {
param([System.IO.FileInfo]$ScriptFile)

$id = [System.IO.Path]::GetFileNameWithoutExtension($ScriptFile.Name)
$resultPath = Join-Path $QueueDir "$id.json"

Write-AgentLog "Executing command: $id"

$stdout = ''
$stderr = ''
$exitCode = 0

try {
$prevPref = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
$output = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $ScriptFile.FullName 2>&1
$ErrorActionPreference = $prevPref

# Separate normal output from error records.
$stdoutLines = @()
$stderrLines = @()
foreach ($line in $output) {
if ($line -is [System.Management.Automation.ErrorRecord]) {
$stderrLines += $line.ToString()
} else {
$stdoutLines += $line.ToString()
}
}
$stdout = $stdoutLines -join "`n"
$stderr = $stderrLines -join "`n"
$exitCode = $LASTEXITCODE
if ($null -eq $exitCode) { $exitCode = 0 }
}
catch {
$stderr = $_.Exception.Message
$exitCode = 1
}

$result = @{
id = $id
exit_code = $exitCode
stdout = $stdout
stderr = $stderr
completed = (Get-Date -Format 'o')
} | ConvertTo-Json -Depth 5

try {
[System.IO.File]::WriteAllText($resultPath, $result)
Write-AgentLog "Command $id finished with exit code $exitCode"
}
finally {
Remove-Item -Path $ScriptFile.FullName -Force -ErrorAction SilentlyContinue
}
}

# --- Main loop: poll the queue directory for new .ps1 requests ---
Initialize-Agent

while ($true) {
try {
$scripts = Get-ChildItem -Path $QueueDir -Filter '*.ps1' -ErrorAction SilentlyContinue |
Sort-Object CreationTime

foreach ($script in $scripts) {
Invoke-QueuedCommand -ScriptFile $script
}
}
catch {
Write-AgentLog "Error in main loop: $_"
}

Start-Sleep -Milliseconds $PollIntervalMs
}
Loading
Loading