mirror of https://github.com/go-co-op/gocron.git
879 lines
15 KiB
Go
879 lines
15 KiB
Go
package gocron
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/goleak"
|
|
)
|
|
|
|
func newTestScheduler(options ...SchedulerOption) (Scheduler, error) {
|
|
// default test options
|
|
out := []SchedulerOption{
|
|
WithLogger(NewJSONSlogLogger(slog.LevelDebug)),
|
|
WithStopTimeout(time.Second),
|
|
}
|
|
|
|
// append any additional options 2nd to override defaults if needed
|
|
out = append(out, options...)
|
|
return NewScheduler(out...)
|
|
}
|
|
|
|
func TestScheduler_OneSecond_NoOptions(t *testing.T) {
|
|
defer goleak.VerifyNone(t)
|
|
cronNoOptionsCh := make(chan struct{}, 10)
|
|
durationNoOptionsCh := make(chan struct{}, 10)
|
|
|
|
tests := []struct {
|
|
name string
|
|
ch chan struct{}
|
|
jd JobDefinition
|
|
tsk Task
|
|
}{
|
|
{
|
|
"cron",
|
|
cronNoOptionsCh,
|
|
CronJob(
|
|
"* * * * * *",
|
|
true,
|
|
),
|
|
NewTask(
|
|
func() {
|
|
cronNoOptionsCh <- struct{}{}
|
|
},
|
|
),
|
|
},
|
|
{
|
|
"duration",
|
|
durationNoOptionsCh,
|
|
DurationJob(
|
|
time.Second,
|
|
),
|
|
NewTask(
|
|
func() {
|
|
durationNoOptionsCh <- struct{}{}
|
|
},
|
|
),
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s, err := newTestScheduler()
|
|
require.NoError(t, err)
|
|
|
|
_, err = s.NewJob(tt.jd, tt.tsk)
|
|
require.NoError(t, err)
|
|
|
|
s.Start()
|
|
|
|
startTime := time.Now()
|
|
var runCount int
|
|
for runCount < 1 {
|
|
<-tt.ch
|
|
runCount++
|
|
}
|
|
err = s.Shutdown()
|
|
require.NoError(t, err)
|
|
stopTime := time.Now()
|
|
|
|
select {
|
|
case <-tt.ch:
|
|
t.Fatal("job ran after scheduler was stopped")
|
|
case <-time.After(time.Millisecond * 50):
|
|
}
|
|
|
|
runDuration := stopTime.Sub(startTime)
|
|
assert.GreaterOrEqual(t, runDuration, time.Millisecond)
|
|
assert.LessOrEqual(t, runDuration, 1500*time.Millisecond)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestScheduler_LongRunningJobs(t *testing.T) {
|
|
defer goleak.VerifyNone(t)
|
|
|
|
durationCh := make(chan struct{}, 10)
|
|
durationSingletonCh := make(chan struct{}, 10)
|
|
|
|
tests := []struct {
|
|
name string
|
|
ch chan struct{}
|
|
jd JobDefinition
|
|
tsk Task
|
|
opts []JobOption
|
|
options []SchedulerOption
|
|
expectedRuns int
|
|
}{
|
|
{
|
|
"duration",
|
|
durationCh,
|
|
DurationJob(
|
|
time.Millisecond * 500,
|
|
),
|
|
NewTask(
|
|
func() {
|
|
time.Sleep(1 * time.Second)
|
|
durationCh <- struct{}{}
|
|
},
|
|
),
|
|
nil,
|
|
[]SchedulerOption{WithStopTimeout(time.Second * 2)},
|
|
3,
|
|
},
|
|
{
|
|
"duration singleton",
|
|
durationSingletonCh,
|
|
DurationJob(
|
|
time.Millisecond * 500,
|
|
),
|
|
NewTask(
|
|
func() {
|
|
time.Sleep(1 * time.Second)
|
|
durationSingletonCh <- struct{}{}
|
|
},
|
|
),
|
|
[]JobOption{WithSingletonMode(LimitModeWait)},
|
|
[]SchedulerOption{WithStopTimeout(time.Second * 5)},
|
|
2,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s, err := newTestScheduler(tt.options...)
|
|
require.NoError(t, err)
|
|
|
|
_, err = s.NewJob(tt.jd, tt.tsk, tt.opts...)
|
|
require.NoError(t, err)
|
|
|
|
s.Start()
|
|
time.Sleep(1600 * time.Millisecond)
|
|
err = s.Shutdown()
|
|
require.NoError(t, err)
|
|
|
|
var runCount int
|
|
timeout := make(chan struct{})
|
|
go func() {
|
|
time.Sleep(2 * time.Second)
|
|
close(timeout)
|
|
}()
|
|
Outer:
|
|
for {
|
|
select {
|
|
case <-tt.ch:
|
|
runCount++
|
|
case <-timeout:
|
|
break Outer
|
|
}
|
|
}
|
|
|
|
assert.Equal(t, tt.expectedRuns, runCount)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestScheduler_Update(t *testing.T) {
|
|
defer goleak.VerifyNone(t)
|
|
|
|
durationJobCh := make(chan struct{})
|
|
|
|
tests := []struct {
|
|
name string
|
|
initialJob JobDefinition
|
|
updateJob JobDefinition
|
|
tsk Task
|
|
ch chan struct{}
|
|
runCount int
|
|
updateAfterCount int
|
|
expectedMinTime time.Duration
|
|
expectedMaxRunTime time.Duration
|
|
}{
|
|
{
|
|
"duration, updated to another duration",
|
|
DurationJob(
|
|
time.Millisecond * 500,
|
|
),
|
|
DurationJob(
|
|
time.Second,
|
|
),
|
|
NewTask(
|
|
func() {
|
|
durationJobCh <- struct{}{}
|
|
},
|
|
),
|
|
durationJobCh,
|
|
2,
|
|
1,
|
|
time.Second * 1,
|
|
time.Second * 2,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s, err := newTestScheduler()
|
|
require.NoError(t, err)
|
|
|
|
j, err := s.NewJob(tt.initialJob, tt.tsk)
|
|
require.NoError(t, err)
|
|
|
|
startTime := time.Now()
|
|
s.Start()
|
|
|
|
var runCount int
|
|
for runCount < tt.runCount {
|
|
select {
|
|
case <-tt.ch:
|
|
runCount++
|
|
if runCount == tt.updateAfterCount {
|
|
_, err = s.Update(j.ID(), tt.updateJob, tt.tsk)
|
|
require.NoError(t, err)
|
|
}
|
|
default:
|
|
}
|
|
}
|
|
err = s.Shutdown()
|
|
require.NoError(t, err)
|
|
stopTime := time.Now()
|
|
|
|
select {
|
|
case <-tt.ch:
|
|
t.Fatal("job ran after scheduler was stopped")
|
|
case <-time.After(time.Millisecond * 50):
|
|
}
|
|
|
|
runDuration := stopTime.Sub(startTime)
|
|
assert.GreaterOrEqual(t, runDuration, tt.expectedMinTime)
|
|
assert.LessOrEqual(t, runDuration, tt.expectedMaxRunTime)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestScheduler_StopTimeout(t *testing.T) {
|
|
defer goleak.VerifyNone(t)
|
|
|
|
tests := []struct {
|
|
name string
|
|
jd JobDefinition
|
|
f any
|
|
opts []JobOption
|
|
}{
|
|
{
|
|
"duration",
|
|
DurationJob(
|
|
time.Millisecond * 100,
|
|
),
|
|
func(testDoneCtx context.Context) {
|
|
select {
|
|
case <-time.After(1 * time.Second):
|
|
case <-testDoneCtx.Done():
|
|
}
|
|
},
|
|
nil,
|
|
},
|
|
{
|
|
"duration singleton",
|
|
DurationJob(
|
|
time.Millisecond * 100,
|
|
),
|
|
func(testDoneCtx context.Context) {
|
|
select {
|
|
case <-time.After(1 * time.Second):
|
|
case <-testDoneCtx.Done():
|
|
}
|
|
},
|
|
[]JobOption{WithSingletonMode(LimitModeWait)},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
testDoneCtx, cancel := context.WithCancel(context.Background())
|
|
s, err := newTestScheduler(
|
|
WithStopTimeout(time.Millisecond * 100),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = s.NewJob(tt.jd, NewTask(tt.f, testDoneCtx), tt.opts...)
|
|
require.NoError(t, err)
|
|
|
|
s.Start()
|
|
time.Sleep(time.Millisecond * 200)
|
|
err = s.Shutdown()
|
|
assert.ErrorIs(t, err, ErrStopJobsTimedOut)
|
|
cancel()
|
|
time.Sleep(2 * time.Second)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestScheduler_Shutdown(t *testing.T) {
|
|
goleak.VerifyNone(t)
|
|
|
|
t.Run("start, stop, start, shutdown", func(t *testing.T) {
|
|
s, err := newTestScheduler(
|
|
WithStopTimeout(time.Second),
|
|
)
|
|
require.NoError(t, err)
|
|
_, err = s.NewJob(
|
|
DurationJob(
|
|
50*time.Millisecond,
|
|
),
|
|
NewTask(
|
|
func() {},
|
|
),
|
|
WithStartAt(
|
|
WithStartImmediately(),
|
|
),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
s.Start()
|
|
time.Sleep(50 * time.Millisecond)
|
|
require.NoError(t, s.StopJobs())
|
|
|
|
time.Sleep(200 * time.Millisecond)
|
|
s.Start()
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
require.NoError(t, s.Shutdown())
|
|
time.Sleep(200 * time.Millisecond)
|
|
})
|
|
|
|
t.Run("calling Job methods after shutdown errors", func(t *testing.T) {
|
|
s, err := newTestScheduler(
|
|
WithStopTimeout(time.Second),
|
|
)
|
|
require.NoError(t, err)
|
|
j, err := s.NewJob(
|
|
DurationJob(
|
|
100*time.Millisecond,
|
|
),
|
|
NewTask(
|
|
func() {},
|
|
),
|
|
WithStartAt(
|
|
WithStartImmediately(),
|
|
),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
s.Start()
|
|
time.Sleep(50 * time.Millisecond)
|
|
require.NoError(t, s.Shutdown())
|
|
|
|
_, err = j.LastRun()
|
|
assert.ErrorIs(t, err, ErrJobNotFound)
|
|
|
|
_, err = j.NextRun()
|
|
assert.ErrorIs(t, err, ErrJobNotFound)
|
|
})
|
|
}
|
|
|
|
func TestScheduler_NewJob(t *testing.T) {
|
|
goleak.VerifyNone(t)
|
|
tests := []struct {
|
|
name string
|
|
jd JobDefinition
|
|
tsk Task
|
|
opts []JobOption
|
|
}{
|
|
{
|
|
"cron with timezone",
|
|
CronJob(
|
|
"CRON_TZ=America/Chicago * * * * * *",
|
|
true,
|
|
),
|
|
NewTask(
|
|
func() {},
|
|
),
|
|
nil,
|
|
},
|
|
{
|
|
"cron with timezone, no seconds",
|
|
CronJob(
|
|
"CRON_TZ=America/Chicago * * * * *",
|
|
false,
|
|
),
|
|
NewTask(
|
|
func() {},
|
|
),
|
|
nil,
|
|
},
|
|
{
|
|
"random duration",
|
|
DurationRandomJob(
|
|
time.Second,
|
|
time.Second*5,
|
|
),
|
|
NewTask(
|
|
func() {},
|
|
),
|
|
nil,
|
|
},
|
|
{
|
|
"daily",
|
|
DailyJob(
|
|
1,
|
|
NewAtTimes(
|
|
NewAtTime(1, 0, 0),
|
|
),
|
|
),
|
|
NewTask(
|
|
func() {},
|
|
),
|
|
nil,
|
|
},
|
|
{
|
|
"weekly",
|
|
WeeklyJob(
|
|
1,
|
|
NewWeekdays(time.Monday),
|
|
NewAtTimes(
|
|
NewAtTime(1, 0, 0),
|
|
),
|
|
),
|
|
NewTask(
|
|
func() {},
|
|
),
|
|
nil,
|
|
},
|
|
{
|
|
"monthly",
|
|
MonthlyJob(
|
|
1,
|
|
NewDaysOfTheMonth(1, -1),
|
|
NewAtTimes(
|
|
NewAtTime(1, 0, 0),
|
|
),
|
|
),
|
|
NewTask(
|
|
func() {},
|
|
),
|
|
nil,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s, err := newTestScheduler()
|
|
require.NoError(t, err)
|
|
|
|
_, err = s.NewJob(tt.jd, tt.tsk, tt.opts...)
|
|
require.NoError(t, err)
|
|
|
|
s.Start()
|
|
require.NoError(t, s.Shutdown())
|
|
time.Sleep(50 * time.Millisecond)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestScheduler_NewJobErrors(t *testing.T) {
|
|
goleak.VerifyNone(t)
|
|
tests := []struct {
|
|
name string
|
|
jd JobDefinition
|
|
opts []JobOption
|
|
err error
|
|
}{
|
|
{
|
|
"cron with timezone",
|
|
CronJob(
|
|
"bad cron",
|
|
true,
|
|
),
|
|
nil,
|
|
ErrCronJobParse,
|
|
},
|
|
{
|
|
"random with bad min/max",
|
|
DurationRandomJob(
|
|
time.Second*5,
|
|
time.Second,
|
|
),
|
|
nil,
|
|
ErrDurationRandomJobMinMax,
|
|
},
|
|
{
|
|
"daily job at times nil",
|
|
DailyJob(
|
|
1,
|
|
nil,
|
|
),
|
|
nil,
|
|
ErrDailyJobAtTimesNil,
|
|
},
|
|
{
|
|
"daily job at time nil",
|
|
DailyJob(
|
|
1,
|
|
NewAtTimes(nil),
|
|
),
|
|
nil,
|
|
ErrDailyJobAtTimeNil,
|
|
},
|
|
{
|
|
"daily job hours out of range",
|
|
DailyJob(
|
|
1,
|
|
NewAtTimes(
|
|
NewAtTime(100, 0, 0),
|
|
),
|
|
),
|
|
nil,
|
|
ErrDailyJobHours,
|
|
},
|
|
{
|
|
"daily job minutes out of range",
|
|
DailyJob(
|
|
1,
|
|
NewAtTimes(
|
|
NewAtTime(1, 100, 0),
|
|
),
|
|
),
|
|
nil,
|
|
ErrDailyJobMinutesSeconds,
|
|
},
|
|
{
|
|
"daily job seconds out of range",
|
|
DailyJob(
|
|
1,
|
|
NewAtTimes(
|
|
NewAtTime(1, 0, 100),
|
|
),
|
|
),
|
|
nil,
|
|
ErrDailyJobMinutesSeconds,
|
|
},
|
|
{
|
|
"weekly job at times nil",
|
|
WeeklyJob(
|
|
1,
|
|
NewWeekdays(time.Monday),
|
|
nil,
|
|
),
|
|
nil,
|
|
ErrWeeklyJobAtTimesNil,
|
|
},
|
|
{
|
|
"weekly job at time nil",
|
|
WeeklyJob(
|
|
1,
|
|
NewWeekdays(time.Monday),
|
|
NewAtTimes(nil),
|
|
),
|
|
nil,
|
|
ErrWeeklyJobAtTimeNil,
|
|
},
|
|
{
|
|
"weekly job weekdays nil",
|
|
WeeklyJob(
|
|
1,
|
|
nil,
|
|
NewAtTimes(
|
|
NewAtTime(1, 0, 0),
|
|
),
|
|
),
|
|
nil,
|
|
ErrWeeklyJobDaysOfTheWeekNil,
|
|
},
|
|
{
|
|
"weekly job hours out of range",
|
|
WeeklyJob(
|
|
1,
|
|
NewWeekdays(time.Monday),
|
|
NewAtTimes(
|
|
NewAtTime(100, 0, 0),
|
|
),
|
|
),
|
|
nil,
|
|
ErrWeeklyJobHours,
|
|
},
|
|
{
|
|
"weekly job minutes out of range",
|
|
WeeklyJob(
|
|
1,
|
|
NewWeekdays(time.Monday),
|
|
NewAtTimes(
|
|
NewAtTime(1, 100, 0),
|
|
),
|
|
),
|
|
nil,
|
|
ErrWeeklyJobMinutesSeconds,
|
|
},
|
|
{
|
|
"weekly job seconds out of range",
|
|
WeeklyJob(
|
|
1,
|
|
NewWeekdays(time.Monday),
|
|
NewAtTimes(
|
|
NewAtTime(1, 0, 100),
|
|
),
|
|
),
|
|
nil,
|
|
ErrWeeklyJobMinutesSeconds,
|
|
},
|
|
{
|
|
"monthly job at times nil",
|
|
MonthlyJob(
|
|
1,
|
|
NewDaysOfTheMonth(1),
|
|
nil,
|
|
),
|
|
nil,
|
|
ErrMonthlyJobAtTimesNil,
|
|
},
|
|
{
|
|
"monthly job at time nil",
|
|
MonthlyJob(
|
|
1,
|
|
NewDaysOfTheMonth(1),
|
|
NewAtTimes(nil),
|
|
),
|
|
nil,
|
|
ErrMonthlyJobAtTimeNil,
|
|
},
|
|
{
|
|
"monthly job days out of range",
|
|
MonthlyJob(
|
|
1,
|
|
NewDaysOfTheMonth(0),
|
|
NewAtTimes(
|
|
NewAtTime(1, 0, 0),
|
|
),
|
|
),
|
|
nil,
|
|
ErrMonthlyJobDays,
|
|
},
|
|
{
|
|
"monthly job days out of range",
|
|
MonthlyJob(
|
|
1,
|
|
nil,
|
|
NewAtTimes(
|
|
NewAtTime(1, 0, 0),
|
|
),
|
|
),
|
|
nil,
|
|
ErrMonthlyJobDaysNil,
|
|
},
|
|
{
|
|
"monthly job hours out of range",
|
|
MonthlyJob(
|
|
1,
|
|
NewDaysOfTheMonth(1),
|
|
NewAtTimes(
|
|
NewAtTime(100, 0, 0),
|
|
),
|
|
),
|
|
nil,
|
|
ErrMonthlyJobHours,
|
|
},
|
|
{
|
|
"monthly job minutes out of range",
|
|
MonthlyJob(
|
|
1,
|
|
NewDaysOfTheMonth(1),
|
|
NewAtTimes(
|
|
NewAtTime(1, 100, 0),
|
|
),
|
|
),
|
|
nil,
|
|
ErrMonthlyJobMinutesSeconds,
|
|
},
|
|
{
|
|
"monthly job seconds out of range",
|
|
MonthlyJob(
|
|
1,
|
|
NewDaysOfTheMonth(1),
|
|
NewAtTimes(
|
|
NewAtTime(1, 0, 100),
|
|
),
|
|
),
|
|
nil,
|
|
ErrMonthlyJobMinutesSeconds,
|
|
},
|
|
{
|
|
"WithName no name",
|
|
DurationJob(
|
|
time.Second,
|
|
),
|
|
[]JobOption{WithName("")},
|
|
ErrWithNameEmpty,
|
|
},
|
|
{
|
|
"WithStartDateTime is zero",
|
|
DurationJob(
|
|
time.Second,
|
|
),
|
|
[]JobOption{WithStartAt(WithStartDateTime(time.Time{}))},
|
|
ErrWithStartDateTimePast,
|
|
},
|
|
{
|
|
"WithStartDateTime is in the past",
|
|
DurationJob(
|
|
time.Second,
|
|
),
|
|
[]JobOption{WithStartAt(WithStartDateTime(time.Now().Add(-time.Second)))},
|
|
ErrWithStartDateTimePast,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s, err := newTestScheduler(
|
|
WithStopTimeout(time.Millisecond * 50),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = s.NewJob(tt.jd, NewTask(func() {}), tt.opts...)
|
|
assert.ErrorIs(t, err, tt.err)
|
|
err = s.Shutdown()
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestScheduler_Singleton(t *testing.T) {
|
|
goleak.VerifyNone(t)
|
|
tests := []struct {
|
|
name string
|
|
duration time.Duration
|
|
limitMode LimitMode
|
|
runCount int
|
|
expectedMin time.Duration
|
|
expectedMax time.Duration
|
|
}{
|
|
{
|
|
"singleton mode reschedule",
|
|
time.Millisecond * 100,
|
|
LimitModeReschedule,
|
|
3,
|
|
time.Millisecond * 600,
|
|
time.Millisecond * 1100,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
jobRanCh := make(chan struct{}, 10)
|
|
|
|
s, err := newTestScheduler(
|
|
WithStopTimeout(1 * time.Second),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = s.NewJob(
|
|
DurationJob(
|
|
tt.duration,
|
|
),
|
|
NewTask(func() {
|
|
time.Sleep(tt.duration * 2)
|
|
jobRanCh <- struct{}{}
|
|
}),
|
|
WithSingletonMode(tt.limitMode),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
start := time.Now()
|
|
s.Start()
|
|
|
|
var runCount int
|
|
for runCount < tt.runCount {
|
|
select {
|
|
case <-jobRanCh:
|
|
runCount++
|
|
case <-time.After(time.Second):
|
|
t.Fatalf("timed out waiting for jobs to run")
|
|
}
|
|
}
|
|
|
|
stop := time.Now()
|
|
require.NoError(t, s.Shutdown())
|
|
|
|
assert.GreaterOrEqual(t, stop.Sub(start), tt.expectedMin)
|
|
assert.LessOrEqual(t, stop.Sub(start), tt.expectedMax)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestScheduler_LimitMode(t *testing.T) {
|
|
goleak.VerifyNone(t)
|
|
tests := []struct {
|
|
name string
|
|
numJobs int
|
|
limit uint
|
|
limitMode LimitMode
|
|
duration time.Duration
|
|
expectedMin time.Duration
|
|
expectedMax time.Duration
|
|
}{
|
|
{
|
|
"limit mode reschedule",
|
|
10,
|
|
2,
|
|
LimitModeReschedule,
|
|
time.Millisecond * 100,
|
|
time.Millisecond * 400,
|
|
time.Millisecond * 700,
|
|
},
|
|
{
|
|
"limit mode wait",
|
|
10,
|
|
2,
|
|
LimitModeWait,
|
|
time.Millisecond * 100,
|
|
time.Millisecond * 200,
|
|
time.Millisecond * 500,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s, err := newTestScheduler(
|
|
WithLimitConcurrentJobs(tt.limit, tt.limitMode),
|
|
WithStopTimeout(2*time.Second),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
jobRanCh := make(chan struct{}, 20)
|
|
|
|
for i := 0; i < tt.numJobs; i++ {
|
|
_, err = s.NewJob(
|
|
DurationJob(tt.duration),
|
|
NewTask(func() {
|
|
time.Sleep(tt.duration / 2)
|
|
jobRanCh <- struct{}{}
|
|
}),
|
|
)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
start := time.Now()
|
|
s.Start()
|
|
|
|
var runCount int
|
|
for runCount < tt.numJobs {
|
|
select {
|
|
case <-jobRanCh:
|
|
runCount++
|
|
case <-time.After(time.Second):
|
|
t.Fatalf("timed out waiting for jobs to run")
|
|
}
|
|
}
|
|
stop := time.Now()
|
|
err = s.Shutdown()
|
|
require.NoError(t, err)
|
|
|
|
assert.GreaterOrEqual(t, stop.Sub(start), tt.expectedMin)
|
|
assert.LessOrEqual(t, stop.Sub(start), tt.expectedMax)
|
|
})
|
|
}
|
|
}
|