Files
autoPriority/windows_native_test.go
T

147 lines
4.0 KiB
Go

//go:build windows
package main
import (
"bufio"
"context"
"fmt"
"io"
"os"
"os/exec"
"sync"
"syscall"
"testing"
"time"
)
func TestNativeProcessHelper(t *testing.T) {
if os.Getenv("AP_OWNED_HELPER") != "1" {
t.Skip("helper child only")
}
fmt.Fprintln(os.Stdout, "READY")
io.Copy(io.Discard, os.Stdin)
os.Exit(0)
}
func startOwnedHelper(t *testing.T) (uint32, func()) {
t.Helper()
exe, err := os.Executable()
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
cmd := exec.CommandContext(ctx, exe, "-test.run=^TestNativeProcessHelper$")
cmd.Env = append(os.Environ(), "AP_OWNED_HELPER=1")
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000}
input, err := cmd.StdinPipe()
if err != nil {
cancel()
t.Fatal(err)
}
output, err := cmd.StdoutPipe()
if err != nil {
input.Close()
cancel()
t.Fatal(err)
}
if err = cmd.Start(); err != nil {
input.Close()
cancel()
t.Fatal(err)
}
var once sync.Once
stop := func() { once.Do(func() { input.Close(); cmd.Wait(); cancel() }) }
t.Cleanup(stop)
line, err := bufio.NewReader(output).ReadString('\n')
if err != nil || line != "READY\n" {
t.Fatalf("owned helper did not start: %v", err)
}
return uint32(cmd.Process.Pid), stop
}
func requireNativeProcesses(t *testing.T) {
t.Helper()
if os.Getenv("AP_NATIVE_PROCESSES") != "1" {
t.Skip("opt-in, test-owned children only; never enumerates other processes")
}
}
func TestNativeOwnedProcessRightsIdentityAndLifetime(t *testing.T) {
requireNativeProcesses(t)
pid, stop := startOwnedHelper(t)
backend := newWindowsBackend()
// The system-wide enumerator is intentionally NEVER called in native tests.
backend.list = func() ([]procInfo, error) { return []procInfo{{PID: pid, Name: "OwnedHelper.exe"}}, nil }
snap, err := backend.Snapshot()
if err != nil || len(snap.Processes) != 1 {
t.Fatalf("owned snapshot failed: %v", err)
}
p := snap.Processes[0]
defer backend.Close(&p)
if p.Key.Created == 0 || p.RSS == 0 || p.Name == "" || !p.Waitable {
t.Fatal("incomplete owned-process metadata")
}
alive, err := backend.Alive(&p)
if err != nil || !alive {
t.Fatal("live helper not recognized", err)
}
if err = setPrio(p.Handle, p.PID, p.Prio); err == nil {
t.Fatal("snapshot handle unexpectedly grants SET_INFORMATION")
}
original := p.Prio
bad := p
bad.Key.Created++
if err = backend.SetPriority(&bad, PriorityClassHigh); err == nil {
t.Fatal("identity mismatch did not prevent write")
}
now, err := backend.Priority(&p)
if err != nil || now != original {
t.Fatal("identity check changed priority")
}
if err = backend.SetPriority(&p, PriorityClassBelowNormal); err != nil {
t.Fatal(err)
}
now, err = backend.Priority(&p)
if err != nil || now != PriorityClassBelowNormal {
t.Fatal("owned priority write not observed", err)
}
// Do not request REALTIME in native tests. The child is about to terminate.
stop()
alive, err = backend.Alive(&p)
if err != nil || alive {
t.Fatal("signaled process handle not recognized as exited", err)
}
}
func TestNativeControllerRestoresOwnedChild(t *testing.T) {
requireNativeProcesses(t)
pid, _ := startOwnedHelper(t)
backend := newWindowsBackend()
backend.list = func() ([]procInfo, error) { return []procInfo{{PID: pid, Name: "OwnedHelper.exe"}}, nil }
initial, err := backend.inspect(procInfo{PID: pid})
if err != nil {
t.Fatal(err)
}
defer backend.Close(&initial)
// Establish a known, non-realtime baseline on this test-owned child.
if err = backend.SetPriority(&initial, PriorityClassBelowNormal); err != nil {
t.Fatal(err)
}
c := newController(backend, 1, 0, false, time.Minute)
r := c.Step()
if len(r.Errors) != 0 {
c.Shutdown()
t.Fatal(r.Errors)
}
actual, err := backend.Priority(&initial)
if err != nil || actual != PriorityClassHigh {
c.Shutdown()
t.Fatal("promotion failed", err)
}
r = c.Shutdown()
if r.Unrestored != 0 || len(r.Errors) != 0 {
t.Fatal(r)
}
actual, err = backend.Priority(&initial)
if err != nil || actual != PriorityClassBelowNormal {
t.Fatal("baseline not restored", err)
}
}