mirror of
https://github.com/usememos/memos.git
synced 2024-11-15 03:07:04 +08:00
239 lines
5.9 KiB
Go
239 lines
5.9 KiB
Go
//nolint:all
|
|
package cron
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"reflect"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func appendingJob(slice *[]int, value int) Job {
|
|
var m sync.Mutex
|
|
return FuncJob(func() {
|
|
m.Lock()
|
|
*slice = append(*slice, value)
|
|
m.Unlock()
|
|
})
|
|
}
|
|
|
|
func appendingWrapper(slice *[]int, value int) JobWrapper {
|
|
return func(j Job) Job {
|
|
return FuncJob(func() {
|
|
appendingJob(slice, value).Run()
|
|
j.Run()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestChain(t *testing.T) {
|
|
var nums []int
|
|
var (
|
|
append1 = appendingWrapper(&nums, 1)
|
|
append2 = appendingWrapper(&nums, 2)
|
|
append3 = appendingWrapper(&nums, 3)
|
|
append4 = appendingJob(&nums, 4)
|
|
)
|
|
NewChain(append1, append2, append3).Then(append4).Run()
|
|
if !reflect.DeepEqual(nums, []int{1, 2, 3, 4}) {
|
|
t.Error("unexpected order of calls:", nums)
|
|
}
|
|
}
|
|
|
|
func TestChainRecover(t *testing.T) {
|
|
panickingJob := FuncJob(func() {
|
|
panic("panickingJob panics")
|
|
})
|
|
|
|
t.Run("panic exits job by default", func(*testing.T) {
|
|
defer func() {
|
|
if err := recover(); err == nil {
|
|
t.Errorf("panic expected, but none received")
|
|
}
|
|
}()
|
|
NewChain().Then(panickingJob).
|
|
Run()
|
|
})
|
|
|
|
t.Run("Recovering JobWrapper recovers", func(*testing.T) {
|
|
NewChain(Recover(PrintfLogger(log.New(io.Discard, "", 0)))).
|
|
Then(panickingJob).
|
|
Run()
|
|
})
|
|
|
|
t.Run("composed with the *IfStillRunning wrappers", func(*testing.T) {
|
|
NewChain(Recover(PrintfLogger(log.New(io.Discard, "", 0)))).
|
|
Then(panickingJob).
|
|
Run()
|
|
})
|
|
}
|
|
|
|
type countJob struct {
|
|
m sync.Mutex
|
|
started int
|
|
done int
|
|
delay time.Duration
|
|
}
|
|
|
|
func (j *countJob) Run() {
|
|
j.m.Lock()
|
|
j.started++
|
|
j.m.Unlock()
|
|
time.Sleep(j.delay)
|
|
j.m.Lock()
|
|
j.done++
|
|
j.m.Unlock()
|
|
}
|
|
|
|
func (j *countJob) Started() int {
|
|
defer j.m.Unlock()
|
|
j.m.Lock()
|
|
return j.started
|
|
}
|
|
|
|
func (j *countJob) Done() int {
|
|
defer j.m.Unlock()
|
|
j.m.Lock()
|
|
return j.done
|
|
}
|
|
|
|
func TestChainDelayIfStillRunning(t *testing.T) {
|
|
t.Run("runs immediately", func(*testing.T) {
|
|
var j countJob
|
|
wrappedJob := NewChain(DelayIfStillRunning(DiscardLogger)).Then(&j)
|
|
go wrappedJob.Run()
|
|
time.Sleep(2 * time.Millisecond) // Give the job 2ms to complete.
|
|
if c := j.Done(); c != 1 {
|
|
t.Errorf("expected job run once, immediately, got %d", c)
|
|
}
|
|
})
|
|
|
|
t.Run("second run immediate if first done", func(*testing.T) {
|
|
var j countJob
|
|
wrappedJob := NewChain(DelayIfStillRunning(DiscardLogger)).Then(&j)
|
|
go func() {
|
|
go wrappedJob.Run()
|
|
time.Sleep(time.Millisecond)
|
|
go wrappedJob.Run()
|
|
}()
|
|
time.Sleep(3 * time.Millisecond) // Give both jobs 3ms to complete.
|
|
if c := j.Done(); c != 2 {
|
|
t.Errorf("expected job run twice, immediately, got %d", c)
|
|
}
|
|
})
|
|
|
|
t.Run("second run delayed if first not done", func(*testing.T) {
|
|
var j countJob
|
|
j.delay = 10 * time.Millisecond
|
|
wrappedJob := NewChain(DelayIfStillRunning(DiscardLogger)).Then(&j)
|
|
go func() {
|
|
go wrappedJob.Run()
|
|
time.Sleep(time.Millisecond)
|
|
go wrappedJob.Run()
|
|
}()
|
|
|
|
// After 5ms, the first job is still in progress, and the second job was
|
|
// run but should be waiting for it to finish.
|
|
time.Sleep(5 * time.Millisecond)
|
|
started, done := j.Started(), j.Done()
|
|
if started != 1 || done != 0 {
|
|
t.Error("expected first job started, but not finished, got", started, done)
|
|
}
|
|
|
|
// Verify that the second job completes.
|
|
time.Sleep(25 * time.Millisecond)
|
|
started, done = j.Started(), j.Done()
|
|
if started != 2 || done != 2 {
|
|
t.Error("expected both jobs done, got", started, done)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestChainSkipIfStillRunning(t *testing.T) {
|
|
t.Run("runs immediately", func(*testing.T) {
|
|
var j countJob
|
|
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
|
|
go wrappedJob.Run()
|
|
time.Sleep(2 * time.Millisecond) // Give the job 2ms to complete.
|
|
if c := j.Done(); c != 1 {
|
|
t.Errorf("expected job run once, immediately, got %d", c)
|
|
}
|
|
})
|
|
|
|
t.Run("second run immediate if first done", func(*testing.T) {
|
|
var j countJob
|
|
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
|
|
go func() {
|
|
go wrappedJob.Run()
|
|
time.Sleep(time.Millisecond)
|
|
go wrappedJob.Run()
|
|
}()
|
|
time.Sleep(3 * time.Millisecond) // Give both jobs 3ms to complete.
|
|
if c := j.Done(); c != 2 {
|
|
t.Errorf("expected job run twice, immediately, got %d", c)
|
|
}
|
|
})
|
|
|
|
t.Run("second run skipped if first not done", func(*testing.T) {
|
|
var j countJob
|
|
j.delay = 10 * time.Millisecond
|
|
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
|
|
go func() {
|
|
go wrappedJob.Run()
|
|
time.Sleep(time.Millisecond)
|
|
go wrappedJob.Run()
|
|
}()
|
|
|
|
// After 5ms, the first job is still in progress, and the second job was
|
|
// aleady skipped.
|
|
time.Sleep(5 * time.Millisecond)
|
|
started, done := j.Started(), j.Done()
|
|
if started != 1 || done != 0 {
|
|
t.Error("expected first job started, but not finished, got", started, done)
|
|
}
|
|
|
|
// Verify that the first job completes and second does not run.
|
|
time.Sleep(25 * time.Millisecond)
|
|
started, done = j.Started(), j.Done()
|
|
if started != 1 || done != 1 {
|
|
t.Error("expected second job skipped, got", started, done)
|
|
}
|
|
})
|
|
|
|
t.Run("skip 10 jobs on rapid fire", func(*testing.T) {
|
|
var j countJob
|
|
j.delay = 10 * time.Millisecond
|
|
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
|
|
for i := 0; i < 11; i++ {
|
|
go wrappedJob.Run()
|
|
}
|
|
time.Sleep(200 * time.Millisecond)
|
|
done := j.Done()
|
|
if done != 1 {
|
|
t.Error("expected 1 jobs executed, 10 jobs dropped, got", done)
|
|
}
|
|
})
|
|
|
|
t.Run("different jobs independent", func(*testing.T) {
|
|
var j1, j2 countJob
|
|
j1.delay = 10 * time.Millisecond
|
|
j2.delay = 10 * time.Millisecond
|
|
chain := NewChain(SkipIfStillRunning(DiscardLogger))
|
|
wrappedJob1 := chain.Then(&j1)
|
|
wrappedJob2 := chain.Then(&j2)
|
|
for i := 0; i < 11; i++ {
|
|
go wrappedJob1.Run()
|
|
go wrappedJob2.Run()
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
var (
|
|
done1 = j1.Done()
|
|
done2 = j2.Done()
|
|
)
|
|
if done1 != 1 || done2 != 1 {
|
|
t.Error("expected both jobs executed once, got", done1, "and", done2)
|
|
}
|
|
})
|
|
}
|