A change to mesh-build-go was judged by the installed module it replaces, so such pull requests were merged by hand. ADR 0535 §1, §2, §4, §5.
208 lines
8.0 KiB
Go
208 lines
8.0 KiB
Go
package check
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// The judged_by answer of mesh-build-go's digest and test steps (ADR 0535 §5; the milestone file's interface for issue
|
|
// 1898): "installed" for a step its installed program ran, "candidate <merge commit>" for one the candidate's program ran
|
|
// under runner workspace; an installed module older than the runner names nothing (it ignores the unknown argument: the
|
|
// SDK's tool server checks no argument against the input schema).
|
|
|
|
const buildGoFile = "modules/mesh-build-go/cmd/mesh-build-go/digest.go"
|
|
|
|
// candidateBench is a bench whose fetch changes the Go build module's directory, and whose build module runs the candidate
|
|
// when asked to (a module with the runner) or ignores the runner (one without).
|
|
func candidateBench(t *testing.T, takesRunner bool) *bench {
|
|
b := newBench(t)
|
|
b.fetch.Changed = []string{"README.md", buildGoFile}
|
|
judged := func(c stepCall) string {
|
|
if !takesRunner {
|
|
return ""
|
|
}
|
|
if c.args["runner"] == RunnerWorkspace {
|
|
return "candidate " + b.fetch.Merge
|
|
}
|
|
return "installed"
|
|
}
|
|
b.steps.handlers["build_go_digest"] = func(c stepCall) (any, error) {
|
|
d := b.digest[c.args["level"].(string)]
|
|
d.JudgedBy = judged(c)
|
|
return d, nil
|
|
}
|
|
b.test = func(c stepCall) (any, error) {
|
|
r := captured[Tested](t, "go-test-slow")
|
|
r.Level, _ = c.args["level"].(string)
|
|
r.JudgedBy = judged(c)
|
|
return r, nil
|
|
}
|
|
return b
|
|
}
|
|
|
|
func TestAMergeChangingTheGoBuildModuleIsAskedWithTheCandidateRunner(t *testing.T) { // 1898/AC1
|
|
b := candidateBench(t, true)
|
|
b.judge(t, caseOf(t, pullOf(catalogueRepository, 9, head1)))
|
|
asked := append(b.steps.tool("digest"), b.steps.tool("test")...)
|
|
if len(asked) < 3 {
|
|
t.Fatalf("asked %+v, want the digest and both levels", asked)
|
|
}
|
|
for _, c := range asked {
|
|
if c.args["runner"] != RunnerWorkspace {
|
|
t.Errorf("%s %v was asked with runner %v, want %s", c.tool, c.args["level"], c.args["runner"], RunnerWorkspace)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAMergeNotChangingTheGoBuildModuleIsAskedWithoutARunner(t *testing.T) { // 1898/AC1
|
|
for _, tc := range []struct {
|
|
name, repo, path string
|
|
}{
|
|
{"another module of the catalogue", catalogueRepository, "modules/mesh-build-node/cmd/mesh-build-node/main.go"},
|
|
{"a look-alike path in another repository", repoA, buildGoFile},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
b := candidateBench(t, true)
|
|
b.fetch.Changed = []string{tc.path}
|
|
out := b.judge(t, caseOf(t, pullOf(tc.repo, 9, head1)))
|
|
for _, c := range append(b.steps.tool("digest"), b.steps.tool("test")...) {
|
|
if _, ok := c.args["runner"]; ok {
|
|
t.Errorf("%s was asked with runner %v", c.tool, c.args["runner"])
|
|
}
|
|
}
|
|
if len(out.Judged) != 0 {
|
|
t.Errorf("judged = %v, want nothing said of the program", out.Judged)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAnInstalledModuleWithoutTheRunnerJudgesAsTodayAndTheLineSaysSo(t *testing.T) { // 1898/AC2
|
|
b := candidateBench(t, false)
|
|
cs := caseOf(t, pullOf(catalogueRepository, 9, head1))
|
|
out := b.judge(t, cs)
|
|
if out.Verdict.State != delivery.VerdictPass {
|
|
t.Fatalf("verdict = %+v", out.Verdict)
|
|
}
|
|
// As today: the digests key kept verdicts, and the levels are asked without the runner the module ignored.
|
|
for _, c := range b.steps.tool("test") {
|
|
if _, ok := c.args["runner"]; ok {
|
|
t.Errorf("the %v level was asked again with the runner the module does not take", c.args["level"])
|
|
}
|
|
}
|
|
if keys, _ := b.kept.Keys(context.Background()); len(keys) != 2 {
|
|
t.Errorf("kept %v, want the passing package at both levels, as today", keys)
|
|
}
|
|
p := cs.Pulls[0]
|
|
want := "judged by the installed mesh-build-go: it does not run a candidate yet"
|
|
if got := out.Judged[pullKey(p)]; got != want {
|
|
t.Fatalf("judged = %q, want %q", got, want)
|
|
}
|
|
if l := Line(out.Verdict, cs, p, out.Builds, out.Slowest, out.Judged); !strings.Contains(l, want) {
|
|
t.Errorf("line %q does not say %q", l, want)
|
|
}
|
|
}
|
|
|
|
func TestACandidateJudgedCheckTakesNoKeptVerdictAndWritesNone(t *testing.T) { // 1898/AC3
|
|
b := candidateBench(t, true)
|
|
if _, err := (Kept{Store: b.kept}).Keep(context.Background(), "unit", digA, keptValue("fixture/slow", 2)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out := b.judge(t, caseOf(t, pullOf(catalogueRepository, 9, head1)))
|
|
if out.Verdict.State != delivery.VerdictPass {
|
|
t.Fatalf("verdict = %+v", out.Verdict)
|
|
}
|
|
tests := b.steps.tool("test")
|
|
if len(tests) != 2 {
|
|
t.Fatalf("test calls = %+v, want both levels", tests)
|
|
}
|
|
for _, c := range tests {
|
|
if _, ok := c.args["packages"]; ok {
|
|
t.Errorf("the %v level was asked for %v, want it whole", c.args["level"], c.args["packages"])
|
|
}
|
|
}
|
|
if u := out.Verdict.Levels[delivery.LevelUnit]; u.Kept != 0 {
|
|
t.Errorf("unit = %+v, want nothing taken", u)
|
|
}
|
|
if keys, _ := b.kept.Keys(context.Background()); len(keys) != 1 || keys[0] != "unit."+digA {
|
|
t.Errorf("kept keys = %v, want only the one kept before", keys)
|
|
}
|
|
}
|
|
|
|
func TestTheLineNamesTheCandidateThatJudged(t *testing.T) { // 1898/AC6
|
|
b := candidateBench(t, true)
|
|
cs := caseOf(t, pullOf(catalogueRepository, 9, head1))
|
|
out := b.judge(t, cs)
|
|
p := cs.Pulls[0]
|
|
want := "judged by the candidate's mesh-build-go at " + b.fetch.Merge[:12]
|
|
for name, l := range map[string]string{
|
|
"line": Line(out.Verdict, cs, p, out.Builds, out.Slowest, out.Judged),
|
|
"report": Report(out.Verdict, cs, p, out.Builds, out.Slowest, out.Judged),
|
|
} {
|
|
if !strings.Contains(l, want) {
|
|
t.Errorf("the %s %q does not say %q", name, l, want)
|
|
}
|
|
}
|
|
// A long failing line keeps the program it was judged by; only the lesser parts go.
|
|
v := out.Verdict
|
|
v.State = delivery.VerdictFail
|
|
v.Failed = delivery.FailedFirst{Test: "unit: git.novox.be/novox/mesh-catalog/modules/mesh-build-go/cmd/mesh-build-go.TestADigestFoldsTheSiblings", Words: strings.Repeat("x", 60)}
|
|
if l := Line(v, cs, p, out.Builds, out.Slowest, out.Judged); !strings.Contains(l, "judged by the candidate's") {
|
|
t.Errorf("the long line %q dropped the program", l)
|
|
}
|
|
}
|
|
|
|
func TestInAGroupOnlyTheBuildModulesPullRequestNamesItsProgram(t *testing.T) { // 1898/AC6
|
|
b := candidateBench(t, true)
|
|
cs := caseOf(t, pullOf(catalogueRepository, 9, head1), pullOf(repoA, 7, head1))
|
|
out := b.judge(t, cs)
|
|
for _, p := range cs.Pulls {
|
|
l := Report(out.Verdict, cs, p, out.Builds, out.Slowest, out.Judged)
|
|
says := strings.Contains(l, "judged by")
|
|
if says != (p.Repository == catalogueRepository) {
|
|
t.Errorf("%s#%d: %q", p.Repository, p.Number, l)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestACandidateThatDoesNotCompileFailsTheCheckWithTheCompilersWords(t *testing.T) { // ADR 0535 §3, checker side
|
|
b := candidateBench(t, true)
|
|
refusal := "the candidate's mesh-build-go does not compile: modules/mesh-build-go/cmd/mesh-build-go/digest.go:12:2: undefined: x"
|
|
b.steps.handlers["build_go_digest"] = func(stepCall) (any, error) { return nil, errorString(refusal) }
|
|
b.test = func(stepCall) (any, error) { return nil, errorString(refusal) }
|
|
out := b.judge(t, caseOf(t, pullOf(catalogueRepository, 9, head1)))
|
|
if out.Verdict.State != delivery.VerdictFail || !strings.Contains(out.Verdict.Failed.Words, "undefined: x") {
|
|
t.Fatalf("verdict = %+v, want a failure naming the compile error", out.Verdict)
|
|
}
|
|
if n := len(b.steps.tool("test")); n != 1 {
|
|
t.Errorf("%d test steps, want the first one's failure to end the check", n)
|
|
}
|
|
}
|
|
|
|
type errorString string
|
|
|
|
func (e errorString) Error() string { return string(e) }
|
|
|
|
func TestTheStatusTheForgeShowsNamesTheProgramThatJudged(t *testing.T) { // 1898/AC6, the forge's line
|
|
ctx := context.Background()
|
|
words := judgedByCandidate + "fedcba987654"
|
|
j := &judgeFunc{f: func(context.Context, *Case, int) (Outcome, error) {
|
|
return Outcome{Verdict: passing(), Judged: map[string]string{repoA + "#7": words}}, nil
|
|
}}
|
|
r := newRig(t, j)
|
|
r.ch.Candidate(ctx, "feat/x", candidateOf(t, "feat/x", pullOf(repoA, 7, head1)))
|
|
r.ch.Wait()
|
|
key, _ := delivery.CheckerOwedKey(repoA, 7, head1)
|
|
raw, _, _, _ := r.owed.Get(ctx, key)
|
|
o, err := delivery.DecodeCheckerOwed(raw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if o.State != "success" || !strings.Contains(o.Text, words) {
|
|
t.Errorf("status %+v, want success saying %q", o, words)
|
|
}
|
|
}
|