package platform import ( "context" "errors" "fmt" "os" "strings" "sync" "testing" "time" ) func TestProbeCommandHelperProcess(t *testing.T) { mode := "" for i, arg := range os.Args { if arg == "platform-probe-helper" && i+1 < len(os.Args) { mode = os.Args[i+1] break } } if mode == "" { return } switch mode { case "empty": os.Exit(0) case "stdout": _, _ = fmt.Fprintln(os.Stdout, "probe-stdout") os.Exit(0) case "stderr-failure": _, _ = fmt.Fprintln(os.Stderr, "probe-went-wrong") os.Exit(3) case "sleep": time.Sleep(5 * time.Second) case "flood": for i := 0; i < 1024; i++ { _, _ = fmt.Fprint(os.Stdout, strings.Repeat("x", 1024)) } default: os.Exit(2) } } func helperProbe(t *testing.T, mode string, timeout time.Duration, maxOutput int) probeSpec { t.Helper() return probeSpec{ name: os.Args[0], args: []string{"-test.run=^TestProbeCommandHelperProcess$", "--", "platform-probe-helper", mode}, timeout: timeout, maxOutput: maxOutput, } } func TestRunProbeCommandTimesOut(t *testing.T) { start := time.Now() _, err := runProbeCommand(context.Background(), helperProbe(t, "sleep", 50*time.Millisecond, 1024)) if !errors.Is(err, context.DeadlineExceeded) { t.Fatalf("runProbeCommand() error = %v, want context.DeadlineExceeded", err) } if elapsed := time.Since(start); elapsed > time.Second { t.Fatalf("timed-out probe took %v", elapsed) } } func TestRunProbeCommandCapsCombinedOutput(t *testing.T) { const limit = 128 output, err := runProbeCommand(context.Background(), helperProbe(t, "flood", time.Second, limit)) if !errors.Is(err, errProbeOutputLimit) { t.Fatalf("runProbeCommand() error = %v, want errProbeOutputLimit", err) } if len(output) > limit { t.Fatalf("captured output length = %d, limit %d", len(output), limit) } } func TestRunProbeCommandHonorsParentCancellation(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() _, err := runProbeCommand(ctx, helperProbe(t, "sleep", time.Second, 1024)) if !errors.Is(err, context.Canceled) { t.Fatalf("runProbeCommand() error = %v, want context.Canceled", err) } } type fakeProbeResponse struct { output string err error } type fakeProbeRunner struct { mu sync.Mutex responses map[string][]fakeProbeResponse specs []probeSpec } func (f *fakeProbeRunner) Run(spec probeSpec) ([]byte, error) { f.mu.Lock() defer f.mu.Unlock() f.specs = append(f.specs, spec) responses := f.responses[spec.name] if len(responses) == 0 { return nil, fmt.Errorf("unexpected probe %s", spec.name) } response := responses[0] f.responses[spec.name] = responses[1:] return []byte(response.output), response.err } func (f *fakeProbeRunner) called() []probeSpec { f.mu.Lock() defer f.mu.Unlock() return append([]probeSpec(nil), f.specs...) } func TestLinuxGPUFallbackGetsIndependentProbeBudget(t *testing.T) { runner := &fakeProbeRunner{responses: map[string][]fakeProbeResponse{ "glxinfo": {{err: context.DeadlineExceeded}}, "vulkaninfo": {{output: "deviceName = NVIDIA GeForce RTX 3080\n"}}, }} info := detectGPULinuxWithRunner(runner) if len(info.Models) != 1 || info.Models[0] != "NVIDIA GeForce RTX 3080" { t.Fatalf("detectGPULinuxWithRunner() models = %v", info.Models) } calls := runner.called() if len(calls) != 3 || calls[0].name != "glxinfo" || calls[1].name != "vulkaninfo" || calls[2].name != "lspci" { t.Fatalf("probe order = %+v, want glxinfo, vulkaninfo, lspci", calls) } for _, call := range calls { if call.timeout != standardProbeTimeout || call.maxOutput != defaultProbeMaxBytes { t.Errorf("probe %s bounds = %s/%d", call.name, call.timeout, call.maxOutput) } } } func TestLinuxCodecFallbackGetsIndependentProbeBudget(t *testing.T) { runner := &fakeProbeRunner{responses: map[string][]fakeProbeResponse{ "vainfo": {{err: context.DeadlineExceeded}}, "vdpauinfo": {{output: "Decoder capabilities: H264 HEVC VP9\n"}}, }} codecs, err := parseCodecSupportLinuxWithRunner(&GPUInfo{}, runner) if err != nil { t.Fatal(err) } for _, want := range []string{"avc", "hevc", "vp9"} { if !contains(codecs, want) { t.Errorf("codecs = %v, missing %q", codecs, want) } } calls := runner.called() if len(calls) != 2 || calls[0].name != "vainfo" || calls[1].name != "vdpauinfo" { t.Fatalf("probe order = %+v, want vainfo then vdpauinfo", calls) } } func TestWindowsGPUFallbackUsesBoundedPowerShell(t *testing.T) { runner := &fakeProbeRunner{responses: map[string][]fakeProbeResponse{ "wmic": {{err: errors.New("not installed")}}, "powershell": {{output: "Intel Arc A770\nNVIDIA GeForce RTX 3060\n"}}, }} info := detectGPUWindowsWithRunner(runner) if len(info.Models) != 2 { t.Fatalf("detectGPUWindowsWithRunner() models = %v", info.Models) } calls := runner.called() if len(calls) != 2 || calls[1].timeout != slowProbeTimeout || calls[1].maxOutput != defaultProbeMaxBytes { t.Fatalf("PowerShell probe was not independently bounded: %+v", calls) } if !contains(calls[1].args, "-NonInteractive") || !contains(calls[1].args, "-NoProfile") { t.Errorf("PowerShell args = %v, want noninteractive/no-profile", calls[1].args) } }