// Fenrir console screen reader. // Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // // You should have received a copy of the GNU General Public License along with // this program. If not, see . package scripts import ( "context" "os" "path/filepath" "strings" "testing" "time" ) // Exercise an unchanged real music controller, with every external action // replaced by a private executable. Never control the contributor's player. func TestInstalledMusicControllerWithFakePlayerctl(t *testing.T) { source := os.Getenv("FENRIR_MUSIC_SCRIPT") if source == "" { t.Skip("set FENRIR_MUSIC_SCRIPT to test an existing music controller") } data, err := os.ReadFile(source) if err != nil { t.Fatal(err) } root := t.TempDir() bin := filepath.Join(root, "bin") if err := os.Mkdir(bin, 0700); err != nil { t.Fatal(err) } write := func(path, body string) { t.Helper() if err := os.WriteFile(path, []byte(body), 0700); err != nil { t.Fatal(err) } } write(filepath.Join(bin, "id"), "#!/bin/sh\ncase \"$1\" in -un) echo Username;; *) echo 1000;; esac\n") write(filepath.Join(bin, "sleep"), "#!/bin/sh\nexit 0\n") write(filepath.Join(bin, "sudo"), "#!/bin/sh\nexit 99\n") write(filepath.Join(bin, "playerctl"), "#!/bin/sh\necho \"$*\" >> \"$SCRIPT_ACTION_LOG\"\ncase \"$*\" in *metadata*) echo 'Sample title by Sample artist';; esac\n") t.Setenv("PATH", bin) t.Setenv("SCRIPT_ACTION_LOG", filepath.Join(root, "actions")) path := filepath.Join(root, "musicplayer.sh") if err := os.WriteFile(path, data, 0700); err != nil { t.Fatal(err) } runner := New_runner(context.Background(), Limits{Workers: 1, Queue: 1, OutputBytes: 4096, Timeout: time.Second}, func(int) string { return "Username" }) defer runner.Close() for _, fixture := range []struct{ key, action string }{{"z", "previous"}, {"x", "play"}, {"c", "play-pause"}, {"v", "stop"}, {"b", "next"}, {"s", "shuffle toggle"}, {"u", "metadata"}, {"minus", "volume 0.05-"}, {"equal", "volume 0.05+"}} { link := filepath.Join(root, "musicplayer__-__key_shift__+__key_"+fixture.key+".sh") if err := os.Symlink(path, link); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(root, "actions"), nil, 0600); err != nil { t.Fatal(err) } runner.Submit(Invocation{Script: Script{Path: link}}) result := receive_result(t, runner) actions, err := os.ReadFile(filepath.Join(root, "actions")) if err != nil || result.Err != nil || result.Stderr != "" || !strings.Contains(string(actions), fixture.action) { t.Fatal(fixture, result, string(actions), err) } } }