// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package atspi import ( "bufio" "context" "fmt" "os" "os/exec" "strings" "testing" "time" ) func TestNativeAnnouncementEventDelivered(t *testing.T) { if os.Getenv("CTHULHU_NATIVE_ANNOUNCEMENT") != "1" { t.Skip("requires private Xorg/AT-SPI session") } ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second) defer cancel() adapter := New() if err := adapter.Start(ctx); err != nil { t.Fatal(err) } defer adapter.Stop(context.Background()) command := exec.CommandContext(ctx, "/usr/bin/python3", "../../tests/gtk_announce.py") command.Env = append(os.Environ(), "GDK_BACKEND=x11") input, err := command.StdinPipe() if err != nil { t.Fatal(err) } output, err := command.StdoutPipe() if err != nil { t.Fatal(err) } command.Stderr = os.Stderr if err := command.Start(); err != nil { t.Fatal(err) } t.Cleanup(func() { input.Close(); command.Process.Kill(); command.Wait() }) reader := bufio.NewReader(output) line, err := reader.ReadString('\n') if fields := strings.Fields(line); err != nil || len(fields) != 2 || fields[0] != "ready" { t.Fatalf("fixture readiness: %q, %v", line, err) } // Publish the complete AT-SPI cache before announcing. if _, err := adapter.CaptureDesktop(ctx); err != nil { t.Fatal(err) } wait := func(text string) Event { t.Helper() for { select { case event := <-adapter.Events(): if event.Type == AnnouncementEvent && event.Payload == text { return event } case <-ctx.Done(): t.Fatalf("announcement %q missing: %v; filtered=%d", text, ctx.Err(), adapter.backgroundEventsFiltered.Load()) } } } fmt.Fprintln(input, "announce polite Workspace 2 Fixture") polite := wait("Workspace 2 Fixture") if polite.Detail1 != 1 { t.Fatalf("polite detail1 = %d, want 1", polite.Detail1) } fmt.Fprintln(input, "announce assertive Fixture error") assertive := wait("Fixture error") if assertive.Detail1 != 2 { t.Fatalf("assertive detail1 = %d, want 2", assertive.Detail1) } }