//go:build !windows package windows import ( "context" "crypto/sha256" "errors" "io" "strings" "sync/atomic" "testing" "time" rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1" "github.com/rvbox/rvbox/internal/client/supervisor" "github.com/rvbox/rvbox/internal/domain" ) func TestExecProcessReleaseIsIdempotent_BH_LAUNCH_05(t *testing.T) { var calls atomic.Int32 process := &execProcess{releaseFn: func() error { calls.Add(1) return errors.New("release failed") }} if err := process.Release(context.Background()); err == nil || err.Error() != "release failed" { t.Fatalf("first release error = %v", err) } if err := process.Release(context.Background()); err == nil || err.Error() != "release failed" { t.Fatalf("repeated release error = %v", err) } if got := calls.Load(); got != 1 { t.Fatalf("release calls = %d, want one", got) } } func TestPortableSupervisorCapturesOutputAndSupportsStdin_HP_SUPERVISOR_03(t *testing.T) { t.Parallel() manager, err := NewSupervisor(NativeOptions{MaxOutputChunk: 8, WindowsTermGrace: 10 * time.Millisecond}) if err != nil { t.Fatal(err) } issue, err := domain.ParseUUIDv7("019c46f1-1d02-7000-8000-0000000000c1") if err != nil { t.Fatal(err) } process, err := manager.Start(context.Background(), supervisor.StartSpec{IssueUUID: issue, CommandRevision: 1, WorkingDirectory: t.TempDir(), Execution: &rvboxv1.ExecutionSpec{ShellType: rvboxv1.ShellType_SHELL_SH, Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "read line; printf 'out:%s\\n' \"$line\"; printf 'err\\n' >&2"}}}) if err != nil { t.Fatal(err) } if process.PID() == 0 { t.Fatal("portable supervisor did not expose process identity PID") } if err := process.WriteStdin(context.Background(), []byte("hello"), true); err != nil { t.Fatal(err) } if err := process.CloseStdin(context.Background()); err != nil { t.Fatal(err) } status, err := process.Wait(context.Background()) if err != nil || status.Code != 0 { t.Fatalf("wait = %+v, %v", status, err) } var output strings.Builder streams := map[rvboxv1.StreamKind]bool{} for { chunk, err := process.ReadOutput(context.Background()) if errors.Is(err, io.EOF) { break } if err != nil { t.Fatal(err) } output.Write(chunk.Data) streams[chunk.Stream] = true } if !strings.Contains(output.String(), "out:hello\n") || !strings.Contains(output.String(), "err\n") || !streams[rvboxv1.StreamKind_STREAM_STDOUT] || !streams[rvboxv1.StreamKind_STREAM_STDERR] { t.Fatalf("captured output = %q streams=%v", output.String(), streams) } second, err := manager.Start(context.Background(), supervisor.StartSpec{IssueUUID: issue, CommandRevision: 1, WorkingDirectory: t.TempDir(), Execution: &rvboxv1.ExecutionSpec{ShellType: rvboxv1.ShellType_SHELL_SH, Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "true"}}}) if err != nil { t.Fatalf("restart after terminal = %v", err) } _, _ = second.Wait(context.Background()) } func TestPortableSupervisorScriptMaterializationAndCleanup_HP_SUPERVISOR_04(t *testing.T) { t.Parallel() manager, err := NewSupervisor(NativeOptions{MaxWrapperBytes: 1024}) if err != nil { t.Fatal(err) } issue, _ := domain.ParseUUIDv7("019c46f1-1d02-7000-8000-0000000000c2") body := []byte("printf script-ok") process, err := manager.Start(context.Background(), supervisor.StartSpec{IssueUUID: issue, CommandRevision: 1, WorkingDirectory: t.TempDir(), ScriptBody: body, Execution: &rvboxv1.ExecutionSpec{ShellType: rvboxv1.ShellType_SHELL_SH, Source: &rvboxv1.ExecutionSpec_Script{Script: &rvboxv1.ScriptDescriptor{SizeBytes: uint64(len(body)), Sha256: digest(body)}}}}) if err != nil { t.Fatal(err) } if _, err := process.Wait(context.Background()); err != nil { t.Fatal(err) } chunk, err := process.ReadOutput(context.Background()) if err != nil || string(chunk.Data) != "script-ok" { t.Fatalf("script output = %+v, %v", chunk, err) } if _, err := process.ReadOutput(context.Background()); !errors.Is(err, io.EOF) { t.Fatalf("script output close = %v", err) } } func digest(value []byte) []byte { result := sha256.Sum256(value) return result[:] }