diff --git a/build/Test-InstalledUpdates.ps1 b/build/Test-InstalledUpdates.ps1 index edf4e28..a1b2404 100644 --- a/build/Test-InstalledUpdates.ps1 +++ b/build/Test-InstalledUpdates.ps1 @@ -77,15 +77,17 @@ function Invoke-SmokeProcess $stderr = $process.StandardError.ReadToEndAsync() try { - if(-not $process.WaitForExit($TimeoutSeconds * 1000)) + $timedOut = -not $process.WaitForExit($TimeoutSeconds * 1000) + if($timedOut) { $process.Kill($true) - throw "Process timed out: $File" + if(-not $process.WaitForExit(10000)) { throw "Timed-out process did not terminate: $File" } } $script:smokeProcessNumber++ $log = Join-Path $outputRoot "process-$script:smokeProcessNumber.log" $text = $stdout.GetAwaiter().GetResult() + $stderr.GetAwaiter().GetResult() Set-Content -LiteralPath $log -Value $text + if($timedOut) { throw "Process timed out: $File. Log: $log" } if($process.ExitCode -ne 0) { throw "Process failed ($($process.ExitCode)): $File. Log: $log`n$text" } } finally { $process.Dispose() } @@ -183,7 +185,7 @@ try if($loose.Installed -or $loose.AppKitSupported -or $null -ne $loose.Version) { throw 'Loose publish output unexpectedly supports updates.' } $records.Add(@{ kind = 'loose'; result = $loose }) $env:K22_UITEST_EXE = Join-Path $buildRoot "publish/0.1.0/$mainExe" - Invoke-SmokeProcess -File 'dotnet' -Arguments @('test', "$repositoryRoot/tests/$name.UiTests", '--no-build', '--logger', 'trx;LogFileName=published-ui.trx', '--results-directory', $outputRoot) -TimeoutSeconds 180 + Invoke-SmokeProcess -File 'dotnet' -Arguments @('test', "$repositoryRoot/tests/$name.UiTests", '--no-build', '--logger', 'trx;LogFileName=published-ui.trx', '--results-directory', $outputRoot) -TimeoutSeconds 300 foreach($initial in @($versions[0], $versions[2])) { $portableDirectory = Join-Path $outputRoot "portable-$($initial.channel)" @@ -234,7 +236,7 @@ try $after = Get-SmokeResult -Exe $installedProbe -ResultPath (Join-Path $outputRoot "$channel-after.json") if($after.Version -ne $initial.target -or $after.Channel -ne $channel -or $null -ne $after.UpdateVersion -or $after.AppKitVersion -ne $initial.target) { throw 'Post-upgrade identity or channel is incorrect.' } $env:K22_UITEST_EXE = $currentExe - Invoke-SmokeProcess -File 'dotnet' -Arguments @('test', "$repositoryRoot/tests/$name.UiTests", '--no-build', '--filter', 'FullyQualifiedName!~CheckForUpdates_InALooseBuild', '--logger', "trx;LogFileName=installed-$channel-ui.trx", '--results-directory', $outputRoot) -TimeoutSeconds 180 + Invoke-SmokeProcess -File 'dotnet' -Arguments @('test', "$repositoryRoot/tests/$name.UiTests", '--no-build', '--filter', 'FullyQualifiedName!~CheckForUpdates_InALooseBuild', '--logger', "trx;LogFileName=installed-$channel-ui.trx", '--results-directory', $outputRoot) -TimeoutSeconds 300 $records.Add(@{ kind = 'installed'; channel = $channel; before = $before; after = $after; restarted = $true; appSha256 = $installedHash }) } finally diff --git a/docs/SESSION-STATE.md b/docs/SESSION-STATE.md index 41241ef..e36d248 100644 --- a/docs/SESSION-STATE.md +++ b/docs/SESSION-STATE.md @@ -11,6 +11,29 @@ Owner authorized signed/DCO commits, pushes, repository configuration and ticket No merges, tags/releases or new Azure attempts. Human review acknowledgement remains required. Local desktop reservation is unanswered; hosted runner UI tests are authorized. +## Source caret alignment probe — 2026-10-11 + +82898bd / CI 38117005710 isolated source job 114403773700 reproduced FAILURE +in 18 seconds. Original timeout is now retained: source focus before=5310.933, +last=5310.933; after physical pointer press, actual native offset resets to 0. +The provider cannot retrieve the offscreen last-heading glyph. This rejects the +hypothesis that GettingFocus captured an already-reset offset; deferred restoration +ran too early to prevent later native caret work. Original full assertions stay intact. + +The single-variable probe positions a zero-length caret at the first visible native +character before focus, using bounded binary search over real native character +rectangles. Active find/nonempty selections remain independent. The previous snapshot- +guarded viewport restoration remains. DEBUG-src-focus records the native caret index; +remove probes/temporary CI job after actual original acceptance proves the fix. +Full/native/installed producers on 82898bd remain observed until terminal before push. + +The enlarged 48-case suite's measured native failure duration is 178 seconds and +published acceptance exceeded 180 seconds before producing TRX. Full published/ +installed UI suites receive a bounded 300 seconds (all cases/assertions unchanged). +The process helper retains stdout/stderr after timed-out child termination before +throwing; a 10-second termination bound still fails closed. Other command timeouts +remain unchanged. A headless owned-child timeout probe checks retained evidence. + ## Tight native source diagnostics after editor acceptance f650e36 / CI 38116258625 native job 114401630861 finished FAILURE: diff --git a/src/MarkdownViewr.Windows/MainWindow.Source.Sync.cs b/src/MarkdownViewr.Windows/MainWindow.Source.Sync.cs index 9bb851c..576440a 100644 --- a/src/MarkdownViewr.Windows/MainWindow.Source.Sync.cs +++ b/src/MarkdownViewr.Windows/MainWindow.Source.Sync.cs @@ -54,9 +54,27 @@ public sealed partial class MainWindow return; } double position = scroller.VerticalOffset; + if(SourceText.SelectionLength == 0 && SourceText.Text.Length > 0) + { + int first = 0; + int last = SourceText.Text.Length - 1; + while(first < last) + { + int middle = first + (last - first) / 2; + if(SourceText.GetRectFromCharacterIndex(middle, trailingEdge: false).Y < position) + { + first = middle + 1; + } + else + { + last = middle; + } + } + SourceText.Select(first, 0); + } if(string.Equals(Environment.GetEnvironmentVariable("MARKDOWNVIEWR_INPUT_TRACE"), "1", StringComparison.Ordinal)) { - App.Log.Info(FormattableString.Invariant($"Input trace: source focus before={position:R}, last={_sourceLastY:R}.")); + App.Log.Info(FormattableString.Invariant($"[DEBUG-src-focus] source focus before={position:R}, last={_sourceLastY:R}, caret={SourceText.SelectionStart}.")); } Core.Documents.DocumentSourceText? snapshot = _sourceDocument; DispatcherQueue.TryEnqueue(() =>