Skip to content

Add UI automation benchmarks (agent-driven and scripted perf) under benchmarks/ui - #1053

Open
Nikola Metulev (nmetulev) wants to merge 8 commits into
mainfrom
nmetulev-ui-automation-agent-benchmark
Open

Nikola Metulev (nmetulev) wants to merge 8 commits into
mainfrom
nmetulev-ui-automation-agent-benchmark

Conversation

@nmetulev

Copy link
Copy Markdown
Member

Description

Adds benchmarks/ui/, two repeatable benchmarks for winapp ui. Rerun them after any winapp ui change to measure its effect.

  • Agent benchmark (run.ps1). A real Copilot CLI agent does natural-language UI tasks in inbox Calculator and WinUI 3 Gallery. The Gallery is pinned to v2.9.3, built from source once and cached. The benchmark measures the agent experience end to end, which is the number that matters: in an earlier comparison, model turns took 75–80% of wall time.
    • Scenarios: seven, including real-world friction such as the app not running, two instances where only one is the target, a minimized window in Scientific mode, a dialog left open, and a cold start.
    • Scoring: the harness checks the final app state itself, through UIA, process count and window state. It never trusts the agent's answer. It also records collateral damage: extra instances, the wrong window modified, or an app closed.
    • Report: pass rate, median wall time, turns, tool calls, tool time vs model time, tokens and credits, plus per-run failure notes.
  • Perf benchmark (perf.ps1). A scripted benchmark with no agent and no credits that tracks winapp ui's own speed so regressions show up.
    • Operations: process startup (winapp --version), list-windows, search, inspect, get-property, get-value, invoke, set-value, wait-for, focus and screenshot. Each runs 20 times after one discarded warm-up, and the report gives the median, p95 and max.
    • Sequences: fixed command sequences that mirror the agent scenarios, reported with total time, pass/fail and per-step times.

Methods and adapters:

  • Two agent methods are checked in: winapp (this repo's plugin plus the CLI) and none (no plugin, and winapp is not available).
  • One perf adapter is checked in: winapp.
  • Other tools run locally through -Method <method.json> or -Adapter <adapter.json>. The README documents both formats.

Safety (both benchmarks):

  • Runs are strictly sequential.
  • The harness kills only processes it launched, matched by PID.
  • A run refuses to start if Calculator or the Gallery is already running, unless you pass -Force.
  • The user's saved Calculator mode is restored at teardown.
  • run.ps1 is resumable: rerunning into the same -OutDir skips finished cells.

benchmarks/agents/lib/Benchmark.psm1 is reused rather than copied. A few helpers were generalized so both benchmarks share Copilot launch, events.jsonl parsing and compare. benchmarks/agents behavior is unchanged; its Pester suite passes.

Usage Example

# Once: build, cache and install the pinned WinUI Gallery (idempotent)
.\benchmarks\ui\setup-gallery.ps1

# Perf benchmark (no credits, ~1.5 min)
.\benchmarks\ui\perf.ps1

# Agent benchmark: dry run, then the full default matrix (7 scenarios × 5 iterations × 2 methods)
.\benchmarks\ui\run.ps1 -Method winapp,none -Plan
.\benchmarks\ui\run.ps1 -Method winapp,none -MaxCredits 1200

# Compare two result folders
.\benchmarks\ui\run.ps1 -Compare benchmarks\ui\results\<a>,benchmarks\ui\results\<b>
.\benchmarks\ui\perf.ps1 -Compare benchmarks\ui\results\<a>,benchmarks\ui\results\<b>

Results go to benchmarks\ui\results\<timestamp>\, which is gitignored and holds JSON plus summary.md.

Related Issue

None.

Type of Change

  • ✨ New feature
  • 🧪 Test update

Checklist

  • New tests added for new functionality (if applicable)
  • Tested locally on Windows

Screenshots / Demo

First full run, measured on 2026-10-09:

  • Machine: Windows 11 Enterprise 10.0.28000, ARM64, Snapdragon X2 Elite, 12 CPUs.
  • winapp: 0.7.2 dev build.
  • Agent run: Copilot CLI 1.0.93 with claude-sonnet-5.5, 5 iterations.

Agent benchmark: 70/70 passed, with no collateral damage. The run took 32 minutes and used 497.5 credits.

Method Median wall time Median turns Credits (35 runs)
winapp 19.4 s 6 315.7
none 21.8 s 5 181.8

How winapp compared with none:

  • Gallery scenarios: winapp was 20–30% faster. Combobox took 19.4 s vs 27.2 s, the dialog left open 38.6 s vs 48.3 s, and cold start 30.3 s vs 39.6 s.
  • Simple Calculator tasks: about the same speed.
  • Minimized window: winapp was slower, 36.9 s and 10 turns vs 15.0 s and 3 turns. See Additional Notes.
  • Cost: winapp used about 1.7× the credits, mostly from the extra input tokens of loading the UI-automation skill.

Perf benchmark (median / p95, ms, measured from process start to exit):

startup list-windows search inspect get-property get-value invoke set-value wait-for focus screenshot
88 / 112 132 / 140 198 / 208 188–220 / 210–225 152 / 171 156 / 172 170 / 182 163 / 170 152 / 162 173 / 195 230 / 249

Sequence medians:

Sequence Commands Median total Result
calc-add 10 1,843 ms pass
calc-two-instances 7 1,318 ms pass
gallery-combobox 6 1,659 ms pass
gallery-dialog 5 1,239 ms pass
calc-minimized-scientific – – fails at step 1 (a known winapp gap; see below)

Additional Notes

  • Validation: the UI suite ran with Invoke-Pester -Path benchmarks\ui\tests and the agents suite with Invoke-Pester -Path benchmarks\agents\tests. Both use no UI and cover:

    • the scenario, method.json and adapter schemas;
    • scoring and collateral-damage detection on recorded fixtures;
    • resumability;
    • the Calculator mode record and restore;
    • reading back the record of processes the benchmark launched, used to recover after an interrupted run;
    • perf statistics and compare.

    Results: UI 130/130 passed; benchmarks/agents 120/120 passed, so it is unchanged. Before the full run, the smoke run passed and fixed several selector and teardown bugs.

  • Known winapp gap that the perf suite surfaces on purpose: while Calculator is minimized, winapp ui sees only the root window, so focus CalculatorResults fails with "No element found". focus on the root window's slug does restore it, but that slug changes on every launch and the command exits 1. The calc-minimized-scientific sequence is kept as a regression signal; it will pass once winapp ui can restore a minimized window. The same gap explains the slower agent result on that scenario.

  • Not run in CI: both benchmarks drive the desktop and the agent benchmark costs credits, so they are run manually. Only the Pester suites are suitable for automation.

Adds a rerunnable benchmark where Copilot CLI drives Calculator and the
WinUI 3 Gallery with a method (the winapp plugin, or none), and the
harness scores the final app state itself through UIA, process counts and
window state. Also adds a scripted, agent-free perf benchmark that times
each winapp ui operation and fixed command sequences.

Reuses benchmarks/agents/lib/Benchmark.psm1, extended with timing fields
and Format-Count.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Record the mode the first launched Calculator shows, switch every owned
Calculator back to it at teardown instead of forcing Standard, and keep
the record across interrupted runs. Launch readiness now waits for the
nav button, which exists in every Calculator mode.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- Invoke-InstanceSetup: don't enumerate .Keys on an empty expect hashtable under StrictMode
- Start-ScenarioInstances: return an array so callers don't get a nested list
- perf.json: set-value targets the Gallery search Edit (TextBox), not its Group wrapper
- perf.ps1: exit 0/1 from the run outcome instead of a stray native exit code

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…timates

- run.ps1: exit 0 only when every run passed and none stopped early, instead of a stray native exit code
- run.ps1: rerunning a finished -OutDir keeps the existing summary.md instead of rewriting it without its header
- config.json: per-session estimates from the first full run (7.1 credits, 0.46 min measured)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- Read the ownership ledger with start times kept as their saved strings. ConvertFrom-Json turned
  them into DateTime, so recovery after an interrupted run never matched and left the processes the
  benchmark had launched running (and then treated them as foreign).
- Record a run's abort reason inside finally, so the early returns for an app opened mid-run or a
  stuck setup process actually stop the loop.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 9, 2026 21:14
@nmetulev Nikola Metulev (nmetulev) added the agent-preparing Agent is addressing feedback or completing required validation and CI label Oct 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Resume integrity, perf error handling, exit statuses, and sequence statistics currently produce skipped or misleading benchmark results.

6 open findings
What changed in this PR

Adds repeatable agent-driven and scripted performance benchmarks for winapp ui, including desktop setup, safety tracking, scoring, reporting, and comparison tooling.

Changes:

  • Adds seven Calculator and WinUI Gallery benchmark scenarios.
  • Adds extensible agent methods and scripted perf adapters.
  • Adds offline Pester coverage and shared agent timing metrics.
File Description
.gitignore Ignores UI benchmark output and cache.
benchmarks/​ui/​README.md Documents setup, usage, results, and safety.
benchmarks/​ui/​config.json Defines defaults and pinned Gallery build.
benchmarks/​ui/​run.ps1 Runs and scores agent-driven scenarios.
benchmarks/​ui/​perf.ps1 Runs scripted performance measurements.
benchmarks/​ui/​perf.json Defines operations and command sequences.
benchmarks/​ui/​setup-gallery.ps1 Builds and installs the benchmark Gallery.
benchmarks/​ui/​lib/​UiBenchmark.psm1 Implements schemas, scoring, resume, and reports.
benchmarks/​ui/​lib/​AppState.psm1 Manages application state and ownership.
benchmarks/​ui/​lib/​Perf.psm1 Implements adapters, statistics, and perf reports.
benchmarks/​ui/​adapters/​winapp.json Maps perf operations to winapp ui.
benchmarks/​ui/​methods/​winapp.json Configures the winapp agent method.
benchmarks/​ui/​methods/​none.json Configures the no-plugin control.
benchmarks/​ui/​scenarios/​calc-running/​scenario.json Adds the Calculator baseline scenario.
benchmarks/​ui/​scenarios/​calc-not-running/​scenario.json Adds the Calculator launch scenario.
benchmarks/​ui/​scenarios/​calc-two-instances/​scenario.json Adds multi-window targeting coverage.
benchmarks/​ui/​scenarios/​calc-minimized-scientific/​scenario.json Adds minimized-window coverage.
benchmarks/​ui/​scenarios/​gallery-combobox/​scenario.json Adds Gallery navigation coverage.
benchmarks/​ui/​scenarios/​gallery-dialog-left-open/​scenario.json Adds modal-dialog coverage.
benchmarks/​ui/​scenarios/​gallery-cold-start/​scenario.json Adds Gallery cold-start coverage.
benchmarks/​ui/​tests/​UiBenchmark.Tests.ps1 Tests schemas, scoring, resume, and reports.
benchmarks/​ui/​tests/​Perf.Tests.ps1 Tests perf configuration and statistics.
benchmarks/​ui/​tests/​AppState.Tests.ps1 Tests mode restoration and ownership records.
benchmarks/​ui/​tests/​fixtures/​scoring-cases.json Supplies recorded scoring cases.
benchmarks/​agents/​lib/​Benchmark.psm1 Adds tool/API/session timing metrics.
benchmarks/​agents/​tests/​Benchmark.Tests.ps1 Tests the new timing calculations.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +303 to +309
$totals = @($Runs | ForEach-Object { Get-Field $_ 'commandMs' } | Where-Object { $null -ne $_ } | ForEach-Object { [double]$_ })
[pscustomobject]@{
Runs = $Runs.Count
Passed = @($Runs | Where-Object { $_.status -eq 'pass' }).Count
Total = Get-PerfStats $totals
Calls = Get-Median @($Runs | ForEach-Object { @(Get-Field $_ 'steps').Count } | ForEach-Object { [double]$_ })
}
Comment on lines +288 to +289
# Resume reruns cells when the method's definition or the skills it installs change.
Hash = Get-ShortHash (($raw -replace '\s', '') + "`n" + (@($expected | Sort-Object) -join ','))
Comment thread benchmarks/ui/perf.ps1
Comment on lines +95 to +99
Write-Host "Adapters: $(@($adapters | ForEach-Object { if ($_.CheckedIn) { $_.Name } else { "$($_.Name) ($($_.SourcePath))" } }) -join ', ')"
Write-Host "Operations: $(@($operations.Id) -join ', ')"
Write-Host " $($perf.Warmup) warm-up + $($perf.Repeat) timed calls each = $($runPlan.OperationCalls) commands"
Write-Host "Sequences: $(@($sequences.Id) -join ', ')"
Write-Host " $($perf.SequenceRepeat) run(s) each = $($runPlan.SequenceRuns) runs, $($runPlan.SequenceCalls) commands"
Comment thread benchmarks/ui/perf.ps1
Comment on lines +168 to +169
$tail = (Get-FileTail $err 300).Trim()
if (-not $tail) { $tail = (Get-FileTail $out 300).Trim() }
Comment thread benchmarks/ui/perf.ps1
Comment on lines +348 to +349
$failedSequences = @($results.sequences | Where-Object { $_.status -ne 'pass' })
exit ([int]([bool]$abort -or $failedSequences.Count -gt 0))
Comment thread benchmarks/ui/run.ps1
Comment on lines +535 to +537
# Without an explicit exit, the script would return whatever native command ran last.
$notPassed = @($finished | Where-Object { $_.status -ne 'pass' })
exit ([int]($header.Contains('Stopped early') -or $notPassed.Count -gt 0))
…n-agent-benchmark

# Conflicts:
#	benchmarks/agents/lib/Benchmark.psm1
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Build Metrics Report

Validation passed. All required build and validation jobs succeeded.

Binary Sizes

Artifact Baseline Current Delta
CLI (ARM64) 57.77 MB 57.77 MB ✅ 0.0 KB (0.00%)
CLI (x64) 57.79 MB 57.79 MB ✅ 0.0 KB (0.00%)
MSIX (ARM64) 24.02 MB 24.02 MB 📉 -0.1 KB (-0.00%)
MSIX (x64) 25.49 MB 25.49 MB 📈 +0.3 KB (+0.00%)
NPM Package 50.08 MB 50.08 MB 📉 -0.8 KB (-0.00%)
NuGet Package 50.19 MB 50.19 MB 📉 -0.4 KB (-0.00%)

.NET Test Results (TRX reports)

Other suites are reflected in the overall validation status above.

✅ 8331 passed, 37 skipped out of 8368 tests in 1106.2s (-65.8s vs. baseline)

Test Coverage

✅ 87.5% line coverage, 82.2% branch coverage · ✅ no change vs. baseline

CLI Startup Time

62ms median (x64, winapp --version) · ✅ no change vs. baseline

Try This Build

Installs the MSIX for your architecture, replacing any previously installed build. Needs the GitHub CLI — the command offers to install it and sign you in if it is missing.

& ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) 1053
Switching between builds often?

Put the tool on your PATH once:

& ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) -AddToPath

Then this build is just:

winapp-pr 1053

Run winapp-pr with no arguments to pick from a list of open PRs.


Updated 2026-10-10 19:48:05 UTC · commit 35c22a9 · workflow run

@nmetulev Nikola Metulev (nmetulev) added ready-for-review Agent work and technical checks complete; awaiting review or re-review, not approval or merge and removed agent-preparing Agent is addressing feedback or completing required validation and CI labels Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review Agent work and technical checks complete; awaiting review or re-review, not approval or merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants