99 lines
2.7 KiB
Go
99 lines
2.7 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package presentation
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
)
|
|
|
|
func TestNativeTextLabelRelations(t *testing.T) {
|
|
if os.Getenv("CTHULHU_NATIVE_TEXT_LABELS") != "1" {
|
|
t.Skip("requires private GTK/AT-SPI session")
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
adapter := atspi.New()
|
|
if err := adapter.Start(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer adapter.Stop(context.Background())
|
|
fixture := exec.CommandContext(ctx, "/usr/bin/python3", "-u", "../../tests/text_label_fixture.py")
|
|
fixture.Env = append(os.Environ(), "NO_AT_BRIDGE=0", "GTK_MODULES=gail:atk-bridge")
|
|
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() }()
|
|
if scanner := bufio.NewScanner(stdout); !scanner.Scan() || scanner.Text() != "ready" {
|
|
t.Fatal("fixture did not start")
|
|
}
|
|
var previous atspi.ObjectID
|
|
for index, probe := range []struct{ name, value string }{
|
|
{"Destination:", ""},
|
|
{"Display name:", "Example document"},
|
|
{"Preferred name", "Example value"},
|
|
{"Secret:", ""},
|
|
{"", "Unlabeled value"},
|
|
{"Account identifier", ""},
|
|
{"Read-only summary:", "Example summary"},
|
|
} {
|
|
if _, err := fmt.Fprintln(stdin, index); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
waitFocus:
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
t.Fatal("missing entry focus", index)
|
|
case event := <-adapter.Events():
|
|
if (event.Type != atspi.FocusEvent && event.Type != atspi.FocusedStateEvent) || !event.Source.Focused {
|
|
continue
|
|
}
|
|
snapshot := event.Source
|
|
if snapshot.Role != "text" && snapshot.Role != "password text" {
|
|
continue
|
|
}
|
|
if snapshot.ID() == previous {
|
|
continue
|
|
}
|
|
previous = snapshot.ID()
|
|
if snapshot.Name != probe.name {
|
|
t.Errorf("entry %d name = %q, want %q", index, snapshot.Name, probe.name)
|
|
}
|
|
if probe.name == "Read-only summary:" && snapshot.Editable {
|
|
t.Error("read-only fixture unexpectedly exposes editable state")
|
|
}
|
|
speech := FocusText(snapshot)
|
|
for _, part := range []string{probe.name, probe.value} {
|
|
if part != "" && !strings.Contains(speech, part) {
|
|
t.Errorf("entry %d speech %q omits %q", index, speech, part)
|
|
}
|
|
}
|
|
if strings.Contains(speech, "private fixture value") {
|
|
t.Error("password content leaked into speech")
|
|
}
|
|
break waitFocus
|
|
}
|
|
}
|
|
}
|
|
}
|