127 lines
3.9 KiB
Go
127 lines
3.9 KiB
Go
package buildrun
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestParseCapacityReadsNodeEqualsCountPairs(t *testing.T) {
|
|
c, err := ParseCapacity(" shanks=3, ace=2 ")
|
|
if err != nil || c["shanks"] != 3 || c["ace"] != 2 || len(c) != 2 {
|
|
t.Errorf("capacity = %v, %v", c, err)
|
|
}
|
|
if c, err := ParseCapacity(""); err != nil || len(c) != 0 {
|
|
t.Errorf("empty: %v, %v", c, err)
|
|
}
|
|
for _, bad := range []string{"shanks", "shanks=x", "=2", "a=1,a=2", "shanks=-1"} {
|
|
if _, err := ParseCapacity(bad); err == nil {
|
|
t.Errorf("%q accepted", bad)
|
|
}
|
|
}
|
|
}
|
|
|
|
func acquireAsync(p *Places, node string) chan func() {
|
|
got := make(chan func(), 1)
|
|
go func() {
|
|
release, err := p.Acquire(context.Background(), node)
|
|
if err != nil {
|
|
close(got)
|
|
return
|
|
}
|
|
got <- release
|
|
}()
|
|
return got
|
|
}
|
|
|
|
func TestThreeStepsOnANodeOfCapacityTwoRunTwoAtOnceAndTheThirdStartsWhenOneEnds(t *testing.T) { // round 3
|
|
p := &Places{Capacity: map[string]int{"ace": 2}}
|
|
a, b := acquireAsync(p, "ace"), acquireAsync(p, "ace")
|
|
ra, rb := <-a, <-b
|
|
third := acquireAsync(p, "ace")
|
|
select {
|
|
case <-third:
|
|
t.Fatal("a third step started on a node of capacity 2 with two running")
|
|
case <-time.After(10 * time.Millisecond):
|
|
}
|
|
if p.Running()["ace"] != 2 {
|
|
t.Errorf("running = %v, want 2 on ace", p.Running())
|
|
}
|
|
ra()
|
|
select {
|
|
case r := <-third:
|
|
r()
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("the third step did not start when one ended")
|
|
}
|
|
rb()
|
|
}
|
|
|
|
func TestANodeWithNoSettingRunsOneStepAtATime(t *testing.T) { // round 3
|
|
p := &Places{Capacity: map[string]int{"shanks": 3}}
|
|
first := <-acquireAsync(p, "g14")
|
|
second := acquireAsync(p, "g14")
|
|
select {
|
|
case <-second:
|
|
t.Fatal("two steps on a node without a setting")
|
|
case <-time.After(10 * time.Millisecond):
|
|
}
|
|
first()
|
|
r := <-second
|
|
r()
|
|
if p.CapacityOf("g14") != 1 || p.CapacityOf("shanks") != 3 {
|
|
t.Errorf("capacities = %d, %d", p.CapacityOf("g14"), p.CapacityOf("shanks"))
|
|
}
|
|
}
|
|
|
|
func TestAWaitingStepStopsWhenItsContextEnds(t *testing.T) { // round 3
|
|
p := &Places{}
|
|
r := <-acquireAsync(p, "ace")
|
|
defer r()
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
done := make(chan error, 1)
|
|
go func() { _, err := p.Acquire(ctx, "ace"); done <- err }()
|
|
cancel()
|
|
select {
|
|
case err := <-done:
|
|
if err == nil {
|
|
t.Error("Acquire returned no error for a cancelled wait")
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("a cancelled wait did not return")
|
|
}
|
|
}
|
|
|
|
// The operator's decision of 2026-10-21 13:40: the builder builds on shanks and ace only; a capacity of 0 keeps it off a node.
|
|
func TestACapacityOfZeroKeepsThePartOffTheNodeAndANodeNotNamedKeepsOne(t *testing.T) { // issue 1661
|
|
c, err := ParseCapacity("shanks=3,ace=2,g14=0,novox=0")
|
|
if err != nil || c["g14"] != 0 || c["novox"] != 0 || c["shanks"] != 3 || len(c) != 4 {
|
|
t.Fatalf("capacity = %v, %v", c, err)
|
|
}
|
|
p := &Places{Capacity: c}
|
|
if p.CapacityOf("g14") != 0 || p.CapacityOf("novox") != 0 || p.CapacityOf("ace") != 2 || p.CapacityOf("other") != DefaultCapacity {
|
|
t.Errorf("capacities = %d %d %d %d, want 0 0 2 and the default for a node not named", p.CapacityOf("g14"), p.CapacityOf("novox"), p.CapacityOf("ace"), p.CapacityOf("other"))
|
|
}
|
|
if _, err := ParseCapacity("shanks=-1"); err == nil {
|
|
t.Error("a negative capacity accepted")
|
|
}
|
|
entries := tcs("go", "go1.27.1", "ace", "g14", "novox", "shanks", "other")
|
|
for i := 0; i < 6; i++ {
|
|
nodes, _, err := ChooseNodes("go", entries, p, nil, 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, n := range nodes {
|
|
if n == "g14" || n == "novox" {
|
|
t.Fatalf("choice %d: %v includes a node of capacity 0", i, nodes)
|
|
}
|
|
}
|
|
}
|
|
// Only nodes of capacity 0 report: nothing qualifies, and the build says why.
|
|
_, _, err = ChooseNodes("go", tcs("go", "go1.27.1", "g14", "novox"), p, nil, 2)
|
|
if err == nil || !strings.Contains(err.Error(), "capacity") {
|
|
t.Errorf("only capacity-0 nodes: error = %v, want it to name the capacity", err)
|
|
}
|
|
}
|