Merged changes from hunter. Attempt to fix cthulhu announcing modifiers when suspended for xterm.

This commit is contained in:
Storm Dragon committed 2026-10-31 16:07:29 -04:00
1 parent 27867a48cb
commit f2cc47c8a0
24 files changed
+679 -67

No files matched your search

+200
View File
@@ -0,0 +1,200 @@
// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package wine
import (
"bytes"
"os"
"path/filepath"
"testing"
"time"
)
func write_helper_file(t *testing.T, path, data string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(data), 0700); err != nil {
t.Fatal(err)
}
}
func TestProtonLoaderBypassesProxy(t *testing.T) {
root := t.TempDir()
loader := filepath.Join(root, "Proton", "files", "bin", "wine")
preloader := filepath.Join(root, "Proton", "files", "lib", "wine", "i386-unix", "wine64-preloader")
proxy := filepath.Join(root, "proxy", "wine")
write_helper_file(t, loader, "loader")
write_helper_file(t, preloader, "preloader")
write_helper_file(t, proxy, "#!/bin/sh\n# PROTONTRICKS_STEAM_RUNTIME\n")
for _, configured := range []string{"", proxy, loader} {
if got := wine_loader(preloader, configured); got != loader {
t.Fatalf("loader = %q, want %q", got, loader)
}
}
}
func TestContainerHelperPublishesImmutableCompleteSets(t *testing.T) {
root := t.TempDir()
t.Setenv("XDG_STATE_HOME", filepath.Join(root, "state"))
helper := filepath.Join(root, "installed", "wine-access.exe")
names := []string{"wine-access.exe", "wine-access.exe.so", "wine-access-menu.dll", "wine-access-menu32.dll", "wine-access-menu-host32.exe"}
for _, name := range names {
write_helper_file(t, filepath.Join(filepath.Dir(helper), name), name)
}
environment := map[string]string{"PRESSURE_VESSEL_RUNTIME": "runtime"}
original, err := stage_helper(helper, environment)
if err != nil {
t.Fatal(err)
}
if original == helper {
t.Fatal("helper was not staged")
}
for _, name := range names {
data, err := os.ReadFile(filepath.Join(filepath.Dir(original), name))
if err != nil || string(data) != name {
t.Fatalf("incomplete set: %s: %v", name, err)
}
}
write_helper_file(t, helper+".so", "new sidecar")
updated, err := stage_helper(helper, environment)
if err != nil || updated == original {
t.Fatalf("updated set = %q, %v", updated, err)
}
data, _ := os.ReadFile(original + ".so")
if string(data) != "wine-access.exe.so" {
t.Fatal("old sidecar overwritten")
}
write_helper_file(t, updated+".so", "tampered")
if _, err := stage_helper(helper, environment); err == nil {
t.Fatal("accepted modified staging")
}
if err := os.Remove(helper + ".so"); err != nil {
t.Fatal(err)
}
if _, err := stage_helper(helper, environment); err == nil {
t.Fatal("accepted incomplete source set")
}
if got, err := stage_helper(helper, nil); err != nil || got != helper {
t.Fatalf("ordinary Wine = %q, %v", got, err)
}
}
func TestControllerOnlyExclusionsReachLauncherBeforeGameStarts(t *testing.T) {
t.Setenv("WINE_ACCESS_NO_DIALOG_READER", "")
manager := &Manager{ExcludedPrograms: []string{"Game.EXE"}}
launcher := prefix{programs: map[string]bool{"launcher.exe": true}}
arguments := manager.helper_arguments(manager.controller_only(launcher))
if len(arguments) != 2 || arguments[0] != "--exclude-hook-program" || arguments[1] != "game.exe" {
t.Fatalf("launcher arguments = %v", arguments)
}
launcher.programs["game.exe"] = true
if !manager.controller_only(launcher) {
t.Fatal("game did not select controller-only")
}
arguments = manager.helper_arguments(true)
if arguments[len(arguments)-1] != "--no-dialog-reader" {
t.Fatalf("game arguments = %v", arguments)
}
t.Setenv("WINE_ACCESS_NO_DIALOG_READER", "1")
if !(&Manager{}).controller_only(prefix{}) {
t.Fatal("environment override ignored")
}
}
func TestProtonInfrastructureDoesNotKeepHelperAlive(t *testing.T) {
for _, program := range []string{`C:\windows\system32\steam.exe`, `C:\windows\syswow64\steam.exe`, "xalia.exe", "conhost.exe"} {
if applicationProcess(program) {
t.Fatalf("infrastructure counted as app: %s", program)
}
}
for _, program := range []string{`C:\Program Files\Steam\steam.exe`, "Game.exe", "explorer.exe"} {
if !applicationProcess(program) {
t.Fatalf("app ignored: %s", program)
}
}
}
func TestLaunchForwardsProtonSyncAndKeepsReaderBus(t *testing.T) {
root := t.TempDir()
output := filepath.Join(root, "environment")
helper := filepath.Join(root, "helper")
write_helper_file(t, helper, "#!/bin/sh\nenv > \"$TEST_WINE_ENV\"\n")
t.Setenv("TEST_WINE_ENV", output)
t.Setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=/reader/bus")
manager := New(nil)
manager.helper = helper
child := &process{}
source := prefix{path: root, environment: map[string]string{"WINEESYNC": "1", "WINEFSYNC": "1", "XAUTHORITY": "/fixture/auth", "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/pressure-vessel/bus"}}
if err := manager.launch(child, source, time.Now()); err != nil {
t.Fatal(err)
}
select {
case <-child.done:
case <-time.After(3 * time.Second):
stop(child)
t.Fatal("helper did not exit")
}
data, err := os.ReadFile(output)
if err != nil {
t.Fatal(err)
}
for _, value := range []string{"WINEESYNC=1\n", "WINEFSYNC=1\n", "XAUTHORITY=/fixture/auth\n", "DBUS_SESSION_BUS_ADDRESS=unix:path=/reader/bus\n"} {
if !bytes.Contains(data, []byte(value)) {
t.Fatalf("missing %q", value)
}
}
}
func TestDiscoverAggregatesGamesAndEscalatesHelper(t *testing.T) {
root := t.TempDir()
procRoot := filepath.Join(root, "proc")
loader := filepath.Join(root, "wine")
write_helper_file(t, loader, "loader")
makeProcess := func(pid, program string) {
t.Helper()
base := filepath.Join(procRoot, pid)
write_helper_file(t, filepath.Join(base, "cmdline"), program+"\x00")
write_helper_file(t, filepath.Join(base, "environ"), "WINEPREFIX="+root+"\x00")
if err := os.Symlink(loader, filepath.Join(base, "exe")); err != nil {
t.Fatal(err)
}
}
makeProcess("101", "launcher.exe")
helper := filepath.Join(root, "helper")
write_helper_file(t, helper, "#!/bin/sh\nexec sleep 30\n")
manager := New(nil)
manager.helper, manager.procRoot = helper, procRoot
manager.ExcludedPrograms = []string{"GAME.exe"}
active := map[string]*process{}
defer func() {
for _, child := range active {
stop(child)
}
}()
manager.poll(active)
child := active[root]
if child == nil || child.cmd == nil || child.controllerOnly {
t.Fatal("launcher helper did not start normally")
}
originalPID := child.cmd.Process.Pid
makeProcess("102", "Game.exe")
discovered := discover(procRoot, uint32(os.Getuid()))
if len(discovered[root].programs) != 2 {
t.Fatalf("programs = %v", discovered[root].programs)
}
manager.poll(active)
if !child.controllerOnly || child.cmd == nil || child.cmd.Process.Pid == originalPID {
t.Fatal("helper did not switch to controller-only")
}
pid := child.cmd.Process.Pid
if err := os.RemoveAll(filepath.Join(procRoot, "102")); err != nil {
t.Fatal(err)
}
manager.poll(active)
if !child.controllerOnly || child.cmd.Process.Pid != pid {
t.Fatal("controller-only helper downgraded after game disappeared")
}
}