Files
cthulhu/internal/presentation/position_sound_test.go

81 lines
3.0 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package presentation
import (
"testing"
"git.stormux.org/storm/cthulhu/internal/atspi"
"git.stormux.org/storm/cthulhu/internal/sound"
)
func TestPositionSoundPolicy(t *testing.T) {
base := Options{EnableSound: true, PlaySoundForPositionInSet: true, RoleSoundPresentation: "sound_and_speech"}
for _, test := range []struct {
name string
position, size int
want string
}{
{"first", 1, 3, sound.PositionCueName(0)},
{"middle", 2, 3, sound.PositionCueName(0.5)},
{"last", 3, 3, sound.PositionCueName(1)},
{"virtual set", 501, 1001, sound.PositionCueName(0.5)},
{"unknown", 0, 0, ""}, {"negative", -1, 3, ""},
{"outside set", 4, 3, ""}, {"unknown size", 1, -1, ""},
{"single item", 1, 1, ""},
} {
t.Run(test.name, func(t *testing.T) {
snapshot := atspi.Snapshot{Name: "Example", Role: "list item", PositionInSet: test.position, SetSize: test.size}
cues := SoundCues(snapshot, base)
if test.want == "" {
if len(cues) != 0 {
t.Fatalf("invalid set produced cues: %v", cues)
}
} else if len(cues) != 1 || cues[0].Name != test.want {
t.Fatalf("position cues = %v, want %s", cues, test.want)
}
if got := FocusTextWithOptions(snapshot, base); got != "Example, list item" {
t.Fatalf("position cue changed speech: %q", got)
}
})
}
snapshot := atspi.Snapshot{Role: "radio button", Checked: true, PositionInSet: 2, SetSize: 3}
for _, gate := range []string{"disabled sounds", "disabled position", "speech only", "unavailable", "defunct"} {
options, source := base, snapshot
switch gate {
case "disabled sounds":
options.EnableSound = false
case "disabled position":
options.PlaySoundForPositionInSet = false
case "speech only":
options.RoleSoundPresentation = "speech_only"
case "unavailable":
options.SoundCueAvailable = func(string) bool { return false }
case "defunct":
source.Defunct = true
}
if got := SoundCues(source, options); len(got) != 0 {
t.Errorf("%s cues: %v", gate, got)
}
}
base.PlaySoundForState = true
base.RoleSoundPresentation = "sound_only"
if got := SoundCues(snapshot, base); len(got) != 2 || got[0].Name != "radiobutton_checked" || got[1].Name != sound.PositionCueName(0.5) {
t.Fatalf("state/position order = %v", got)
}
if got := EventSoundCues(atspi.NewEvent("object:state-changed:checked", snapshot), base); len(got) != 1 || got[0].Name != "radiobutton_checked" {
t.Fatalf("state change replayed position: %v", got)
}
for _, eventType := range []string{atspi.FocusEvent, atspi.FocusedStateEvent, atspi.ActiveDescendantEvent, atspi.SelectionChangedEvent} {
if got := EventSoundCues(atspi.NewEvent(eventType, snapshot), base); len(got) != 2 {
t.Errorf("%s cues: %v", eventType, got)
}
}
line := atspi.DocumentLine{Contents: []atspi.DocumentContent{{Snapshot: snapshot}, {Snapshot: snapshot}}}
if got := DocumentLineSoundCues(line, base); len(got) != 2 {
t.Fatalf("line replayed position: %v", got)
}
}