106 lines
3.0 KiB
Go
106 lines
3.0 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
"git.stormux.org/storm/cthulhu/internal/platform/xorg"
|
|
)
|
|
|
|
func TestNativeSubmissionClearAndSelectedReplacement(t *testing.T) {
|
|
if os.Getenv("CTHULHU_NATIVE_SUBMISSION_ECHO") != "1" {
|
|
t.Skip("requires native_test_session.py private GTK session")
|
|
}
|
|
ctx, cancel := context.WithTimeout(t.Context(), 25*time.Second)
|
|
defer cancel()
|
|
adapter := atspi.New()
|
|
if err := adapter.Start(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer adapter.Stop(context.Background())
|
|
listener := xorg.New()
|
|
if err := listener.Start(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer listener.Stop(context.Background())
|
|
previousBus := ""
|
|
for _, mode := range []string{"submit", "replace"} {
|
|
t.Run(mode, func(t *testing.T) {
|
|
fixture := exec.CommandContext(ctx, "/usr/bin/python3", filepath.Join("..", "..", "tests", "gtk_submit_echo.py"), mode)
|
|
output, err := fixture.StdoutPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fixture.Stderr = os.Stderr
|
|
if err := fixture.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { fixture.Process.Kill(); fixture.Wait() }()
|
|
if line, err := bufio.NewReader(output).ReadString('\n'); err != nil || line != "READY\n" {
|
|
t.Fatalf("GTK fixture readiness: %q %v", line, err)
|
|
}
|
|
keys := func(args ...string) {
|
|
t.Helper()
|
|
if out, err := exec.CommandContext(ctx, "xdotool", args...).CombinedOutput(); err != nil {
|
|
t.Fatalf("xdotool: %v: %s", err, out)
|
|
}
|
|
}
|
|
keys("search", "--sync", "--onlyvisible", "--pid", strconv.Itoa(fixture.Process.Pid), "--name", "^Submission echo probe$", "windowfocus", "--sync")
|
|
var focus atspi.Event
|
|
for focus.Source.Name != "Message input" || focus.Source.BusName == previousBus || !isFocusGainEvent(focus) {
|
|
select {
|
|
case focus = <-adapter.Events():
|
|
case <-ctx.Done():
|
|
t.Fatal(ctx.Err())
|
|
}
|
|
}
|
|
previousBus = focus.Source.BusName
|
|
keys("key", "--clearmodifiers", "Return")
|
|
inputLoop:
|
|
for {
|
|
select {
|
|
case key := <-listener.Events():
|
|
if key.Pressed && key.Name == "Return" {
|
|
break inputLoop
|
|
}
|
|
case <-ctx.Done():
|
|
t.Fatal(ctx.Err())
|
|
}
|
|
}
|
|
for {
|
|
select {
|
|
case event := <-adapter.Events():
|
|
if event.Source.ID() != focus.Source.ID() || event.Type != atspi.TextDeletedEvent {
|
|
continue
|
|
}
|
|
if !listener.RecentInput().PlainEnter {
|
|
t.Fatal("native X11 Enter activity missing")
|
|
}
|
|
got := submitted_text_speech(t, listener, focus, event)
|
|
if mode == "submit" && len(got) != 1 {
|
|
t.Fatalf("native submission echoed deletion: %q; snapshot=%+v", got, event.Source)
|
|
}
|
|
if mode == "replace" && (len(got) != 2 || got[1] != "A sample message.") {
|
|
t.Fatalf("native selected replacement lost feedback: %q", got)
|
|
}
|
|
t.Logf("native %s: %q", mode, got)
|
|
return
|
|
case <-ctx.Done():
|
|
t.Fatal(ctx.Err())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|