A stop for a walk that never built its modules (issue 474) kept saying "error: stopped" on the forge and in show after a later delivered merge carried its change to every machine. The stop stays final, as a covered superseded member stays superseded (issue 442); its status, view, note and show now say covered and by which merge, told once and from what is kept, so a restart or a stop covered before this tells it too. deliveries dropped such a stop after a day while stalled still listed it; it now lists it as long as stalled does. novox/hq issue 480, points 1 and 3.
135 lines
4.9 KiB
Go
135 lines
4.9 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// novox/hq issue 480: a delivery stopped because its walk ended done before its word without building its modules
|
|
// (issue 474) is still read for a later merge that delivers them, and kept CoveredBy once one did. Its state stays
|
|
// stopped — a final state never moves, as for a covered superseded member (issue 442) — but the forge's status, its
|
|
// view, its note and `show` went on saying "stopped" for a change that reached every machine. Once covered they say
|
|
// covered, and by which merge. And while `stalled` lists such a stop, `deliveries` lists it too, past a day.
|
|
|
|
// strandedNight is theNight with #215 stopped, its walk past the grace with nothing later carrying build-agent.
|
|
func strandedNight(t *testing.T) (*world, string) {
|
|
w := theNight(t)
|
|
id := IDOf(ctl474, head215)
|
|
w.later(strandedAfter + 5*time.Minute)
|
|
w.settleAll()
|
|
if d := w.delivery(id); d.State != Stopped || d.Stranded == "" {
|
|
t.Fatalf("#215 is %s, stranded %q", d.State, d.Stranded)
|
|
}
|
|
return w, id
|
|
}
|
|
|
|
func TestAStopALaterMergeCoversReadsCoveredByThatMerge(t *testing.T) {
|
|
w, id := strandedNight(t)
|
|
if got := statusOn(w, ctl474, head215); !strings.HasPrefix(got, "error stopped: ") {
|
|
t.Fatalf("stopped, its status says %q", got)
|
|
}
|
|
w.forge.contains[merge215+" in "+merge219] = true
|
|
by := deliverLater(w, ctl474, 219, "main", head219, merge219, w.now, walkDone, "build-agent")
|
|
w.later(2 * time.Minute)
|
|
w.settleAll()
|
|
d := w.delivery(id)
|
|
if d.State != Stopped || !strings.Contains(d.CoveredBy, by) {
|
|
t.Fatalf("after #219 it is %s, covered by %q", d.State, d.CoveredBy)
|
|
}
|
|
got := statusOn(w, ctl474, head215)
|
|
if !strings.HasPrefix(got, "success covered") || !strings.Contains(got, by) {
|
|
t.Fatalf("covered by %s, its status still says %q", by, got)
|
|
}
|
|
shown, err := w.h.Show(id)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if reads, _ := shown.(map[string]any)["reads"].(string); !strings.HasPrefix(reads, "covered") || !strings.Contains(reads, by) {
|
|
t.Fatalf("show reads %q", reads)
|
|
}
|
|
view := w.forge.views[ctl474+"#215"]
|
|
if !strings.Contains(view, "Covered") || !strings.Contains(view, by) {
|
|
t.Fatalf("its view does not say it was covered by %s:\n%s", by, view)
|
|
}
|
|
noted := false
|
|
for _, l := range w.forge.notesOn(merge215) {
|
|
noted = noted || strings.Contains(l, "covered") && strings.Contains(l, by)
|
|
}
|
|
if !noted {
|
|
t.Fatalf("its merge's note does not say it was covered: %v", w.forge.notesOn(merge215))
|
|
}
|
|
// Told once: further ticks owe nothing more.
|
|
w.later(time.Hour)
|
|
w.settleAll()
|
|
if d := w.delivery(id); len(d.Owed) != 0 || !d.CoverTold {
|
|
t.Fatalf("still owes %+v, told %v", d.Owed, d.CoverTold)
|
|
}
|
|
}
|
|
|
|
// A stop covered before this was built (CoveredBy kept, nothing told) is told after a restart, from what was kept.
|
|
func TestACoveredStopKeptBeforeIsToldAfterARestart(t *testing.T) {
|
|
w, id := strandedNight(t)
|
|
w.h.mu.Lock()
|
|
d := w.h.deliveries[id]
|
|
d.CoveredBy = IDOf(ctl474, head219) + " (merged as " + merge219[:8] + ")"
|
|
if err := w.store.PutDelivery(d); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w.h.mu.Unlock()
|
|
again := newWorldOver(t, w.store, w.ctl, w.forge, w.now.Add(time.Hour))
|
|
again.settleAll()
|
|
if got := statusOn(again, ctl474, head215); !strings.HasPrefix(got, "success covered") ||
|
|
!strings.Contains(got, IDOf(ctl474, head219)) {
|
|
t.Fatalf("after a restart its status says %q", got)
|
|
}
|
|
if d := again.delivery(id); d.State != Stopped || len(d.Transitions) != len(w.delivery(id).Transitions) {
|
|
t.Fatalf("a restart moved it: %s", d.State)
|
|
}
|
|
}
|
|
|
|
// Only a stop for a walk that never built its modules reads covered: a person's stop, or one the cover test has
|
|
// not answered, keeps its error.
|
|
func TestOnlyACoveredStrandedStopReadsCovered(t *testing.T) {
|
|
for _, d := range []*Delivery{
|
|
{State: Stopped, CoveredBy: "x"}, // a person's stop, with a cover found while it was held
|
|
{State: Stopped, Stranded: "never reached"}, // stranded, not covered
|
|
} {
|
|
if state, _ := statusOf(d); state != "error" {
|
|
t.Fatalf("%+v reads %s", d, state)
|
|
}
|
|
if d.coveredStop() {
|
|
t.Fatalf("%+v reads as a covered stop", d)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAStopStalledListsStaysInDeliveries(t *testing.T) {
|
|
w, id := strandedNight(t)
|
|
w.later(30 * time.Hour)
|
|
w.settleAll()
|
|
listed := func() *Line {
|
|
for _, l := range w.h.Deliveries("", "", "", false) {
|
|
if l.ID == id {
|
|
return &l
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
if len(w.h.Stalled()) != 1 {
|
|
t.Fatalf("stalled lists %+v", w.h.Stalled())
|
|
}
|
|
l := listed()
|
|
if l == nil || !l.Stalled || !strings.Contains(l.Waits, "build-agent") {
|
|
t.Fatalf("listed by stalled, deliveries shows %+v", l)
|
|
}
|
|
// Covered, it ends like any final delivery: a day after its stop.
|
|
w.forge.contains[merge215+" in "+merge219] = true
|
|
deliverLater(w, ctl474, 219, "main", head219, merge219, w.now, walkDone, "build-agent")
|
|
w.later(2 * time.Minute)
|
|
w.settleAll()
|
|
if len(w.h.Stalled()) != 0 || listed() != nil {
|
|
t.Fatalf("covered, stalled lists %+v and deliveries %+v", w.h.Stalled(), listed())
|
|
}
|
|
}
|