mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 20:17:09 +00:00
feat(corpus-sweep): run one specification against a served corpus of implementations
same fixed campaign as implementation-sweep, over a corpus that needs no build. each implementation gets its own port: the corpus holds pairs that write the same localStorage key, and one shared origin is one stored record shared between them.
This commit is contained in:
1 parent
146152a3af
commit
341d6a0614
9 files changed
+2103
No files matched your search
@@ -0,0 +1,132 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
manifestFileName = "sweep.json"
|
||||
recordsFileName = "implementations.jsonl"
|
||||
)
|
||||
|
||||
// plannedImplementation is one implementation the sweep intends to run, with
|
||||
// the origin that keeps its stored state its own and the URL every seed is
|
||||
// driven at.
|
||||
type plannedImplementation struct {
|
||||
Name string `json:"name"`
|
||||
Document string `json:"document"`
|
||||
Port int `json:"port"`
|
||||
Origin string `json:"origin"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// manifest is sweep.json: what the sweep INTENDED to run, written before the
|
||||
// first run so a host that dropped an implementation or a seed shows up as a
|
||||
// missing run rather than as a smaller sample.
|
||||
type manifest struct {
|
||||
Generator string `json:"generator"`
|
||||
Platform string `json:"platform"`
|
||||
SpecPath string `json:"spec_path"`
|
||||
CorpusRoot string `json:"corpus_root"`
|
||||
CorpusCommit string `json:"corpus_commit"`
|
||||
MaxSteps int `json:"max_steps"`
|
||||
DurationMillis int64 `json:"duration_millis"`
|
||||
Seeds []int64 `json:"seeds"`
|
||||
Implementations []plannedImplementation `json:"implementations"`
|
||||
Concurrency int `json:"concurrency"`
|
||||
Host string `json:"host"`
|
||||
CampaignPath string `json:"campaign_path"`
|
||||
SanderlingPath string `json:"sanderling_path"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
}
|
||||
|
||||
func buildManifest(
|
||||
configuration config,
|
||||
implementations []implementation,
|
||||
host string,
|
||||
startedAt time.Time,
|
||||
) manifest {
|
||||
planned := make([]plannedImplementation, 0, len(implementations))
|
||||
for _, target := range implementations {
|
||||
planned = append(planned, plannedImplementation{
|
||||
Name: target.Name,
|
||||
Document: target.Document,
|
||||
Port: target.Port,
|
||||
Origin: target.Origin(),
|
||||
URL: target.URL(),
|
||||
})
|
||||
}
|
||||
return manifest{
|
||||
Generator: generator,
|
||||
Platform: platform,
|
||||
SpecPath: configuration.specPath,
|
||||
CorpusRoot: configuration.corpusRoot,
|
||||
CorpusCommit: corpusCommit(configuration.corpusRoot),
|
||||
MaxSteps: configuration.maxSteps,
|
||||
DurationMillis: configuration.duration.Milliseconds(),
|
||||
Seeds: configuration.seeds,
|
||||
Implementations: planned,
|
||||
Concurrency: configuration.concurrency,
|
||||
Host: host,
|
||||
CampaignPath: configuration.campaignPath,
|
||||
SanderlingPath: configuration.sanderlingPath,
|
||||
StartedAt: startedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// corpusCommit records which checkout was swept. It is empty rather than fatal
|
||||
// for a corpus that is not a git working tree, because the population check has
|
||||
// already established the corpus holds the right examples.
|
||||
func corpusCommit(corpusRoot string) string {
|
||||
command := exec.Command("git", "-C", corpusRoot, "rev-parse", "HEAD")
|
||||
output, err := command.Output()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(output))
|
||||
}
|
||||
|
||||
func writeManifest(directory string, value manifest) error {
|
||||
body, err := json.MarshalIndent(value, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal manifest: %w", err)
|
||||
}
|
||||
return os.WriteFile(
|
||||
filepath.Join(directory, manifestFileName),
|
||||
append(body, '\n'),
|
||||
0o644,
|
||||
)
|
||||
}
|
||||
|
||||
// runRecord is one campaign, which is one implementation at one seed.
|
||||
type runRecord struct {
|
||||
Seed int64 `json:"seed"`
|
||||
ExitCode int `json:"exit_code"`
|
||||
LaunchError string `json:"launch_error,omitempty"`
|
||||
CampaignDirectory string `json:"campaign_directory"`
|
||||
MonotonicMillis int64 `json:"monotonic_millis"`
|
||||
}
|
||||
|
||||
// implementationRecord is one line of implementations.jsonl. FailedStage names
|
||||
// the step that stopped this implementation, and one that never got served
|
||||
// carries no runs at all.
|
||||
type implementationRecord struct {
|
||||
Name string `json:"implementation"`
|
||||
Document string `json:"document"`
|
||||
Port int `json:"port"`
|
||||
Origin string `json:"origin"`
|
||||
URL string `json:"url"`
|
||||
FailedStage string `json:"failed_stage,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
MonotonicMillis int64 `json:"monotonic_millis"`
|
||||
Runs []runRecord `json:"runs"`
|
||||
}
|
||||
|
||||
const stageServe = "serve"
|
||||
Reference in new issue
Block a user