From d5f48266e8d9437680e0ce079534f457cfec1ef9 Mon Sep 17 00:00:00 2001 From: PJ Date: Tue, 18 Aug 2026 00:01:26 +0530 Subject: [PATCH] fix(corpus-sweep): name every missing binary, in flag order Same map-ranging bug as the sibling tool, and this copy had no test on the missing-binary path at all. --- cmd/internal-tools/corpus-sweep/sweep.go | 26 +++++++---- cmd/internal-tools/corpus-sweep/sweep_test.go | 46 +++++++++++++++++++ 2 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 cmd/internal-tools/corpus-sweep/sweep_test.go diff --git a/cmd/internal-tools/corpus-sweep/sweep.go b/cmd/internal-tools/corpus-sweep/sweep.go index 39d56be..ba12adf 100644 --- a/cmd/internal-tools/corpus-sweep/sweep.go +++ b/cmd/internal-tools/corpus-sweep/sweep.go @@ -17,23 +17,31 @@ import ( // resolveBinaries turns campaign and sanderling into absolute paths before // anything is served. Each campaign runs from the sweep's own directory, and a // binary that is missing altogether has to stop the sweep here rather than fail -// once per implementation and seed. +// once per implementation and seed. Every one that is missing is named +// together, in flag order: stopping at the first turns that single stop into +// one rerun per missing binary. func resolveBinaries(configuration *config) error { - for name, value := range map[string]*string{ - "--campaign": &configuration.campaignPath, - "--sanderling": &configuration.sanderlingPath, + var missing []error + for _, binary := range []struct { + name string + value *string + }{ + {"--campaign", &configuration.campaignPath}, + {"--sanderling", &configuration.sanderlingPath}, } { - resolved, err := exec.LookPath(*value) + resolved, err := exec.LookPath(*binary.value) if err != nil { - return fmt.Errorf("%s: %w", name, err) + missing = append(missing, fmt.Errorf("%s: %w", binary.name, err)) + continue } absolute, err := filepath.Abs(resolved) if err != nil { - return fmt.Errorf("%s: %w", name, err) + missing = append(missing, fmt.Errorf("%s: %w", binary.name, err)) + continue } - *value = absolute + *binary.value = absolute } - return nil + return errors.Join(missing...) } type sweep struct { diff --git a/cmd/internal-tools/corpus-sweep/sweep_test.go b/cmd/internal-tools/corpus-sweep/sweep_test.go new file mode 100644 index 0000000..151da43 --- /dev/null +++ b/cmd/internal-tools/corpus-sweep/sweep_test.go @@ -0,0 +1,46 @@ +package main + +import ( + "path/filepath" + "strings" + "testing" +) + +// Two binaries missing is one rerun, not two: the operator is told about both +// at once, in flag order, whatever order the check happened to walk. +func TestResolveBinaries_NamesEveryMissingBinaryInFlagOrder(t *testing.T) { + configuration := config{ + campaignPath: "campaign-that-is-not-installed", + sanderlingPath: "sanderling-that-is-not-installed", + } + + err := resolveBinaries(&configuration) + if err == nil { + t.Fatal("got no error, want both missing binaries named") + } + message := err.Error() + campaign := strings.Index(message, "--campaign") + sanderling := strings.Index(message, "--sanderling") + if campaign < 0 || sanderling < 0 { + t.Fatalf("got %q, want both --campaign and --sanderling named", message) + } + if campaign > sanderling { + t.Errorf("got %q, want --campaign named before --sanderling", message) + } + + resolved := config{ + campaignPath: writeScript( + t, + filepath.Join(t.TempDir(), "stub-campaign"), + "#!/bin/sh\nexit 0\n", + ), + sanderlingPath: "sanderling-that-is-not-installed", + } + err = resolveBinaries(&resolved) + if err == nil { + t.Fatal("got no error, want the missing sanderling named") + } + if strings.Contains(err.Error(), "--campaign") { + t.Errorf("got %q, want the campaign that resolved left out", err.Error()) + } +}