Skip to content
Merged
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
10 changes: 8 additions & 2 deletions Tools/windows/Tests/UiCaptureDriver.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3344,7 +3344,7 @@ function Invoke-CapMatchedTabContracts {
'self.appendAccessibilityElement(&elements, &owned_identities, "workspace-tab-close", close_key, "Close tab", 4, .{ .physical = close_bounds }, false, workspace.canCloseTab())') -and
$appSource.Contains('const tab_bounds = workspace.visibleTabBounds(tab_index) orelse continue;') -and
$appSource.Contains('if (workspace.tabCloseBounds(tab_index) == null or !workspace.canCloseTab()) return false;') -and
$terminalSource.Contains('bounds.right - Dpi.scale(24, self.dpi)') -and
$terminalSource.Contains('bounds.right - Dpi.scale(Tokens.tab_control_width, self.dpi)') -and
$terminalSource.Contains('const first = if (selected >= count) selected - count + 1 else 0;') -and
$terminalSource -cmatch '(?s)pub fn canCloseTab\(self: \*const Workspace\) bool \{\s*return self\.layout\.tabs\.items\.len > 1;\s*\}' -and
$accessibilitySource.Contains('sidebar_scroll: bool = false,') -and
Expand Down Expand Up @@ -3390,7 +3390,11 @@ function Invoke-CapMatchedTabContracts {
ExpectedDpi=192;DpiBase=96}
if (-not $isFailedManyFirst) {$arguments.VisibleIndexes=$shape.visible}
$proof=Get-CapMatchedTabObservation @arguments
Assert-CapTest ($proof.visible.Count -eq $shape.visible.Count -and $proof.selected -eq $shape.selected) 'Tab shape omitted a visible state.'
Assert-CapTest ($proof.visible.Count -eq $shape.visible.Count -and $proof.selected -eq $shape.selected -and
@($proof.visible | Where-Object {$_.physicalWidth -ne 106 -or $_.physicalHeight -ne 24 -or
$_.logicalWidth -ne 53 -or $_.logicalHeight -ne 12}).Count -eq 0 -and
$proof.geometryContract -ceq 'OWNED_UIA_PHYSICAL_BOUNDS_NORMALIZED_BY_DECLARED_DPI') `
'Tab shape omitted a visible state or exact physical/logical geometry.'
if ($isFailedSeed) {
Assert-CapTest ($proof.visible[0].closeEnabled -and $proof.visible[0].lastTabGuard -ceq
'SOURCE_BOUND_WORKSPACE_CAN_CLOSE_TAB_GT_ONE') 'Exact failed C8R seed did not preserve its source-bound last-tab guard.'
Expand All @@ -3399,6 +3403,8 @@ function Invoke-CapMatchedTabContracts {
selected=$proof.visible[0].selected;closeEnabled=$proof.visible[0].closeEnabled
lastTabGuard=$proof.visible[0].lastTabGuard;dpi=192;dpiBase=96
bounds=$proof.visible[0].bounds;closeBounds=$proof.visible[0].closeBounds
physicalWidth=$proof.visible[0].physicalWidth;physicalHeight=$proof.visible[0].physicalHeight
logicalWidth=$proof.visible[0].logicalWidth;logicalHeight=$proof.visible[0].logicalHeight
closeBoundsContract=$proof.closeBoundsContract}
}
if ($isFailedManyFirst) {
Expand Down
68 changes: 66 additions & 2 deletions Tools/windows/Tests/ValidationRunner.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ function Test-MultiProjectProtocolContracts([string] $stubSource, [string] $gate
"Test-RetryableUiaError",
"Test-UpdateBannerClickTarget",
"Get-DynamicProjectCardSnapshotDecision",
"Get-ConnectorDragSnapshotDecision"
"Get-ConnectorDragSnapshotDecision",
"Get-WorkspaceTabMountDecision",
"Get-UiaSelectionEventDecision"
))
}, $true)) { . ([scriptblock]::Create($definition.Extent.Text)) }
}
Expand Down Expand Up @@ -42,6 +44,67 @@ function Test-MultiProjectProtocolContracts([string] $stubSource, [string] $gate
}
throw "RED: multi-project expected actual $prefix rejection"
}
$tab = {
param([string] $id, [string] $name, [bool] $selected)
return [ordered]@{ automationId = $id; name = $name; selected = $selected }
}
$initialTab = & $tab "workspace-tab-initial" "Goal" $true
$newTab = & $tab "workspace-tab-new" "Shell" $true
$initialUnselected = & $tab "workspace-tab-initial" "Goal" $false
$newUnselected = & $tab "workspace-tab-new" "Shell" $false
Invoke-MultiCase "DPI192 narrow New Tab overflow still proves both mounted stable tabs" {
$decision = Get-WorkspaceTabMountDecision `
@($initialTab) @($newTab) @($initialUnselected, $newTab) `
@($initialTab, $newUnselected) @($initialUnselected, $newTab)
Assert-MultiCase ($decision.ready -and $decision.overflowObserved -and
$decision.initialId -ceq "workspace-tab-initial" -and
$decision.newId -ceq "workspace-tab-new") "narrow mounted-tab round trip was rejected"
return "One visible Shell at expanded DPI192 width, then stable Goal/Shell identities after bounded panel collapse and both selection round trips"
}
Invoke-MultiCase "wide New Tab roster proves the same mounted stable tabs without overflow" {
$decision = Get-WorkspaceTabMountDecision `
@($initialTab) @($initialUnselected, $newTab) @($initialUnselected, $newTab) `
@($initialTab, $newUnselected) @($initialUnselected, $newTab)
Assert-MultiCase ($decision.ready -and -not $decision.overflowObserved) `
"wide mounted-tab round trip was rejected"
return "Two visible tabs before collapse retain the same stable identities and selected roles"
}
foreach ($case in @(
@{ name = "same-id"; after = @(& $tab "workspace-tab-initial" "Shell" $true); mounted = @($initialUnselected, $newTab); first = @($initialTab, $newUnselected); second = @($initialUnselected, $newTab); failure = "new-tab" },
@{ name = "missing-mounted-initial"; after = @($newTab); mounted = @($newTab); first = @($initialTab, $newUnselected); second = @($initialUnselected, $newTab); failure = "mounted-pair" },
@{ name = "renamed-mounted-initial"; after = @($newTab); mounted = @((& $tab "workspace-tab-initial" "Agent" $false), $newTab); first = @($initialTab, $newUnselected); second = @($initialUnselected, $newTab); failure = "mounted-pair" },
@{ name = "initial-switch-did-not-select"; after = @($newTab); mounted = @($initialUnselected, $newTab); first = @($initialUnselected, $newTab); second = @($initialUnselected, $newTab); failure = "initial-switch" },
@{ name = "new-switch-changed-id"; after = @($newTab); mounted = @($initialUnselected, $newTab); first = @($initialTab, $newUnselected); second = @($initialUnselected, (& $tab "workspace-tab-replaced" "Shell" $true)); failure = "new-switch" },
@{ name = "duplicate-mounted-selection"; after = @($newTab); mounted = @($initialTab, $newTab); first = @($initialTab, $newUnselected); second = @($initialUnselected, $newTab); failure = "mounted-pair" }
)) {
Invoke-MultiCase "mounted tab refuses permanent $($case.name)" -Negative {
$decision = Get-WorkspaceTabMountDecision `
@($initialTab) @($case.after) @($case.mounted) @($case.first) @($case.second)
Assert-MultiCase (-not $decision.ready -and $decision.failure -ceq $case.failure) `
"permanent $($case.name) returned '$($decision.failure)'"
return $decision.failure
}
}
Invoke-MultiCase "selection event accepts exactly one stable owned source" {
$decision = Get-UiaSelectionEventDecision 1 "worktree-row-safe" "42,7,9" `
"worktree-row-safe" "42,7,9"
Assert-MultiCase $decision.ready "exact stable selection event was rejected"
return "One event, exact automation id, exact runtime id"
}
foreach ($case in @(
@{name="zero";count=0;id="worktree-row-safe";runtime="42,7,9";failure="zero-events"},
@{name="duplicate";count=2;id="worktree-row-safe";runtime="42,7,9";failure="duplicate-events"},
@{name="wrong-element";count=1;id="worktree-row-foreign";runtime="42,7,9";failure="wrong-automation-id"},
@{name="wrong-generation";count=1;id="worktree-row-safe";runtime="42,7,10";failure="wrong-runtime-id"}
)) {
Invoke-MultiCase "selection event refuses $($case.name)" -Negative {
$decision = Get-UiaSelectionEventDecision $case.count $case.id $case.runtime `
"worktree-row-safe" "42,7,9"
Assert-MultiCase (-not $decision.ready -and $decision.failure -ceq $case.failure) `
"selection $($case.name) returned '$($decision.failure)'"
return $decision.failure
}
}
Invoke-MultiCase "compact overview narrow client retains the Mac lane and requires real pan" {
$geometry = Get-MultiProjectLaneGeometry @(220,34,1028,900) @(382,134,632,230)
Assert-MultiCase (($geometry.band -join ",") -ceq "300,96,1822,250" -and
Expand Down Expand Up @@ -4646,7 +4709,8 @@ Start-Sleep -Seconds 60
$uiaLiveGateSource -notmatch 'Write-UiaWaitAttribution "sketch/custody modal: \$title"' -or
$uiaLiveGateSource -notmatch 'Write-UiaWaitAttribution "multi-project observation: \$surface"' -or
$uiaLiveGateSource -notmatch 'Write-UiaWaitAttribution "gate failure: \$failureStep"' -or
$uiaLiveGateSource -notmatch 'Write-UiaLateEventAttribution "ElementSelected after Select"') {
$uiaLiveGateSource -notmatch 'Write-UiaLateEventAttribution "ElementSelected after Select"' -or
$uiaLiveGateSource -notmatch '(?s)RemoveFromSelection\(\).*?GetSelection\(\)\.Count -eq 0.*?RawView Worktrees event subscription.*?SelectionSourceRuntimeId = \$null.*?\$selectionEventRow = \$safeRow.*?AddAutomationEventHandler\(.*?\$selectionEventDeadlineUtc.*?Get-UiaSelectionEventDecision \$selectedOnTime.*?\$safeRowId \$safeRowRuntimeId.*?Require \$selectedDecision\.ready.*?\$selectedAfterStableDrain.*?RemoveAutomationEventHandler\(\$selectedEvent, \$selectionEventRow') {
throw "RED: UIA wait timeouts are not attributed to step, deadline, foreground owner, shell windows, and a shell-only capture"
}
if ($stubDaemonSource -notmatch '\$frame\.command\.graphCommand\.command\.createNode\._0' -or
Expand Down
4 changes: 2 additions & 2 deletions Tools/windows/Tests/WindowsShell.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ function Assert-WorkspaceTabSelectors {
$node.Left.Extent.Text -match '\.Current\.AutomationId$' -and
$node.Right.Extent.Text -ceq "'^workspace-tab-[0-9]+$'"
}, $true))
Assert-Contract ($predicates.Count -eq 7) `
"expected seven actual-tab selectors in the UIA live gate, found $($predicates.Count)"
Assert-Contract ($predicates.Count -eq 10) `
"expected ten actual-tab selectors in the UIA live gate, found $($predicates.Count)"
$graphWait = $ast.Find({
param($node)
$node -is [Management.Automation.Language.FunctionDefinitionAst] -and
Expand Down
6 changes: 6 additions & 0 deletions Tools/windows/UI-CAPTURE-SCENARIOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,12 @@ observed UI state, not a `selected` property invented on the daemon's node.
| `s-tabs-last-guard` | One selected tab; the exact visible close row is retained, while the phase's source-bound `Workspace.canCloseTab()` contract proves that one tab cannot close. Dynamic UIA rows report enabled independently of that product guard, and capture does not attempt the close. |
| `s-restart-confirmed` | Existing Restart session UIA route against an actual isolated daemon, with same-connection protocol and new owned-attach evidence described below. |

Every matched tab observation records each visible tab's owned UIA bounds in
physical pixels and the corresponding logical width and height normalized by
the job's declared DPI. This preserves each baseline phase's observed geometry
while allowing a corrected DPI192 phase to report its native
50-physical/25-logical pill without resampling either image.

The adapter requests eighteen scenes for each before/after phase: **36 original
images**, **22 cmd-only seed lifecycles**, and **two real-daemon Restart scenes**.
The live transition rehearsal executes the same order and transitions but emits
Expand Down
6 changes: 6 additions & 0 deletions Tools/windows/UiCaptureDriver.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -3721,8 +3721,13 @@ function Get-CapMatchedTabObservation {
$close[1] -ne $tab[1] -or $close[2] -ne $tab[2] -or $close[3] -ne $tab[3]) {
throw 'Matched tabs close bounds escape their exact tab.'
}
$physicalWidth=[long]$tab[2]-[long]$tab[0]
$physicalHeight=[long]$tab[3]-[long]$tab[1]
$observed.Add([pscustomobject]@{index=$index;automationId=$id;name=$title;selected=$tabs[0].Selected
bounds=$tab;closeBounds=$close;closeEnabled=$closes[0].IsEnabled
physicalWidth=$physicalWidth;physicalHeight=$physicalHeight
logicalWidth=[Math]::Round($physicalWidth*$DpiBase/$ExpectedDpi,3)
logicalHeight=[Math]::Round($physicalHeight*$DpiBase/$ExpectedDpi,3)
lastTabGuard=if ($TotalTabs -eq 1) {'SOURCE_BOUND_WORKSPACE_CAN_CLOSE_TAB_GT_ONE'} else {'MULTI_TAB_CLOSE_AVAILABLE'}
shortcut=if ($index -lt 9) {"Ctrl+$($index+1)"} else {$null}})
}
Expand All @@ -3738,6 +3743,7 @@ function Get-CapMatchedTabObservation {
sourceCommit=$SourceCommit;identityContract='VISIBLE_LAYOUT_INDEX_FNV1A64_ROW_KEY'
visibilityContract=$(if ($visibilityDerived) {'DERIVED_CONTIGUOUS_OWNED_VISIBLE_STABLE_IDS'} else {'DECLARED_VISIBLE_STABLE_IDS'})
closeBoundsContract="SOURCE_DPI_SCALED_24_LOGICAL_TO_${expectedCloseWidth}_PHYSICAL"
geometryContract='OWNED_UIA_PHYSICAL_BOUNDS_NORMALIZED_BY_DECLARED_DPI'
closeGuardContract='WORKSPACE_CAN_CLOSE_TAB_GT_ONE_NOT_UIA_ISENABLED'
evidenceMode='OWNED_UIA_STATE_NOT_GLYPHS_OR_VISUAL_PARITY';hover='VISUAL_ONLY_NOT_EXPOSED_BY_UIA'}
}
Expand Down
Loading
Loading