Files
2026-07-13 13:00:08 +08:00

59 lines
1.7 KiB
Go

package builtin
import (
"context"
"sync"
"testing"
"time"
"reasonix/internal/event"
"reasonix/internal/jobs"
"reasonix/internal/sandbox"
)
// A foreground bash command keeps the Windows sandbox's short lock wait so a
// queued turn fails fast with the holder named; a background job blocks nobody
// while it queues, so it opts into the patient wait instead of dying while
// another sandboxed command holds the workspace.
func TestBashSandboxLockWaitForegroundVsBackground(t *testing.T) {
sh := sandbox.ResolveShell("", "", nil)
var mu sync.Mutex
var waits []time.Duration
oldCommand := bashSandboxCommand
bashSandboxCommand = func(spec sandbox.Spec, sh sandbox.Shell, command string) ([]string, bool) {
mu.Lock()
waits = append(waits, spec.WindowsLockWait)
mu.Unlock()
return unconfinedShellArgv(sh, command), false
}
defer func() { bashSandboxCommand = oldCommand }()
if _, err := (bash{shell: sh}).Execute(context.Background(), argsJSON(t, map[string]any{
"command": echoForShell(sh, "fg"),
})); err != nil {
t.Fatalf("foreground bash: %v", err)
}
m := jobs.NewManager(event.Discard)
defer m.Close()
ctx := jobs.WithManager(context.Background(), m)
if _, err := (bash{shell: sh}).Execute(ctx, argsJSON(t, map[string]any{
"command": echoForShell(sh, "bg"),
"run_in_background": true,
})); err != nil {
t.Fatalf("background bash: %v", err)
}
mu.Lock()
defer mu.Unlock()
if len(waits) != 2 {
t.Fatalf("sandbox wraps = %d, want 2", len(waits))
}
if waits[0] != 0 {
t.Fatalf("foreground WindowsLockWait = %s, want 0 (the short interactive default)", waits[0])
}
if waits[1] != windowsBackgroundSandboxLockWait {
t.Fatalf("background WindowsLockWait = %s, want %s", waits[1], windowsBackgroundSandboxLockWait)
}
}