139 lines
4.3 KiB
Go
139 lines
4.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"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
"git.stormux.org/storm/cthulhu/internal/notification"
|
|
)
|
|
|
|
func TestNotificationProvidersAndHistoryMatchReferenceBehavior(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
event atspi.Event
|
|
expectedSpeech []string
|
|
expectedHistory string
|
|
}{
|
|
{
|
|
name: "xfce notification daemon labels",
|
|
event: notificationSnapshot("xfce4-notifyd", atspi.WindowCreateEvent, atspi.NewSnapshot(
|
|
"/notification", "", "frame", "", "Mail arrived New message",
|
|
)),
|
|
expectedSpeech: []string{"Notification Mail arrived New message"},
|
|
expectedHistory: "Notification Mail arrived New message",
|
|
},
|
|
{
|
|
name: "notify-osd name without value announces prefix and message",
|
|
event: notificationSnapshot("notify-osd", atspi.AccessibleNameEvent, atspi.NewSnapshot(
|
|
"/notification", "Volume", "notification", "at maximum", "",
|
|
)),
|
|
expectedSpeech: []string{"Notification", "Volume at maximum"},
|
|
expectedHistory: "Volume at maximum",
|
|
},
|
|
{
|
|
name: "notify-osd gauge value is not saved",
|
|
event: gaugeSnapshot(),
|
|
expectedSpeech: []string{"55"},
|
|
expectedHistory: "",
|
|
},
|
|
{
|
|
name: "generic accessible notification is saved",
|
|
event: showingNotification("Mail", "You have one unread message", true),
|
|
expectedSpeech: []string{"Mail, notification, You have one unread message"},
|
|
expectedHistory: "Mail You have one unread message",
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
presenter := notification.NewPresenter()
|
|
speech, entries := processNotificationEvent(t, test.event, presenter)
|
|
if len(speech) != len(test.expectedSpeech) {
|
|
t.Fatalf("speech count = %d, want %d: %q", len(speech), len(test.expectedSpeech), speech)
|
|
}
|
|
for index, expected := range test.expectedSpeech {
|
|
if speech[index] != expected {
|
|
t.Fatalf("speech %d = %q, want %q", index, speech[index], expected)
|
|
}
|
|
}
|
|
if test.expectedHistory == "" {
|
|
if len(entries) != 0 {
|
|
t.Fatalf("history length = %d, want 0", len(entries))
|
|
}
|
|
return
|
|
}
|
|
if len(entries) != 1 {
|
|
t.Fatalf("history length = %d, want 1", len(entries))
|
|
}
|
|
if entries[0].Message != test.expectedHistory {
|
|
t.Fatalf("history = %q, want %q", entries[0].Message, test.expectedHistory)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHiddenGenericNotificationIsNotSaved(t *testing.T) {
|
|
event := showingNotification("Mail", "You have one unread message", false)
|
|
presenter := notification.NewPresenter()
|
|
_, entries := processNotificationEvent(t, event, presenter)
|
|
if len(entries) != 0 {
|
|
t.Fatalf("history length = %d, want 0", len(entries))
|
|
}
|
|
}
|
|
|
|
func showingNotification(name, text string, showing bool) atspi.Event {
|
|
event := notificationSnapshot("Application", atspi.ShowingStateEvent,
|
|
atspi.NewSnapshot("/notification", name, "notification", "", text))
|
|
if showing {
|
|
event.Detail1 = 1
|
|
}
|
|
return event
|
|
}
|
|
|
|
func notificationSnapshot(application, eventType string, snapshot atspi.Snapshot) atspi.Event {
|
|
snapshot.ApplicationName = application
|
|
return atspi.NewEvent(eventType, snapshot)
|
|
}
|
|
|
|
func gaugeSnapshot() atspi.Event {
|
|
snapshot := atspi.NewSnapshot("/notification", "Volume", "notification", "", "")
|
|
snapshot.ValueKnown = true
|
|
snapshot.CurrentValue = 55
|
|
return notificationSnapshot("notify-osd", atspi.ValueChangedEvent, snapshot)
|
|
}
|
|
|
|
func processNotificationEvent(t *testing.T, event atspi.Event, presenter *notification.Presenter) ([]string, []notification.Entry) {
|
|
t.Helper()
|
|
speech := &fakeSpeechQueue{spoken: make(chan string, 8)}
|
|
adapter := &fakeAdapter{events: make(chan atspi.Event, 1)}
|
|
adapter.events <- event
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
completed := make(chan error, 1)
|
|
go func() {
|
|
completed <- processEventsWithInput(
|
|
ctx, slog.New(slog.DiscardHandler), speech, adapter,
|
|
nil, nil, 0, nil, nil, nil, presenter,
|
|
)
|
|
}()
|
|
var messages []string
|
|
for {
|
|
select {
|
|
case message := <-speech.spoken:
|
|
messages = append(messages, message)
|
|
case <-time.After(50 * time.Millisecond):
|
|
cancel()
|
|
if err := <-completed; err != nil {
|
|
t.Fatalf("process events: %v", err)
|
|
}
|
|
return messages, presenter.Entries()
|
|
}
|
|
}
|
|
}
|