test(app): wait for the maintainer to be idle instead of sleeping
- commit
- ee9ce2c9fec92017ea44f6e1212e73b9c7fe8ae1
- author
- brice · · 2026-10-10 11:16 UTC
- parent
- fcf895d
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +17 -6
A internal/app/export_test.go +11 -0
| @@ -0,0 +1,11 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package app | |
| 4 | + | ||
| 5 | + | // Idle reports whether nothing is pending or running: tests wait for it | |
| 6 | + | // instead of sleeping. | |
| 7 | + | func (m *Maintainer) Idle() bool { | |
| 8 | + | m.mu.Lock() | |
| 9 | + | defer m.mu.Unlock() | |
| 10 | + | return len(m.entries) == 0 | |
| 11 | + | } | |
| @@ -0,0 +1,11 @@ | |||||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |||
| 2 | + | ||||
| 3 | + | package app | |||
| 4 | + | ||||
| 5 | + | // Idle reports whether nothing is pending or running: tests wait for it | |||
| 6 | + | // instead of sleeping. | |||
| 7 | + | func (m *Maintainer) Idle() bool { | |||
| 8 | + | m.mu.Lock() | |||
| 9 | + | defer m.mu.Unlock() | |||
| 10 | + | return len(m.entries) == 0 | |||
| 11 | + | } | |||
M internal/app/maintenance_test.go +6 -6
| @@ -5,6 +5,7 @@ package app_test | |||
| 5 | 5 | import ( | |
| 6 | 6 | "context" | |
| 7 | 7 | "errors" | |
| 8 | + | "runtime" | |
| 8 | 9 | "sync" | |
| 9 | 10 | "sync/atomic" | |
| 10 | 11 | "testing" | |
| @@ -37,16 +38,16 @@ func TestMaintainerDebounces(t *testing.T) { | |||
| 37 | 38 | runs.Add(1) | |
| 38 | 39 | return nil | |
| 39 | 40 | }) | |
| 40 | - | m := app.NewMaintainer(ctx, git, 50*time.Millisecond) | |
| 41 | + | m := app.NewMaintainer(ctx, git, 200*time.Millisecond) | |
| 41 | 42 | defer m.Close(ctx) | |
| 42 | 43 | ||
| 43 | 44 | for range 10 { | |
| 44 | 45 | m.Schedule(ctx, n) | |
| 45 | 46 | } | |
| 46 | - | time.Sleep(10 * time.Millisecond) | |
| 47 | 47 | assert.Equal(t, int32(0), runs.Load(), "waits for the delay") | |
| 48 | 48 | waitFor(t, func() bool { return runs.Load() == 1 }, "one run") | |
| 49 | - | time.Sleep(150 * time.Millisecond) | |
| 49 | + | // Idle: nothing is pending or running, so no second run is coming. | |
| 50 | + | waitFor(t, m.Idle, "the maintainer to be idle") | |
| 50 | 51 | assert.Equal(t, int32(1), runs.Load(), "a burst makes one run") | |
| 51 | 52 | ||
| 52 | 53 | m.Schedule(ctx, n) | |
| @@ -86,13 +87,12 @@ func TestMaintainerOneRunAtATimePerRepo(t *testing.T) { | |||
| 86 | 87 | m.Schedule(ctx, n) | |
| 87 | 88 | m.Schedule(ctx, other) | |
| 88 | 89 | waitFor(t, otherRan.Load, "another repository is not blocked") | |
| 89 | - | time.Sleep(60 * time.Millisecond) | |
| 90 | 90 | assert.Equal(t, int32(1), runs.Load(), "the second run waits for the first") | |
| 91 | 91 | ||
| 92 | 92 | release <- struct{}{} // finish first run | |
| 93 | 93 | waitFor(t, func() bool { return runs.Load() == 2 }, "queued run") | |
| 94 | 94 | release <- struct{}{} | |
| 95 | - | time.Sleep(60 * time.Millisecond) | |
| 95 | + | waitFor(t, m.Idle, "the maintainer to be idle") | |
| 96 | 96 | assert.Equal(t, int32(2), runs.Load(), "exactly one queued run") | |
| 97 | 97 | assert.Equal(t, int32(1), maxConcurrent.Load()) | |
| 98 | 98 | } | |
| @@ -163,7 +163,7 @@ func TestMaintainerConcurrentSchedule(t *testing.T) { | |||
| 163 | 163 | defer wg.Done() | |
| 164 | 164 | for range 50 { | |
| 165 | 165 | m.Schedule(ctx, name(t, string(rune('a'+i%3)))) | |
| 166 | - | time.Sleep(100 * time.Microsecond) | |
| 166 | + | runtime.Gosched() | |
| 167 | 167 | } | |
| 168 | 168 | }() | |
| 169 | 169 | } | |
| @@ -5,6 +5,7 @@ package app_test | |||||
| 5 | import ( | 5 | import ( | ||
| 6 | "context" | 6 | "context" | ||
| 7 | "errors" | 7 | "errors" | ||
| 8 | + | "runtime" | |||
| 8 | "sync" | 9 | "sync" | ||
| 9 | "sync/atomic" | 10 | "sync/atomic" | ||
| 10 | "testing" | 11 | "testing" | ||
| @@ -37,16 +38,16 @@ func TestMaintainerDebounces(t *testing.T) { | |||||
| 37 | runs.Add(1) | 38 | runs.Add(1) | ||
| 38 | return nil | 39 | return nil | ||
| 39 | }) | 40 | }) | ||
| 40 | - | m := app.NewMaintainer(ctx, git, 50*time.Millisecond) | 41 | + | m := app.NewMaintainer(ctx, git, 200*time.Millisecond) |
| 41 | defer m.Close(ctx) | 42 | defer m.Close(ctx) | ||
| 42 | 43 | ||||
| 43 | for range 10 { | 44 | for range 10 { | ||
| 44 | m.Schedule(ctx, n) | 45 | m.Schedule(ctx, n) | ||
| 45 | } | 46 | } | ||
| 46 | - | time.Sleep(10 * time.Millisecond) | |||
| 47 | assert.Equal(t, int32(0), runs.Load(), "waits for the delay") | 47 | assert.Equal(t, int32(0), runs.Load(), "waits for the delay") | ||
| 48 | waitFor(t, func() bool { return runs.Load() == 1 }, "one run") | 48 | waitFor(t, func() bool { return runs.Load() == 1 }, "one run") | ||
| 49 | - | time.Sleep(150 * time.Millisecond) | 49 | + | // Idle: nothing is pending or running, so no second run is coming. |
| 50 | + | waitFor(t, m.Idle, "the maintainer to be idle") | |||
| 50 | assert.Equal(t, int32(1), runs.Load(), "a burst makes one run") | 51 | assert.Equal(t, int32(1), runs.Load(), "a burst makes one run") | ||
| 51 | 52 | ||||
| 52 | m.Schedule(ctx, n) | 53 | m.Schedule(ctx, n) | ||
| @@ -86,13 +87,12 @@ func TestMaintainerOneRunAtATimePerRepo(t *testing.T) { | |||||
| 86 | m.Schedule(ctx, n) | 87 | m.Schedule(ctx, n) | ||
| 87 | m.Schedule(ctx, other) | 88 | m.Schedule(ctx, other) | ||
| 88 | waitFor(t, otherRan.Load, "another repository is not blocked") | 89 | waitFor(t, otherRan.Load, "another repository is not blocked") | ||
| 89 | - | time.Sleep(60 * time.Millisecond) | |||
| 90 | assert.Equal(t, int32(1), runs.Load(), "the second run waits for the first") | 90 | assert.Equal(t, int32(1), runs.Load(), "the second run waits for the first") | ||
| 91 | 91 | ||||
| 92 | release <- struct{}{} // finish first run | 92 | release <- struct{}{} // finish first run | ||
| 93 | waitFor(t, func() bool { return runs.Load() == 2 }, "queued run") | 93 | waitFor(t, func() bool { return runs.Load() == 2 }, "queued run") | ||
| 94 | release <- struct{}{} | 94 | release <- struct{}{} | ||
| 95 | - | time.Sleep(60 * time.Millisecond) | 95 | + | waitFor(t, m.Idle, "the maintainer to be idle") |
| 96 | assert.Equal(t, int32(2), runs.Load(), "exactly one queued run") | 96 | assert.Equal(t, int32(2), runs.Load(), "exactly one queued run") | ||
| 97 | assert.Equal(t, int32(1), maxConcurrent.Load()) | 97 | assert.Equal(t, int32(1), maxConcurrent.Load()) | ||
| 98 | } | 98 | } | ||
| @@ -163,7 +163,7 @@ func TestMaintainerConcurrentSchedule(t *testing.T) { | |||||
| 163 | defer wg.Done() | 163 | defer wg.Done() | ||
| 164 | for range 50 { | 164 | for range 50 { | ||
| 165 | m.Schedule(ctx, name(t, string(rune('a'+i%3)))) | 165 | m.Schedule(ctx, name(t, string(rune('a'+i%3)))) | ||
| 166 | - | time.Sleep(100 * time.Microsecond) | 166 | + | runtime.Gosched() |
| 167 | } | 167 | } | ||
| 168 | }() | 168 | }() | ||
| 169 | } | 169 | } | ||