The builder says wait-held for a key queued behind a package, the package as its part and on (package:<ref>), on its stream waits, and wait-ended published once the key is claimed; a toolchain wait never ends by that, and a package wait never by a toolchain's end. Files: internal/build/waits.go (Package, On, WaitPublished), internal/build/run.go (Hold at the queued result, End at the claim), cmd/mesh-delivery-builder/waits.go (Part in the body, the End rule). Builds: design 47 G A-192, §499 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
86 lines
2.9 KiB
Go
86 lines
2.9 KiB
Go
package build
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
|
|
)
|
|
|
|
// memWaits records the waits held and ended.
|
|
type memWaits struct {
|
|
mu sync.Mutex
|
|
held map[string]ToolchainWait
|
|
ended []string
|
|
}
|
|
|
|
func (w *memWaits) Hold(_ context.Context, t ToolchainWait) {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
if w.held == nil {
|
|
w.held = map[string]ToolchainWait{}
|
|
}
|
|
w.held[t.Module+"."+t.Fingerprint] = t
|
|
}
|
|
|
|
func (w *memWaits) End(_ context.Context, module, fp, how string) bool {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
k := module + "." + fp
|
|
t, ok := w.held[k]
|
|
if !ok || (t.Package != "") != (how == WaitPublished) { // as the builder's waitsKept ends them: a package wait by its publishing alone
|
|
return false
|
|
}
|
|
delete(w.held, k)
|
|
w.ended = append(w.ended, how)
|
|
return true
|
|
}
|
|
|
|
// Design 47 G §672, §267, §276: an entry whose fingerprint names a toolchain version other than the agreed one is held,
|
|
// built and ended `ran` when the agreed toolchain moves to that version; and one held is built with the running build
|
|
// module by Release and ended `released`.
|
|
func TestAnEntryWhoseFingerprintNamesAnotherToolchainWaitsAndIsBuiltWhenItIsAgreedOrReleased(t *testing.T) {
|
|
old := map[string]builds.ToolchainUsed{"go": {Version: "go1.26.0", Recipe: "1"}}
|
|
t.Run("ran", func(t *testing.T) {
|
|
r := newRig(t, goBundle)
|
|
w := &memWaits{}
|
|
r.b.Waits = w
|
|
r.b.Module(context.Background(), "m", moduleValueWith(t, "modules/m", old))
|
|
r.b.Wait()
|
|
if len(w.held) != 1 || len(r.st.vals) != 0 {
|
|
t.Fatalf("held %v, keys %v: want one wait and nothing written", w.held, r.st.vals)
|
|
}
|
|
for _, h := range w.held {
|
|
if h.On() != "toolchain:go@go1.26.0" {
|
|
t.Errorf("the wait is on %s", h.On())
|
|
}
|
|
}
|
|
r.tools.Set("ace", tcOf("go", "go1.26.0"))
|
|
r.tools.Set("novox", tcOf("go", "go1.26.0"))
|
|
r.b.Reconsider(context.Background())
|
|
r.b.Wait()
|
|
if v := valueOf(t, r.st, r.key); v.State != builds.BuildDone || len(w.held) != 0 || len(w.ended) != 1 || w.ended[0] != WaitRan {
|
|
t.Errorf("build %+v, held %v, ended %v: want done and one wait ended ran", v, w.held, w.ended)
|
|
}
|
|
})
|
|
t.Run("released", func(t *testing.T) {
|
|
r := newRig(t, goBundle)
|
|
w := &memWaits{}
|
|
r.b.Waits = w
|
|
v := moduleValueWith(t, "modules/m", old)
|
|
r.b.Module(context.Background(), "m", v)
|
|
r.b.Wait()
|
|
if why := r.b.Release(context.Background(), "m", fp1, "the build module runs", "operator"); why != "" {
|
|
t.Fatalf("release refused: %s", why)
|
|
}
|
|
r.b.Wait()
|
|
if got := valueOf(t, r.st, r.key); got.State != builds.BuildDone || got.Toolchains["go"].Version != "go1.27.1" || len(w.ended) != 1 || w.ended[0] != WaitReleased {
|
|
t.Errorf("build %+v, ended %v: want done with the running toolchain and a wait ended released", got, w.ended)
|
|
}
|
|
if why := r.b.Release(context.Background(), "m", fp1, "again", "operator"); why == "" {
|
|
t.Error("a second release of an entry that is not held was taken")
|
|
}
|
|
})
|
|
}
|