Cthulhu is no longer a fork. Now it is a screen reader written in Golang.

This commit is contained in:
Storm Dragon committed 2026-10-14 00:16:34 -04:00
1 parent a998fe67de
commit 069fc674dc
708 files changed
+112482 -532

No files matched your search

@@ -0,0 +1,87 @@
package app
import (
"context"
"log/slog"
"path/filepath"
"testing"
"git.stormux.org/storm/cthulhu/internal/config"
"git.stormux.org/storm/cthulhu/internal/input"
"git.stormux.org/storm/cthulhu/internal/preferences"
"git.stormux.org/storm/cthulhu/internal/speech"
)
func TestApplicationPreferencesSwitchAndPersistIndependently(t *testing.T) {
path := filepath.Join(t.TempDir(), "settings.toml")
global := config.DefaultSettings()
global.Path = path
store, err := config.NewStore(path)
if err != nil {
t.Fatal(err)
}
if err := store.Save(global); err != nil {
t.Fatal(err)
}
worker := newPreferencesWorker(slog.New(slog.DiscardHandler), global,
settingsUpdateFunc(func(context.Context, speech.Settings) error { return nil }))
worker.live = &liveSettings{}
worker.live.Store(global)
ctx := context.Background()
worker.switchApplication(ctx, "Example Editor")
if !worker.beginEdit(ctx, "\x00") || worker.editApp != "Example Editor" {
t.Fatal("application shortcut did not select focused application")
}
values := preferences.FromSettings(worker.editing)
values.TimeFormat = "24-hour"
values.Reading.AutomaticallyReadClipboardChanges = true
response := worker.apply(ctx, preferences.Request{Values: values, Save: true})
if !response.Accepted || worker.settings.TimeFormat != "24-hour" || !worker.settings.Reading.AutomaticallyReadClipboardChanges {
t.Fatalf("application settings were not applied: %+v", response)
}
worker.editingActive = false
worker.switchApplication(ctx, "Other Editor")
if worker.settings.TimeFormat != global.TimeFormat || worker.settings.Reading.AutomaticallyReadClipboardChanges {
t.Fatal("application settings leaked to another application")
}
worker.switchApplication(ctx, "Example Editor")
if worker.settings.TimeFormat != "24-hour" || worker.live.Load().TimeFormat != "24-hour" {
t.Fatal("application settings were not restored on return")
}
worker.handleSpeechCommand(ctx, input.IncreaseSpeechSetting)
worker.handleSpeechCommand(ctx, input.SaveSpeechSettings)
if !worker.beginEdit(ctx, "") {
t.Fatal("global preferences did not open")
}
globalValues := preferences.FromSettings(worker.editing)
globalValues.DateFormat = "numbers-mdy"
if response := worker.apply(ctx, preferences.Request{Values: globalValues, Save: true}); !response.Accepted {
t.Fatalf("global setting was not applied: %+v", response)
}
worker.editingActive = false
if worker.settings.TimeFormat != "24-hour" || worker.settings.DateFormat != "numbers-mdy" {
t.Fatalf("application session did not inherit global date: %+v", worker.settings)
}
loaded, err := store.Load()
if err != nil || loaded.TimeFormat != global.TimeFormat || loaded.DateFormat != "numbers-mdy" {
t.Fatalf("global profile changed: %+v, %v", loaded, err)
}
appLoaded, mapped, err := store.LoadForApplication("Example Editor")
if err != nil || !mapped || appLoaded.TimeFormat != "24-hour" || appLoaded.DateFormat != "numbers-mdy" || appLoaded.DefaultVoice.Rate == nil || !appLoaded.Reading.AutomaticallyReadClipboardChanges {
t.Fatalf("application profile not saved: %+v, %t, %v", appLoaded, mapped, err)
}
if loaded.DefaultVoice.Rate != nil || loaded.Reading.AutomaticallyReadClipboardChanges {
t.Fatal("application speech shortcut overwrote global voice")
}
restarted := newPreferencesWorker(slog.New(slog.DiscardHandler), loaded,
settingsUpdateFunc(func(context.Context, speech.Settings) error { return nil }))
sessionGlobal := loaded
sessionGlobal.DateFormat = "numbers-dmy"
if response := restarted.applyNext(ctx, sessionGlobal, false, false); !response.Accepted {
t.Fatalf("session global change rejected: %+v", response)
}
restarted.switchApplication(ctx, "Example Editor")
if restarted.settings.DateFormat != "numbers-dmy" || restarted.settings.TimeFormat != "24-hour" || !restarted.settings.Reading.AutomaticallyReadClipboardChanges {
t.Fatalf("first application load lost session inheritance: %+v", restarted.settings)
}
}