mesh/merge-gate head fbbf28d21c37 merged into trunk c3d3194bc8d4 — pass: builds mesh-delivery-gate → novox; no bus step; every machine composes with the ch…
mesh/repo-check head fbbf28d21c37 merged into trunk c3d3194bc8d4 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
mesh/check failed: unit: modules/gitea.test: [gitea] where the poll of merges last looked from could not be read, so none is kept — no runtime | fbbf2…
154 lines
5.8 KiB
Go
154 lines
5.8 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// Issue 1274: a proposed delivery whose asked check the build queue no longer holds is asked again, and a recheck from
|
|
// proposed is taken when no check of its head is waiting or in flight.
|
|
|
|
// 1274/AC1: the queue holds nothing for the ask, the sweep runs after the bound: the check is asked once more and the
|
|
// transition says the earlier ask was lost.
|
|
func TestACheckTheQueueLostIsAskedAgainBySweepAfterTheBound(t *testing.T) {
|
|
w := newWorld(t)
|
|
id := IDOf("novox/app", head)
|
|
w.h.PullUpdated(pr("novox/app", 7, head, "feat/x", "src/a.go"))
|
|
w.settleAll()
|
|
first := w.delivery(id).CheckAsk
|
|
if len(w.ctl.checks) != 1 || first == nil {
|
|
t.Fatalf("asked %v, ask %+v", w.ctl.checks, first)
|
|
}
|
|
w.ctl.queue = nil // the queue lost it
|
|
w.later(9 * time.Minute)
|
|
w.settleAll()
|
|
if len(w.ctl.checks) != 1 {
|
|
t.Fatalf("asked again inside the bound: %v", w.ctl.checks)
|
|
}
|
|
w.later(2 * time.Minute)
|
|
w.settleAll()
|
|
d := w.delivery(id)
|
|
last := d.Transitions[len(d.Transitions)-1]
|
|
if len(w.ctl.checks) != 2 || d.CheckAsk == nil || d.CheckAsk.ID == first.ID || last.Event != EvLost ||
|
|
!strings.Contains(last.Why, first.ID) || !strings.Contains(last.Why, "lost") {
|
|
t.Fatalf("asked %v; ask %+v; last transition %+v", w.ctl.checks, d.CheckAsk, last)
|
|
}
|
|
w.later(time.Hour)
|
|
w.settleAll()
|
|
if len(w.ctl.checks) != 2 {
|
|
t.Fatalf("an ask the queue holds was asked again: %v", w.ctl.checks)
|
|
}
|
|
}
|
|
|
|
// 1274/AC1: a verdict that went out of date, its check against the trunk's head asked and lost, is asked again too (the
|
|
// case of novox/mesh-controller@28a278c1462a), and the ask's bound runs from the new ask.
|
|
func TestACheckAgainstTheTrunkTheQueueLostIsAskedAgain(t *testing.T) {
|
|
w := approvedGroup(t)
|
|
id := IDOf(gCtl, gHc)
|
|
w.h.PullMerged(merged(gCtl, 5, "5555555555555555", gT1))
|
|
w.settleAll()
|
|
d := w.delivery(id)
|
|
if d.State != Proposed || d.AskedAgain == nil {
|
|
t.Fatalf("it is %s, asked again %v", d.State, d.AskedAgain)
|
|
}
|
|
asked := len(w.ctl.checks)
|
|
w.ctl.queue = nil
|
|
w.later(11 * time.Minute)
|
|
w.settleAll()
|
|
d = w.delivery(id)
|
|
last := d.Transitions[len(d.Transitions)-1]
|
|
if len(w.ctl.checks) != asked+1 || last.Event != EvLost || d.AskedAgain == nil || !d.AskedAgain.After(w.now.Add(-time.Minute)) {
|
|
t.Fatalf("asked %v; last transition %+v; asked again %v", w.ctl.checks[asked:], last, d.AskedAgain)
|
|
}
|
|
}
|
|
|
|
// 1274/AC2: the queue still holds the ask, waiting or in flight, or cannot be read: nothing is asked again.
|
|
func TestACheckTheQueueStillHoldsIsNotAskedAgain(t *testing.T) {
|
|
w := newWorld(t)
|
|
id := IDOf("novox/app", head)
|
|
w.h.PullUpdated(pr("novox/app", 7, head, "feat/x", "src/a.go"))
|
|
w.settleAll()
|
|
w.ctl.queue[0].State = "in flight"
|
|
w.later(3 * time.Hour)
|
|
w.settleAll()
|
|
if len(w.ctl.checks) != 1 {
|
|
t.Fatalf("an ask in flight was asked again: %v", w.ctl.checks)
|
|
}
|
|
w.ctl.queue = nil
|
|
w.ctl.queueDown = true
|
|
w.later(time.Hour)
|
|
w.settleAll()
|
|
if len(w.ctl.checks) != 1 || w.delivery(id).State != Proposed {
|
|
t.Fatalf("an unreadable queue asked again: %v", w.ctl.checks)
|
|
}
|
|
w.ctl.queueDown = false
|
|
w.later(3 * time.Minute)
|
|
w.ctl.queue = []QueuedAsk{{ID: "elsewhere", State: "dead", Kind: "check", Repository: "novox/app", Commit: head}}
|
|
w.settleAll()
|
|
if len(w.ctl.checks) != 2 {
|
|
t.Fatalf("a dead ask counted as held: %v", w.ctl.checks)
|
|
}
|
|
}
|
|
|
|
// 1274/AC3: recheck of a proposed delivery with no check waiting or in flight is taken and asks its check; with one
|
|
// held it is refused naming it.
|
|
func TestARecheckFromProposedIsTakenOnlyWhenNoCheckOfItsHeadIsHeld(t *testing.T) {
|
|
w := newWorld(t)
|
|
id := IDOf("novox/app", head)
|
|
w.h.PullUpdated(pr("novox/app", 7, head, "feat/x", "src/a.go"))
|
|
w.settleAll()
|
|
held := w.delivery(id).CheckAsk.ID
|
|
if _, err := w.h.Recheck(id, "nothing came", "jochen"); err == nil || !strings.Contains(err.Error(), held) {
|
|
t.Fatalf("a recheck with a check waiting: %v", err)
|
|
}
|
|
if _, err := w.h.Recheck(id, "", "jochen"); err == nil {
|
|
t.Fatal("a recheck with no why was taken")
|
|
}
|
|
if len(w.ctl.checks) != 1 {
|
|
t.Fatalf("a refused recheck asked: %v", w.ctl.checks)
|
|
}
|
|
w.ctl.queue = nil
|
|
said, err := w.h.Recheck(id, "nothing came", "jochen")
|
|
d := w.delivery(id)
|
|
last := d.Transitions[len(d.Transitions)-1]
|
|
if err != nil || len(w.ctl.checks) != 2 || d.State != Proposed || last.Event != EvLost || last.By != "jochen" ||
|
|
!strings.Contains(last.Why, "nothing came") || d.CheckAsk.ID != "check-2" || !strings.Contains(said, "check-2") {
|
|
t.Fatalf("%q %v; asked %v; last transition %+v", said, err, w.ctl.checks, last)
|
|
}
|
|
// Taken, it is now held: a second recheck is refused.
|
|
if _, err := w.h.Recheck(id, "again", "jochen"); err == nil || !strings.Contains(err.Error(), "check-2") {
|
|
t.Fatalf("a second recheck: %v", err)
|
|
}
|
|
}
|
|
|
|
// 1274/AC4: a group member re-asked this way has its group's composed check asked again at the members' heads once its
|
|
// own check answers.
|
|
func TestAGroupMemberReaskedAfterALostCheckHasTheComposedCheckAskedAgain(t *testing.T) {
|
|
w := approvedGroup(t)
|
|
id := IDOf(gCat, gHk)
|
|
if _, err := w.h.Recheck(id, "its verdict is wanted again", "jochen"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w.settleAll() // its own check is recorded as asked
|
|
w.h.mu.Lock()
|
|
w.h.groups["feat/g"].Check.OutOfDate = "" // as if the composed check stood: only the lost ask's own act marks it
|
|
w.h.mu.Unlock()
|
|
w.ctl.queue = nil
|
|
w.later(11 * time.Minute)
|
|
w.settleAll()
|
|
g := w.h.groups["feat/g"]
|
|
if g.Check.OutOfDate == "" || !strings.Contains(g.Check.OutOfDate, "lost") {
|
|
t.Fatalf("the composed check stands: %+v", g.Check)
|
|
}
|
|
first := g.Check.Asked
|
|
asked := len(w.ctl.checks)
|
|
w.later(time.Minute)
|
|
w.h.Checked(judged(gCat, 2, gHk, "pass", gTrunk0))
|
|
w.settleAll()
|
|
last := w.ctl.checks[len(w.ctl.checks)-1]
|
|
if len(w.ctl.checks) <= asked || !strings.HasPrefix(last, "feat/g:") || w.h.groups["feat/g"].Check.Asked == first {
|
|
t.Fatalf("the composed check was not asked again: %v", w.ctl.checks[asked:])
|
|
}
|
|
}
|