Files
cthulhu/internal/app/wine_live_settings_test.go
hjozwiak 9a10a15b68 Read Wine settings live at poll and spawn
Phase 2 of the Wine/Proton stage (decisions 3, 4, 16, 17, 19 in docs/testing/wine-proton.md): replace the Wine manager's startup snapshot with a live settings source read once per poll, so accepted global settings take effect with no reader restart. The source publishes only the active/global profile's Wine values; per-application profile switching never changes the governing list. A newly listed running program restarts its helper once in the same mode; removals and clearing the global switch never downgrade a running helper.
2026-11-02 01:15:33 -05:00

99 lines
3.8 KiB
Go

package app
import (
"context"
"log/slog"
"os"
"path/filepath"
"reflect"
"testing"
"git.stormux.org/storm/cthulhu/internal/config"
"git.stormux.org/storm/cthulhu/internal/speech"
"git.stormux.org/storm/cthulhu/internal/wine"
)
// Wine settings are live and global-only: editing the TOML and applying the
// reloaded global profile changes what the Wine manager will use at its next
// poll or spawn, with no reader restart, while switching focus between
// applications with their own profiles never changes the governing list.
func TestWineManagerReadsLiveGlobalSettingsFromTOML(t *testing.T) {
path := filepath.Join(t.TempDir(), "settings.toml")
applications := "[applications]\n" +
"'Example Editor' = 'editor'\n" +
"'Other Editor' = 'other'\n" +
"[profiles.editor.wine]\n" +
"controller-only = true\n" +
"excluded-hook-programs = ['EditorOnly.exe']\n" +
"[profiles.other.wine]\n" +
"excluded-hook-programs = ['OtherOnly.exe']\n"
writeSettings := func(programs string) {
t.Helper()
contents := "schema-version = 2\nactive-profile = 'default'\n" +
"[profiles.default.wine]\n" +
"excluded-hook-programs = [" + programs + "]\n" + applications
if err := os.WriteFile(path, []byte(contents), 0600); err != nil {
t.Fatal(err)
}
}
writeSettings("'Game.exe'")
store, err := config.NewStore(path)
if err != nil {
t.Fatal(err)
}
global, err := store.Load()
if 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)
worker.live.StoreGlobalWine(global.Wine)
manager := newWineManager(slog.New(slog.DiscardHandler), worker.live)
if manager.SettingsSource == nil {
t.Fatal("Wine manager has no live settings source")
}
governing := func() wine.Settings { return manager.SettingsSource() }
if want := (wine.Settings{ExcludedPrograms: []string{"Game.exe"}}); !reflect.DeepEqual(governing(), want) {
t.Fatalf("governing Wine settings = %+v, want %+v", governing(), want)
}
// Edit the TOML, reload it, and apply the loaded global profile through
// the same path preferences Apply uses. The same manager instance must
// now govern with the addition: no reader or manager restart happened.
ctx := context.Background()
writeSettings("'Game.exe', 'New Game.exe'")
reloaded, err := store.Load()
if err != nil {
t.Fatal(err)
}
if response := worker.applyNext(ctx, reloaded, false, false); !response.Accepted {
t.Fatalf("reloaded TOML settings were not applied: %+v", response)
}
want := wine.Settings{ExcludedPrograms: []string{"Game.exe", "New Game.exe"}}
if !reflect.DeepEqual(governing(), want) {
t.Fatalf("governing Wine settings after live addition = %+v, want %+v", governing(), want)
}
// Moving focus to an application whose profile carries different Wine
// values changes the reader's effective settings, but must never change
// which Wine list governs a prefix.
worker.switchApplication(ctx, "Example Editor")
if effective := worker.live.Load().Wine; !effective.ControllerOnly ||
!reflect.DeepEqual(effective.ExcludedPrograms, []string{"EditorOnly.exe"}) {
t.Fatalf("application switch did not change effective Wine settings: %+v", effective)
}
if !reflect.DeepEqual(governing(), want) {
t.Fatalf("application profile changed governing Wine settings: %+v, want %+v", governing(), want)
}
worker.switchApplication(ctx, "Other Editor")
if effective := worker.live.Load().Wine; effective.ControllerOnly ||
!reflect.DeepEqual(effective.ExcludedPrograms, []string{"OtherOnly.exe"}) {
t.Fatalf("second application switch did not change effective Wine settings: %+v", effective)
}
if !reflect.DeepEqual(governing(), want) {
t.Fatalf("second application profile changed governing Wine settings: %+v, want %+v", governing(), want)
}
}