Merge pull request 'power: its loop and boot tests run on a fake clock, so a busy build node cannot fail them (issue 1126)' (#551) from fix/power-loop-flake into main
This commit was merged in pull request #551.
This commit is contained in:
@@ -271,13 +271,12 @@ func (m *Machine) Publish(r Reading) error {
|
|||||||
|
|
||||||
// RunDraw samples and publishes until the context ends.
|
// RunDraw samples and publishes until the context ends.
|
||||||
func RunDraw(ctx context.Context, m *Machine) error {
|
func RunDraw(ctx context.Context, m *Machine) error {
|
||||||
return runDraw(ctx, m, SampleEvery, m.Publish)
|
return runDraw(ctx, m, SampleEvery, sleepFor(ctx), m.Publish)
|
||||||
}
|
}
|
||||||
|
|
||||||
// runDraw publishes one reading every `every`. A battery reading is instant, so it waits the interval
|
// sleepFor waits d on the real clock, or less when ctx ends first; it reports whether the full wait passed.
|
||||||
// itself; a counter reading waits inside its sample. It returns as soon as the context ends.
|
func sleepFor(ctx context.Context) func(time.Duration) bool {
|
||||||
func runDraw(ctx context.Context, m *Machine, every time.Duration, publish func(Reading) error) error {
|
return func(d time.Duration) bool {
|
||||||
wait := func(d time.Duration) bool {
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return false
|
return false
|
||||||
@@ -285,6 +284,12 @@ func runDraw(ctx context.Context, m *Machine, every time.Duration, publish func(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// runDraw publishes one reading every `every`. A battery reading is instant, so it waits the interval
|
||||||
|
// itself; a counter reading waits inside its sample. The wait is injected so a test drives the clock;
|
||||||
|
// it returns false when the loop must stop. It returns as soon as the context ends.
|
||||||
|
func runDraw(ctx context.Context, m *Machine, every time.Duration, wait func(time.Duration) bool, publish func(Reading) error) error {
|
||||||
for ctx.Err() == nil {
|
for ctx.Err() == nil {
|
||||||
d := m.Sample(ctx, every, wait)
|
d := m.Sample(ctx, every, wait)
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
|
|||||||
@@ -187,24 +187,25 @@ func TestTheLoopPublishesEveryIntervalAndStopsWhenCancelled(t *testing.T) {
|
|||||||
"/sys/class/power_supply/BAT0/status": "Discharging",
|
"/sys/class/power_supply/BAT0/status": "Discharging",
|
||||||
"/sys/class/power_supply/BAT0/power_now": "12000000",
|
"/sys/class/power_supply/BAT0/power_now": "12000000",
|
||||||
})
|
})
|
||||||
|
// A fake clock: each wait returns at once; the tenth cancels the context, as the end of the run.
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
var lines []string
|
var lines []string
|
||||||
done := make(chan error)
|
waits := 0
|
||||||
go func() {
|
wait := func(d time.Duration) bool {
|
||||||
done <- runDraw(ctx, m, 20*time.Millisecond, func(r Reading) error { lines = append(lines, r.Text); return nil })
|
if d != 20*time.Millisecond {
|
||||||
}()
|
t.Errorf("waited %v, want the 20 ms interval", d)
|
||||||
time.Sleep(210 * time.Millisecond)
|
|
||||||
cancel()
|
|
||||||
select {
|
|
||||||
case err := <-done:
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
case <-time.After(time.Second):
|
if waits++; waits == 10 {
|
||||||
t.Fatal("the loop did not stop when cancelled")
|
cancel()
|
||||||
|
}
|
||||||
|
return ctx.Err() == nil
|
||||||
}
|
}
|
||||||
// One at once, then one every 20 ms: about eleven in 210 ms, never a burst, never a stall.
|
if err := runDraw(ctx, m, 20*time.Millisecond, wait, func(r Reading) error { lines = append(lines, r.Text); return nil }); err != nil {
|
||||||
if len(lines) < 6 || len(lines) > 12 || lines[0] != "12.0 W machine" {
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// One at once, then one after each of the nine waits that passed; the tenth wait ended the loop.
|
||||||
|
if len(lines) != 10 || lines[0] != "12.0 W machine" {
|
||||||
t.Fatalf("%d lines: %v", len(lines), lines)
|
t.Fatalf("%d lines: %v", len(lines), lines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,11 +213,24 @@ func TestAMachineWithoutMainsSaysNothingAboutSupplies(t *testing.T) {
|
|||||||
func TestRunWithoutLogindStillSaysTheBoot(t *testing.T) {
|
func TestRunWithoutLogindStillSaysTheBoot(t *testing.T) {
|
||||||
m := machine(t, map[string]string{"/proc/sys/kernel/random/boot_id": "xyz\n"})
|
m := machine(t, map[string]string{"/proc/sys/kernel/random/boot_id": "xyz\n"})
|
||||||
b := &bus{}
|
b := &bus{}
|
||||||
w := NewWatcher(m, b.emit, func() (Login1, error) { return nil, errors.New("no system bus") })
|
// No timeout racing the loop: the run ends once logind was tried and the boot was published,
|
||||||
w.state = filepath.Join(t.TempDir(), "b")
|
// both of which happen in Run's own goroutine.
|
||||||
w.poll = 10 * time.Millisecond
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
dialed, emitted := false, false
|
||||||
|
stopWhenBoth := func() {
|
||||||
|
if dialed && emitted {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w := NewWatcher(m, func(typ string, body any) error {
|
||||||
|
err := b.emit(typ, body)
|
||||||
|
emitted = true
|
||||||
|
stopWhenBoth()
|
||||||
|
return err
|
||||||
|
}, func() (Login1, error) { dialed = true; defer stopWhenBoth(); return nil, errors.New("no system bus") })
|
||||||
|
w.state = filepath.Join(t.TempDir(), "b")
|
||||||
|
w.poll = time.Millisecond
|
||||||
w.Run(ctx)
|
w.Run(ctx)
|
||||||
if got := b.seen(); len(got) == 0 || got[0] != Booted || w.Snapshot().Problem == "" {
|
if got := b.seen(); len(got) == 0 || got[0] != Booted || w.Snapshot().Problem == "" {
|
||||||
t.Fatalf("%v %+v", got, w.Snapshot())
|
t.Fatalf("%v %+v", got, w.Snapshot())
|
||||||
|
|||||||
Reference in New Issue
Block a user