shad-go/smartsched/scheduler_test.go

152 lines
3.6 KiB
Go
Raw Normal View History

2020-04-05 11:29:46 +00:00
package scheduler_test
2020-03-28 21:34:09 +00:00
import (
2020-04-05 11:29:46 +00:00
"context"
2020-03-28 21:34:09 +00:00
"testing"
"time"
"github.com/jonboulle/clockwork"
2020-04-05 11:29:46 +00:00
"github.com/stretchr/testify/assert"
2020-03-28 21:34:09 +00:00
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
"go.uber.org/zap/zaptest"
"gitlab.com/slon/shad-go/distbuild/pkg/api"
"gitlab.com/slon/shad-go/distbuild/pkg/build"
"gitlab.com/slon/shad-go/distbuild/pkg/scheduler"
2020-03-28 21:34:09 +00:00
)
const (
2020-03-29 16:03:07 +00:00
workerID0 api.WorkerID = "w0"
2020-03-28 21:34:09 +00:00
)
var (
config = scheduler.Config{
CacheTimeout: time.Second,
DepsTimeout: time.Minute,
}
)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
type testScheduler struct {
*scheduler.Scheduler
clockwork.FakeClock
2021-04-25 13:25:31 +00:00
reset chan struct{}
2020-04-05 11:29:46 +00:00
}
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
func newTestScheduler(t *testing.T) *testScheduler {
log := zaptest.NewLogger(t)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
s := &testScheduler{
FakeClock: clockwork.NewFakeClock(),
Scheduler: scheduler.NewScheduler(log, config),
2021-04-25 13:25:31 +00:00
reset: make(chan struct{}),
2020-04-05 11:29:46 +00:00
}
2020-03-28 21:34:09 +00:00
2021-04-25 13:25:31 +00:00
go func() {
select {
case <-time.After(time.Second * 5):
panic("test hang")
case <-s.reset:
return
}
}()
2021-04-25 13:10:08 +00:00
scheduler.TimeAfter = s.FakeClock.After
2020-04-05 11:29:46 +00:00
return s
}
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
func (s *testScheduler) stop(t *testing.T) {
2021-04-25 13:25:31 +00:00
close(s.reset)
2021-04-25 13:10:08 +00:00
scheduler.TimeAfter = time.After
s.Scheduler.Stop()
2020-04-05 11:29:46 +00:00
goleak.VerifyNone(t)
}
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
func TestScheduler_SingleJob(t *testing.T) {
s := newTestScheduler(t)
defer s.stop(t)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
job0 := &api.JobSpec{Job: build.Job{ID: build.NewID()}}
pendingJob0 := s.ScheduleJob(job0)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
s.BlockUntil(1)
s.Advance(config.DepsTimeout) // At this point job must be in global queue.
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
s.RegisterWorker(workerID0)
pickerJob := s.PickJob(context.Background(), workerID0)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
require.Equal(t, pendingJob0, pickerJob)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
result := &api.JobResult{ID: job0.ID, ExitCode: 0}
s.OnJobComplete(workerID0, job0.ID, result)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
select {
case <-pendingJob0.Finished:
require.Equal(t, pendingJob0.Result, result)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
default:
t.Fatalf("job0 is not finished")
}
}
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
func TestScheduler_PickJobCancelation(t *testing.T) {
s := newTestScheduler(t)
defer s.stop(t)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
ctx, cancel := context.WithCancel(context.Background())
cancel()
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
s.RegisterWorker(workerID0)
require.Nil(t, s.PickJob(ctx, workerID0))
}
func TestScheduler_CacheLocalScheduling(t *testing.T) {
s := newTestScheduler(t)
defer s.stop(t)
cachedJob := &api.JobSpec{Job: build.Job{ID: build.NewID()}}
uncachedJob := &api.JobSpec{Job: build.Job{ID: build.NewID()}}
s.RegisterWorker(workerID0)
s.OnJobComplete(workerID0, cachedJob.ID, &api.JobResult{})
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
pendingUncachedJob := s.ScheduleJob(uncachedJob)
pendingCachedJob := s.ScheduleJob(cachedJob)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
s.BlockUntil(2) // both jobs should be blocked
firstPickedJob := s.PickJob(context.Background(), workerID0)
assert.Equal(t, pendingCachedJob, firstPickedJob)
s.Advance(config.DepsTimeout) // At this point uncachedJob is put into global queue.
secondPickedJob := s.PickJob(context.Background(), workerID0)
assert.Equal(t, pendingUncachedJob, secondPickedJob)
}
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
func TestScheduler_DependencyLocalScheduling(t *testing.T) {
s := newTestScheduler(t)
defer s.stop(t)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
job0 := &api.JobSpec{Job: build.Job{ID: build.NewID()}}
s.RegisterWorker(workerID0)
s.OnJobComplete(workerID0, job0.ID, &api.JobResult{})
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
job1 := &api.JobSpec{Job: build.Job{ID: build.NewID(), Deps: []build.ID{job0.ID}}}
job2 := &api.JobSpec{Job: build.Job{ID: build.NewID()}}
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
pendingJob2 := s.ScheduleJob(job2)
pendingJob1 := s.ScheduleJob(job1)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
s.BlockUntil(2) // both jobs should be blocked on DepsTimeout
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
firstPickedJob := s.PickJob(context.Background(), workerID0)
require.Equal(t, pendingJob1, firstPickedJob)
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
s.Advance(config.DepsTimeout) // At this point job2 is put into global queue.
2020-03-28 21:34:09 +00:00
2020-04-05 11:29:46 +00:00
secondPickedJob := s.PickJob(context.Background(), workerID0)
require.Equal(t, pendingJob2, secondPickedJob)
2020-03-28 21:34:09 +00:00
}