157 lines
6.3 KiB
Go
157 lines
6.3 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
"git.stormux.org/storm/cthulhu/internal/braille"
|
|
)
|
|
|
|
func TestNativeAudacious(t *testing.T) {
|
|
if os.Getenv("CTHULHU_NATIVE_AUDACIOUS") == "" {
|
|
t.Skip("requires private Audacious GUI session")
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
defer cancel()
|
|
adapter := atspi.New()
|
|
if err := adapter.Start(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer adapter.Stop(context.Background())
|
|
queue := &autocompleteRequestQueue{requests: make(chan speechRequest, 256)}
|
|
frames := make(chan braille.Frame, 256)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
processEventsWithInput(ctx, slog.New(slog.DiscardHandler),
|
|
outputQueue{speech: queue, braille: frameRecorder{frames: frames}},
|
|
adapter, nil, nil, 0, nil, nil, nil, nil)
|
|
}()
|
|
defer func() { cancel(); <-done }()
|
|
keys := func(chord string) {
|
|
t.Helper()
|
|
t.Logf("KEY %s", chord)
|
|
if out, err := exec.CommandContext(ctx, "xdotool", "key", "--clearmodifiers", chord).CombinedOutput(); err != nil {
|
|
t.Fatalf("keys: %v %s", err, out)
|
|
}
|
|
}
|
|
wait := func(wantSpeech, wantBraille string) {
|
|
t.Helper()
|
|
spoken, displayed := wantSpeech == "", wantBraille == ""
|
|
timer := time.NewTimer(2 * time.Second)
|
|
defer timer.Stop()
|
|
for {
|
|
select {
|
|
case request := <-queue.requests:
|
|
t.Logf("speech %s: %q", request.Cause, request.Text)
|
|
if request.Cause == atspi.CaretEvent && strings.Contains(request.Text, "password text") {
|
|
t.Fatalf("unfocused proxy password initialization was spoken: %q", request.Text)
|
|
}
|
|
if strings.Contains(request.Text, ".wav") || strings.Contains(request.Text, "/tmp/") {
|
|
t.Fatalf("hidden playlist columns were spoken: %q", request.Text)
|
|
}
|
|
spoken = spoken || strings.Contains(request.Text, wantSpeech)
|
|
case frame := <-frames:
|
|
t.Logf("Braille %q", frame.Text)
|
|
displayed = displayed || strings.Contains(frame.Text, wantBraille)
|
|
case <-timer.C:
|
|
if spoken && displayed {
|
|
return
|
|
}
|
|
out, _ := exec.CommandContext(ctx, "/usr/bin/python3", "../../tests/native_audacious.py", "--diagnose").CombinedOutput()
|
|
t.Logf("native selection: %s", out)
|
|
t.Fatalf("missing output speech=%q (%v) Braille=%q (%v)", wantSpeech, spoken, wantBraille, displayed)
|
|
case <-ctx.Done():
|
|
t.Fatal(ctx.Err())
|
|
}
|
|
if spoken && displayed {
|
|
timer.Reset(80 * time.Millisecond)
|
|
}
|
|
}
|
|
}
|
|
step := func(chord, speech, braille string) {
|
|
t.Helper()
|
|
keys(chord)
|
|
wait(speech, braille)
|
|
}
|
|
// Queueing fixtures never starts playback. Select a row explicitly before
|
|
// testing return from menus: an initially unselected table has no row.
|
|
step("alt+f", "Open Files ..., menu item", "Open Files ...")
|
|
if os.Getenv("CTHULHU_NATIVE_AUDACIOUS") == "-Q" {
|
|
step("Down", "Open Folder ..., menu item", "Open Folder ...")
|
|
step("Down", "Open URL ..., menu item", "Open URL ...")
|
|
step("Up", "Open Folder ..., menu item", "Open Folder ...")
|
|
} else {
|
|
step("Down", "Open URL ..., menu item", "Open URL ...")
|
|
step("Up", "Open Files ..., menu item", "Open Files ...")
|
|
}
|
|
step("Right", "Play, menu item", "Play")
|
|
for _, name := range []string{"Pause", "Stop", "Previous", "Next", "Previous Album", "Next Album", "Repeat"} {
|
|
step("Down", name+", ", name)
|
|
}
|
|
step("Return", "Alpha, Example Artist, Example Album, 0:01", "Alpha table cell")
|
|
step("Down", "Beta, Example Artist, Example Album, 0:01", "Beta table cell")
|
|
step("Up", "Alpha, Example Artist, Example Album, 0:01", "Alpha table cell")
|
|
step("alt+p", "Play, menu item", "Play")
|
|
for _, name := range []string{"Pause", "Stop", "Previous", "Next", "Previous Album", "Next Album"} {
|
|
step("Down", name+", ", name)
|
|
}
|
|
step("Down", "Repeat, check menu item, checked", "<x> Repeat check menu item")
|
|
step("Return", "Alpha, Example Artist, Example Album, 0:01", "Alpha table cell")
|
|
step("alt+p", "Play, menu item", "Play")
|
|
step("Right", "Play/Resume, menu item", "Play/Resume")
|
|
step("Down", "Refresh, menu item", "Refresh")
|
|
if os.Getenv("CTHULHU_NATIVE_AUDACIOUS") == "-Q" {
|
|
step("Down", "Find ..., menu item", "Find ...")
|
|
}
|
|
step("Down", "Sort, menu", "Sort")
|
|
step("Right", "By Track Number, menu item", "By Track Number")
|
|
step("Down", "By Title, menu item", "By Title")
|
|
step("Up", "By Track Number, menu item", "By Track Number")
|
|
step("Left", "Sort, menu", "Sort")
|
|
if os.Getenv("CTHULHU_NATIVE_AUDACIOUS") == "-Q" {
|
|
step("Escape", "Playlist, menu item", "Playlist")
|
|
}
|
|
step("Escape", "Alpha, Example Artist, Example Album, 0:01", "Alpha table cell")
|
|
step("ctrl+p", "Playlist Manager", "New Playlist table cell")
|
|
step("Tab", "New, button", "New button")
|
|
step("shift+Tab", "New Playlist, 3", "New Playlist table cell")
|
|
step("Escape", "Alpha, Example Artist, Example Album, 0:01", "Alpha table cell")
|
|
step("alt+f", "Open Files ..., menu item", "Open Files ...")
|
|
fileCommands := []string{"Open URL ...", "Add Files ...", "Add URL ...", "Search Library", "About ...", "Settings ..."}
|
|
if os.Getenv("CTHULHU_NATIVE_AUDACIOUS") == "-Q" {
|
|
fileCommands = []string{"Open Folder ...", "Open URL ...", "Add Files ...", "Add Folder ...", "Add URL ...", "Search Library", "About ...", "Settings ..."}
|
|
}
|
|
for _, name := range fileCommands {
|
|
step("Down", name+", menu item", name)
|
|
}
|
|
if os.Getenv("CTHULHU_NATIVE_AUDACIOUS") == "-Q" {
|
|
step("Return", "Audacious Settings", "Restart in")
|
|
step("Tab", "Qt Interface, combo box", "Qt Interface combo box")
|
|
step("Tab", "Native, combo box", "Native combo box")
|
|
step("shift+Tab", "Qt Interface, combo box", "Qt Interface combo box")
|
|
} else {
|
|
step("Return", "Audacious Settings", "Appearance")
|
|
step("Tab", "Restart in Qt mode, button", "Restart in Qt mode button")
|
|
step("Tab", "GTK Interface, combo box", "GTK Interface combo box")
|
|
step("shift+Tab", "Restart in Qt mode, button", "Restart in Qt mode button")
|
|
}
|
|
step("Escape", "Alpha, Example Artist, Example Album, 0:01", "Alpha table cell")
|
|
// Qt opens this popup without an active item; Down chooses the first.
|
|
keys("Menu")
|
|
step("Down", "Song Info ..., menu item", "Song Info ...")
|
|
step("Down", "Queue/Unqueue, menu item", "Queue/Unqueue")
|
|
step("Up", "Song Info ..., menu item", "Song Info ...")
|
|
step("Escape", "Alpha, Example Artist, Example Album, 0:01", "Alpha table cell")
|
|
}
|