Merge hunter terminal and keyboard improvements into testing
Preserve meaningful title status symbols during spinner suppression and keep testing version metadata.
This commit is contained in:
commit
4f9b89e38b
18 files changed
+1101
-16
No files matched your search
@@ -10,6 +10,7 @@ import (
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.stormux.org/storm/cthulhu/internal/atspi"
|
||||
"git.stormux.org/storm/cthulhu/internal/config"
|
||||
@@ -76,8 +77,96 @@ func TestAutomaticTitlePolicyCoalescesAndPreservesSpeech(t *testing.T) {
|
||||
changed.Source.Name = "Loading 100%"
|
||||
titles.accessible(changed)
|
||||
titles.flush()
|
||||
// A title restored within the repeat window is a repeat, not news;
|
||||
// animated cycles pass through a blank frame every cycle and must
|
||||
// not revive announcements. It announces again after the window.
|
||||
if len(queue.requests) != 1 {
|
||||
t.Fatal("restored title repeated within the repeat window")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutomaticTitleSuppressesAnimatedSpinnerCycle(t *testing.T) {
|
||||
titles, queue, changed := titleControllerFixture(t)
|
||||
// Terminal TUIs animate a spinner glyph in the title several times a
|
||||
// second while the meaningful title text stays the same. The cycle
|
||||
// includes a blank frame; it must not reset suppression.
|
||||
for _, text := range []string{"⠋ Terminal", "⠙ Terminal", "⠹ Terminal", "", "⠋ Terminal", "Terminal", "", "⠙ Terminal"} {
|
||||
changed.Source.Name = text
|
||||
titles.accessible(changed)
|
||||
titles.flush()
|
||||
}
|
||||
if len(queue.requests) != 1 || queue.requests[0].Text != "⠋ Terminal" {
|
||||
t.Fatalf("spinner title requests = %+v, want only the first frame", queue.requests)
|
||||
}
|
||||
// A different title is not part of the animation and still announces.
|
||||
changed.Source.Name = "Build finished"
|
||||
titles.accessible(changed)
|
||||
titles.flush()
|
||||
if len(queue.requests) != 2 || queue.requests[1].Text != "Build finished" {
|
||||
t.Fatalf("title after spinner = %+v", queue.requests)
|
||||
}
|
||||
// A leading count is meaningful: each value announces, a rapid repeat
|
||||
// of an earlier value in the same cycle does not.
|
||||
for _, text := range []string{"(2) Terminal", "(3) Terminal", "(2) Terminal"} {
|
||||
changed.Source.Name = text
|
||||
titles.accessible(changed)
|
||||
titles.flush()
|
||||
}
|
||||
if len(queue.requests) != 4 {
|
||||
t.Fatalf("counted title requests = %+v, want the cycle repeat suppressed", queue.requests)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindowReactivationDoesNotReviveSpinnerCycle(t *testing.T) {
|
||||
titles, queue, changed := titleControllerFixture(t)
|
||||
for _, text := range []string{"9 Terminal", "8 Terminal"} {
|
||||
changed.Source.Name = text
|
||||
titles.accessible(changed)
|
||||
titles.flush()
|
||||
}
|
||||
if len(queue.requests) != 2 {
|
||||
t.Fatal("restored title was not announced")
|
||||
t.Fatalf("initial spinner titles = %+v", queue.requests)
|
||||
}
|
||||
// Leaving for another workspace deactivates (reset) and returning
|
||||
// reactivates the same window. Neither may restart the cycle.
|
||||
titles.reset()
|
||||
titles.activated(changed)
|
||||
for _, text := range []string{"9 Terminal", "8 Terminal"} {
|
||||
changed.Source.Name = text
|
||||
titles.accessible(changed)
|
||||
titles.flush()
|
||||
}
|
||||
if len(queue.requests) != 2 {
|
||||
t.Fatalf("spinner revived by workspace round-trip = %+v", queue.requests)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNativeTitleSwitchSamplesDoNotReviveSpinnerCycle(t *testing.T) {
|
||||
titles, queue, changed := titleControllerFixture(t)
|
||||
source := &titleProbeSource{title: atspi.ForegroundTitle{Title: xorg.WindowTitle{Window: 10, TitleWindow: 10, Title: "Original"}}}
|
||||
titles.source, titles.foreground = source, source
|
||||
titles.nativeChanged()
|
||||
titles.flush()
|
||||
changed.Source.Name = "9 Terminal"
|
||||
titles.accessible(changed)
|
||||
titles.flush()
|
||||
changed.Source.Name = "8 Terminal"
|
||||
titles.accessible(changed)
|
||||
titles.flush()
|
||||
if len(queue.requests) != 2 {
|
||||
t.Fatalf("initial spinner titles = %+v", queue.requests)
|
||||
}
|
||||
// A native sampler may re-report the same client as a switch on every
|
||||
// sample. That must not wipe the animated-title repeat history, so the
|
||||
// cycle returning to an earlier frame stays suppressed.
|
||||
for _, text := range []string{"9 Terminal", "8 Terminal"} {
|
||||
source.title.Epoch++
|
||||
source.title.Title.Title = text
|
||||
titles.nativeChanged()
|
||||
titles.flush()
|
||||
}
|
||||
if len(queue.requests) != 2 {
|
||||
t.Fatalf("spinner revived by native switch samples = %+v", queue.requests)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,3 +747,60 @@ func TestWineReviewTitlesUseNativeApplicationPreferences(t *testing.T) {
|
||||
t.Fatal("Wine title ignored its application preference")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTitleRepeatHistoryIsScopedToWindow(t *testing.T) {
|
||||
titles, queue, changed := titleControllerFixture(t)
|
||||
changed.Source.Name = "Build finished"
|
||||
titles.accessible(changed)
|
||||
titles.flush()
|
||||
other := ownerEvent(atspi.WindowActivatedEvent, ":1.51", "/window", "/window")
|
||||
other.Source.Name, other.Source.Role = "Original", "frame"
|
||||
titles.state.owner.activate(other)
|
||||
titles.activated(other)
|
||||
other.Type, other.Source.Name = atspi.AccessibleNameEvent, "Build finished"
|
||||
titles.accessible(other)
|
||||
titles.flush()
|
||||
if len(queue.requests) != 2 {
|
||||
t.Fatalf("another window's title was suppressed: %+v", queue.requests)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTitleStatusSymbolsRemainMeaningful(t *testing.T) {
|
||||
titles, queue, changed := titleControllerFixture(t)
|
||||
for _, text := range []string{"✓ Build", "✗ Build", "! Build", "? Build"} {
|
||||
changed.Source.Name = text
|
||||
titles.accessible(changed)
|
||||
titles.flush()
|
||||
if len(queue.requests) == 0 || queue.requests[len(queue.requests)-1].Text != text {
|
||||
t.Fatalf("meaningful title status %q was suppressed: %+v", text, queue.requests)
|
||||
}
|
||||
}
|
||||
if len(queue.requests) != 4 {
|
||||
t.Fatalf("status titles = %+v", queue.requests)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTitleRepeatHistoryRefreshesAndExpires(t *testing.T) {
|
||||
titles, _, changed := titleControllerFixture(t)
|
||||
pending := pendingWindowTitle{window: changed.Source.ID(), text: "⠋ Terminal"}
|
||||
start := time.Now()
|
||||
if titles.repeatedWithinWindow(pending, start) {
|
||||
t.Fatal("first title suppressed")
|
||||
}
|
||||
pending.text = "⠙ Terminal"
|
||||
if !titles.repeatedWithinWindow(pending, start.Add(2*time.Second)) ||
|
||||
!titles.repeatedWithinWindow(pending, start.Add(4*time.Second)) {
|
||||
t.Fatal("active animation history expired")
|
||||
}
|
||||
if titles.repeatedWithinWindow(pending, start.Add(8*time.Second)) {
|
||||
t.Fatal("inactive animation history did not expire")
|
||||
}
|
||||
pending.native = xorg.WindowTitle{Window: 10, TitleWindow: 11}
|
||||
if titles.repeatedWithinWindow(pending, start.Add(8*time.Second)) {
|
||||
t.Fatal("native window inherited AT-SPI-only history")
|
||||
}
|
||||
pending.native.TitleWindow = 12
|
||||
if titles.repeatedWithinWindow(pending, start.Add(8*time.Second)) {
|
||||
t.Fatal("another native child inherited title history")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user