101 lines
3.0 KiB
Go
101 lines
3.0 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package atspi
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"os/exec"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestNativeForegroundTitlePublication(t *testing.T) {
|
|
if os.Getenv("CTHULHU_NATIVE_WINDOW_TITLES") != "1" {
|
|
t.Skip("private Xvfb and D-Bus fixture only")
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
fixture := exec.CommandContext(ctx, "python3", "-u", "../../tests/window_title_fixture.py")
|
|
stdin, err := fixture.StdinPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
stdout, err := fixture.StdoutPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fixture.Stderr = os.Stderr
|
|
if err = fixture.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { stdin.Close(); fixture.Wait() }()
|
|
scanner := bufio.NewScanner(stdout)
|
|
if !scanner.Scan() {
|
|
t.Fatal("fixture did not start")
|
|
}
|
|
var windows map[string]uint64
|
|
if err = json.Unmarshal(scanner.Bytes(), &windows); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
adapter := New()
|
|
if err = adapter.Start(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer adapter.Stop(context.Background())
|
|
waitTitle := func(window, child uint64, text string) ForegroundTitle {
|
|
t.Helper()
|
|
for {
|
|
value := adapter.ForegroundTitle()
|
|
if value.Title.Window == window && value.Title.TitleWindow == child && value.Title.Title == text {
|
|
return value
|
|
}
|
|
select {
|
|
case <-adapter.TitleChanges():
|
|
case <-ctx.Done():
|
|
t.Fatalf("missing title %q: %+v", text, value)
|
|
}
|
|
}
|
|
}
|
|
command := func(action, window, title string) {
|
|
t.Helper()
|
|
data, _ := json.Marshal(map[string]string{"action": action, "window": window, "title": title})
|
|
if _, err = stdin.Write(append(data, '\n')); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !scanner.Scan() || scanner.Text() != "ready" {
|
|
t.Fatal("fixture command failed")
|
|
}
|
|
}
|
|
initial := waitTitle(windows["normal"], windows["normal"], "Example title")
|
|
command("title", "normal", "Changed without AT-SPI")
|
|
changed := waitTitle(windows["normal"], windows["normal"], "Changed without AT-SPI")
|
|
if changed.Epoch != initial.Epoch {
|
|
t.Fatal("title change reset foreground ownership")
|
|
}
|
|
command("activate", "desktop", "")
|
|
command("focus", "control", "")
|
|
wine := waitTitle(windows["desktop"], windows["game"], "Wine game")
|
|
if wine.Epoch == initial.Epoch || wine.Title.Application != "game.exe" {
|
|
t.Fatalf("Wine ownership = %+v", wine)
|
|
}
|
|
if !wine.Title.Wine || !wine.Title.Focused || !wine.Title.Client.Known ||
|
|
wine.Title.Client.Window != windows["game"] || wine.Title.Client.Width != 250 || wine.Title.Client.Height != 150 {
|
|
t.Fatalf("Wine child geometry/focus missing: %+v", wine)
|
|
}
|
|
command("focus", "second", "")
|
|
dialog := waitTitle(windows["desktop"], windows["second"], "Wine dialog")
|
|
if dialog.Epoch != wine.Epoch {
|
|
t.Fatal("Wine child change reset desktop ownership")
|
|
}
|
|
command("destroy", "desktop", "")
|
|
empty := waitTitle(0, 0, "")
|
|
if empty.Epoch == wine.Epoch {
|
|
t.Fatal("lost foreground kept its epoch")
|
|
}
|
|
}
|