Add global Wine preferences tab
Phase 3 of the Wine/Proton stage (decisions 6, 9, 13, 19 in docs/testing/wine-proton.md): a global-only Wine tab modeled on the Pronunciation page, with the controller-only checkbox and a typed editable program list. Wine values flow through the preferences protocol so a global Apply reaches the live Wine settings from Phase 2 with no reader restart. Entries are validated as edited and named in-tab, blocking Apply until fixed; case-insensitive duplicates collapse. Per-application preferences expose no Wine editing and an app-scoped Apply cannot change the governing list.
This commit is contained in:
1 parent
dc6a961032
commit
75f34ea767
15 files changed
+543
-5
No files matched your search
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// Authors: Storm Dragon, Angus Kola
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package preferences
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.stormux.org/storm/cthulhu/internal/config"
|
||||
)
|
||||
|
||||
func TestWineValuesRoundTripAndOwnership(t *testing.T) {
|
||||
settings := config.DefaultSettings()
|
||||
settings.Wine = config.WineSettings{ControllerOnly: true, ExcludedPrograms: []string{"Game.exe", "Another Game.exe"}}
|
||||
values := FromSettings(settings)
|
||||
if !reflect.DeepEqual(values.Wine, settings.Wine) {
|
||||
t.Fatalf("Wine values = %+v", values.Wine)
|
||||
}
|
||||
values.Wine.ExcludedPrograms[0] = "Changed.exe"
|
||||
if settings.Wine.ExcludedPrograms[0] != "Game.exe" {
|
||||
t.Fatal("FromSettings aliases the Wine program list")
|
||||
}
|
||||
values.Wine.ExcludedPrograms[0] = "Game.exe"
|
||||
values.Wine.ControllerOnly = false
|
||||
values.Wine.ExcludedPrograms = append(values.Wine.ExcludedPrograms, "New Game.exe")
|
||||
merged, err := values.Merge(settings)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := FromSettings(merged); !reflect.DeepEqual(got, values) {
|
||||
t.Fatalf("Wine values did not round trip:\n got %+v\nwant %+v", got.Wine, values.Wine)
|
||||
}
|
||||
values.Wine.ExcludedPrograms[0] = "Changed.exe"
|
||||
if merged.Wine.ExcludedPrograms[0] != "Game.exe" {
|
||||
t.Fatal("merged Wine programs alias the UI values")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWineValuesMergeCollapsesCaseInsensitiveDuplicates(t *testing.T) {
|
||||
settings := config.DefaultSettings()
|
||||
values := FromSettings(settings)
|
||||
values.Wine.ExcludedPrograms = []string{"Game.exe", "game.EXE", "Other.exe", "GAME.exe", "other.exe"}
|
||||
merged, err := values.Merge(settings)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := []string{"Game.exe", "Other.exe"}
|
||||
if !reflect.DeepEqual(merged.Wine.ExcludedPrograms, want) {
|
||||
t.Fatalf("collapsed Wine programs = %q, want %q", merged.Wine.ExcludedPrograms, want)
|
||||
}
|
||||
values.Wine.ExcludedPrograms = nil
|
||||
merged, err = values.Merge(settings)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if merged.Wine.ExcludedPrograms != nil {
|
||||
t.Fatalf("empty Wine list became %q, want nil", merged.Wine.ExcludedPrograms)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWineValuesMergeRejectsInvalidEntriesByName(t *testing.T) {
|
||||
overlong := strings.Repeat("a", 252) + ".exe"
|
||||
cases := []struct {
|
||||
name string
|
||||
entry string
|
||||
wantSub string
|
||||
}{
|
||||
{name: "windows path", entry: `C:\Games\Game.exe`, wantSub: `C:\Games\Game.exe`},
|
||||
{name: "unix path", entry: "Games/Game.exe", wantSub: "Games/Game.exe"},
|
||||
{name: "blank", entry: "", wantSub: "blank"},
|
||||
{name: "spaces only", entry: " ", wantSub: "blank"},
|
||||
{name: "overlong", entry: overlong, wantSub: overlong},
|
||||
{name: "leading space", entry: " Game.exe", wantSub: "Game.exe"},
|
||||
{name: "trailing space", entry: "Game.exe ", wantSub: "Game.exe"},
|
||||
}
|
||||
for _, test := range cases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
settings := config.DefaultSettings()
|
||||
settings.Wine.ExcludedPrograms = []string{"Kept.exe"}
|
||||
values := FromSettings(settings)
|
||||
values.Wine.ExcludedPrograms = []string{"Kept.exe", test.entry}
|
||||
merged, err := values.Merge(settings)
|
||||
if err == nil {
|
||||
t.Fatalf("Merge accepted Wine entry %q", test.entry)
|
||||
}
|
||||
if !strings.Contains(err.Error(), test.wantSub) {
|
||||
t.Fatalf("Merge error %q does not name %q", err, test.wantSub)
|
||||
}
|
||||
if !reflect.DeepEqual(merged.Wine.ExcludedPrograms, []string{"Kept.exe"}) {
|
||||
t.Fatalf("rejected Merge changed Wine programs to %q", merged.Wine.ExcludedPrograms)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user