114 lines
3.4 KiB
Go
114 lines
3.4 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
//go:build linux && cgo
|
|
|
|
package terminal
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"os/exec"
|
|
"slices"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/fenrir/internal/applicationoverrides/networkmanager"
|
|
"git.stormux.org/storm/fenrir/internal/screen"
|
|
)
|
|
|
|
// Opt-in read-only application check: open nmtui's main menu, move its
|
|
// selection, and exit. No connection, hostname or network setting is changed.
|
|
func TestNetworkManagerOverrideNative(t *testing.T) {
|
|
if os.Getenv("FENRIR_APPLICATION_OVERRIDE_NATIVE") != "1" {
|
|
t.Skip("set FENRIR_APPLICATION_OVERRIDE_NATIVE=1 for private nmtui/Screen PTY checks")
|
|
}
|
|
nmtui, err := exec.LookPath("nmtui")
|
|
if err != nil {
|
|
t.Skip("nmtui unavailable")
|
|
}
|
|
for _, multiplexed := range []bool{false, true} {
|
|
name := "direct"
|
|
if multiplexed {
|
|
name = "screen"
|
|
}
|
|
t.Run(name, func(t *testing.T) {
|
|
env := append(os.Environ(), "TERM=xterm-256color", "LANG=C", "LC_ALL=C", "NEWT_COLORS=")
|
|
cmd := exec.Command(nmtui)
|
|
if multiplexed {
|
|
path, err := exec.LookPath("screen")
|
|
if err != nil {
|
|
t.Skip("GNU Screen unavailable")
|
|
}
|
|
socketDirectory := t.TempDir()
|
|
if err := os.Chmod(socketDirectory, 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
env = append(env, "SCREENDIR="+socketDirectory)
|
|
cmd = exec.Command(path, "-c", "/dev/null", "-m", "-S", "fenrir-override-test", nmtui)
|
|
defer func() {
|
|
quit := exec.Command(path, "-S", "fenrir-override-test", "-X", "quit")
|
|
quit.Env = env
|
|
_ = quit.Run()
|
|
}()
|
|
}
|
|
master, slave, err := open_pty(24, 100)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer syscall.Close(master)
|
|
slaveFile := os.NewFile(uintptr(slave), "private nmtui PTY")
|
|
defer slaveFile.Close()
|
|
cmd.Stdin, cmd.Stdout, cmd.Stderr = slaveFile, slaveFile, slaveFile
|
|
cmd.Env = env
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true, Setctty: true, Ctty: 0}
|
|
if err := cmd.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = cmd.Process.Kill(); _ = cmd.Wait() }()
|
|
if err := syscall.SetNonblock(master, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
emulator, err := new_emulator(24, 100)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer emulator.close()
|
|
adapter := &networkmanager.Adapter{}
|
|
var before []screen.Attribute
|
|
waitForMenu := func() {
|
|
t.Helper()
|
|
deadline := time.Now().Add(4 * time.Second)
|
|
var buffer [32768]byte
|
|
for time.Now().Before(deadline) {
|
|
n, err := syscall.Read(master, buffer[:])
|
|
if n > 0 {
|
|
emulator.feed(buffer[:n])
|
|
}
|
|
if err != nil && !errors.Is(err, syscall.EAGAIN) && !errors.Is(err, syscall.EINTR) {
|
|
t.Fatalf("nmtui PTY: %v; screen: %s", err, string(emulator.snapshot(true, false).Cells))
|
|
}
|
|
snapshot := emulator.snapshot(true, false)
|
|
if adapter.Observe(snapshot).Recognized && (before == nil || !slices.Equal(before, snapshot.Attributes)) {
|
|
return
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
t.Fatalf("nmtui menu not recognized: %s", strings.TrimSpace(string(emulator.snapshot(true, false).Cells)))
|
|
}
|
|
waitForMenu()
|
|
before = slices.Clone(emulator.snapshot(true, false).Attributes)
|
|
// GNU Screen and xterm both forward the normal arrow sequence.
|
|
if _, err := syscall.Write(master, []byte("\x1bOB")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
waitForMenu()
|
|
if _, err := syscall.Write(master, []byte("\x1b")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
}
|
|
}
|