//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) } }