// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package atspi import ( "context" "os" "testing" "time" ) func TestNativeEchoEditFieldFocus(t *testing.T) { if os.Getenv("CTHULHU_SPEECH_METADATA") != "1" { t.Skip("requires private GTK speech fixture") } ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second) defer cancel() adapter := New() if err := adapter.Start(ctx); err != nil { t.Fatal(err) } defer func() { stop, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() if err := adapter.Stop(stop); err != nil { t.Error(err) } }() tree, err := adapter.CaptureDesktop(ctx) if err != nil { t.Fatal(err) } for _, probe := range []struct { key, name string want bool }{ {"F9", "Mnemonic probe", false}, {"F10", "Indentation probe", true}, {"F9", "Mnemonic probe", false}, {"F11", "Color attribute probe", true}, {"F7", "Protected text probe", false}, {"shift+F10", "Indentation probe", false}, {"F10", "Indentation probe", true}, } { reference := findNamedReference(t, tree, probe.name) snapshot, err := tree.Resolve(reference) if err != nil { t.Fatal(err) } pressTreeProbeKey(t, probe.key) deadline := time.NewTimer(time.Second) tick := time.NewTicker(5 * time.Millisecond) matched := false for !matched { select { case <-ctx.Done(): t.Fatal(ctx.Err()) case <-deadline.C: t.Fatalf("%s: cached editable=%v want=%v", probe.key, adapter.EditFieldFocused(), probe.want) case <-adapter.Events(): case <-tick.C: } focus := adapter.echoFocus.Load() // A false result must describe this control, not a focus-event gap. matched = focus != nil && focus.object == snapshot.ID() && adapter.EditFieldFocused() == probe.want } deadline.Stop() tick.Stop() } }