Files
cthulhu/internal/app/window_owner_test.go

107 lines
4.2 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"testing"
"git.stormux.org/storm/cthulhu/internal/atspi"
)
func ownerEvent(eventType, bus, window, focus string) atspi.Event {
event := atspi.NewEvent(eventType, atspi.Snapshot{BusName: bus, Path: focus})
event.Window = atspi.NewObjectID(bus, window)
return event
}
func TestWindowOwnerKeepsWindowAndFocusIndependent(t *testing.T) {
var owner windowOwner
activation := ownerEvent(atspi.WindowActivatedEvent, ":1.40", "/window", "/button")
if owner.activate(activation) {
t.Fatal("first activation was treated as duplicate")
}
if owner.window != activation.Window || owner.focus != activation.Source.ID() || owner.applicationBus != ":1.40" {
t.Fatalf("owner = %#v", owner)
}
focus := ownerEvent(atspi.FocusedStateEvent, ":1.40", "/window", "/entry")
if !owner.follow(focus) || owner.window != activation.Window || owner.focus != focus.Source.ID() {
t.Fatalf("focus did not remain in active window: %#v", owner)
}
if !owner.activate(activation) || owner.focus != focus.Source.ID() {
t.Fatal("duplicate activation replaced descendant focus")
}
}
func TestFocusBeforeActivationStillAdmitsFirstWindowActivation(t *testing.T) {
var owner windowOwner
focus := ownerEvent(atspi.FocusedStateEvent, ":1.40", "/window", "/entry")
focus.Source.Focused = true
if !owner.follow(focus) || owner.activated {
t.Fatalf("focus-first ownership = %#v", owner)
}
activation := ownerEvent(atspi.WindowActivatedEvent, ":1.40", "/window", "/window")
if owner.activate(activation) || owner.focus != focus.Source.ID() {
t.Fatalf("first activation lost focus or was skipped: %#v", owner)
}
if !owner.activate(activation) {
t.Fatal("repeated activation was not suppressed")
}
}
func TestWindowOwnerRejectsBackgroundFocusAndUnrelatedLifecycle(t *testing.T) {
var owner windowOwner
owner.activate(ownerEvent(atspi.WindowActivatedEvent, ":1.40", "/window", "/button"))
if owner.follow(ownerEvent(atspi.FocusedStateEvent, ":1.41", "/other", "/entry")) {
t.Fatal("background focus changed ownership")
}
if owner.end(ownerEvent(atspi.WindowDestroyedEvent, ":1.41", "/other", "/other"), false) {
t.Fatal("unrelated destruction cleared ownership")
}
if !owner.window.Valid() {
t.Fatal("unrelated lifecycle cleared active window")
}
}
func TestWindowOwnerHandlesTransientDeactivationAndDestruction(t *testing.T) {
var owner windowOwner
activation := ownerEvent(atspi.WindowActivatedEvent, ":1.40", "/window", "/button")
owner.activate(activation)
deactivation := ownerEvent(atspi.WindowDeactivatedEvent, ":1.40", "/window", "/window")
deactivation.Source.StatesKnown = true
deactivation.Source.Active = true
if owner.end(deactivation, false) || !owner.window.Valid() {
t.Fatal("active key-grab deactivation cleared ownership")
}
deactivation.Source.StatesKnown = false
deactivation.Source.Active = false
if owner.end(deactivation, true) || !owner.window.Valid() {
t.Fatal("consumed-command deactivation cleared ownership")
}
destruction := ownerEvent(atspi.WindowDestroyedEvent, ":1.40", "/window", "/window")
if !owner.end(destruction, true) || owner.window.Valid() || owner.focus.Valid() || owner.applicationBus != "" {
t.Fatalf("destruction did not clear ownership: %#v", owner)
}
}
func TestWindowOwnerClearsDefunctFocusWithoutDroppingWindow(t *testing.T) {
var owner windowOwner
activation := ownerEvent(atspi.WindowActivatedEvent, ":1.40", "/window", "/button")
owner.activate(activation)
defunct := atspi.NewEvent(atspi.DefunctStateEvent, activation.Source)
defunct.Detail1 = 1
windowEnded, focusEnded := owner.defunct(defunct)
if windowEnded || !focusEnded || !owner.window.Valid() || owner.focus.Valid() {
t.Fatalf("defunct focus result = windowEnded:%v focusEnded:%v owner:%#v", windowEnded, focusEnded, owner)
}
windowDefunct := atspi.NewEvent(atspi.DefunctStateEvent, atspi.Snapshot{BusName: ":1.40", Path: "/window"})
windowDefunct.Detail1 = 1
windowEnded, focusEnded = owner.defunct(windowDefunct)
if !windowEnded || !focusEnded || owner.window.Valid() || owner.applicationBus != "" {
t.Fatalf("defunct window result = windowEnded:%v focusEnded:%v owner:%#v", windowEnded, focusEnded, owner)
}
}