cron_test.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. package cron
  2. import (
  3. "fmt"
  4. "sync"
  5. "testing"
  6. "time"
  7. )
  8. // Many tests schedule a job for every second, and then wait at most a second
  9. // for it to run. This amount is just slightly larger than 1 second to
  10. // compensate for a few milliseconds of runtime.
  11. const ONE_SECOND = 1*time.Second + 10*time.Millisecond
  12. func TestFuncPanicRecovery(t *testing.T) {
  13. cron := New()
  14. cron.Start()
  15. defer cron.Stop()
  16. cron.AddFunc("* * * * * ?", func() { panic("YOLO") })
  17. select {
  18. case <-time.After(ONE_SECOND):
  19. return
  20. }
  21. }
  22. type DummyJob struct{}
  23. func (d DummyJob) Run() {
  24. panic("YOLO")
  25. }
  26. func TestJobPanicRecovery(t *testing.T) {
  27. var job DummyJob
  28. cron := New()
  29. cron.Start()
  30. defer cron.Stop()
  31. cron.AddJob("* * * * * ?", job)
  32. select {
  33. case <-time.After(ONE_SECOND):
  34. return
  35. }
  36. }
  37. // Start and stop cron with no entries.
  38. func TestNoEntries(t *testing.T) {
  39. cron := New()
  40. cron.Start()
  41. select {
  42. case <-time.After(ONE_SECOND):
  43. t.FailNow()
  44. case <-stop(cron):
  45. }
  46. }
  47. // Start, stop, then add an entry. Verify entry doesn't run.
  48. func TestStopCausesJobsToNotRun(t *testing.T) {
  49. wg := &sync.WaitGroup{}
  50. wg.Add(1)
  51. cron := New()
  52. cron.Start()
  53. cron.Stop()
  54. cron.AddFunc("* * * * * ?", func() { wg.Done() })
  55. select {
  56. case <-time.After(ONE_SECOND):
  57. // No job ran!
  58. case <-wait(wg):
  59. t.FailNow()
  60. }
  61. }
  62. // Add a job, start cron, expect it runs.
  63. func TestAddBeforeRunning(t *testing.T) {
  64. wg := &sync.WaitGroup{}
  65. wg.Add(1)
  66. cron := New()
  67. cron.AddFunc("* * * * * ?", func() { wg.Done() })
  68. cron.Start()
  69. defer cron.Stop()
  70. // Give cron 2 seconds to run our job (which is always activated).
  71. select {
  72. case <-time.After(ONE_SECOND):
  73. t.FailNow()
  74. case <-wait(wg):
  75. }
  76. }
  77. // Start cron, add a job, expect it runs.
  78. func TestAddWhileRunning(t *testing.T) {
  79. wg := &sync.WaitGroup{}
  80. wg.Add(1)
  81. cron := New()
  82. cron.Start()
  83. defer cron.Stop()
  84. cron.AddFunc("* * * * * ?", func() { wg.Done() })
  85. select {
  86. case <-time.After(ONE_SECOND):
  87. t.FailNow()
  88. case <-wait(wg):
  89. }
  90. }
  91. // Test timing with Entries.
  92. func TestSnapshotEntries(t *testing.T) {
  93. wg := &sync.WaitGroup{}
  94. wg.Add(1)
  95. cron := New()
  96. cron.AddFunc("@every 2s", func() { wg.Done() })
  97. cron.Start()
  98. defer cron.Stop()
  99. // Cron should fire in 2 seconds. After 1 second, call Entries.
  100. select {
  101. case <-time.After(ONE_SECOND):
  102. cron.Entries()
  103. }
  104. // Even though Entries was called, the cron should fire at the 2 second mark.
  105. select {
  106. case <-time.After(ONE_SECOND):
  107. t.FailNow()
  108. case <-wait(wg):
  109. }
  110. }
  111. // Test that the entries are correctly sorted.
  112. // Add a bunch of long-in-the-future entries, and an immediate entry, and ensure
  113. // that the immediate entry runs immediately.
  114. // Also: Test that multiple jobs run in the same instant.
  115. func TestMultipleEntries(t *testing.T) {
  116. wg := &sync.WaitGroup{}
  117. wg.Add(2)
  118. cron := New()
  119. cron.AddFunc("0 0 0 1 1 ?", func() {})
  120. cron.AddFunc("* * * * * ?", func() { wg.Done() })
  121. cron.AddFunc("0 0 0 31 12 ?", func() {})
  122. cron.AddFunc("* * * * * ?", func() { wg.Done() })
  123. cron.Start()
  124. defer cron.Stop()
  125. select {
  126. case <-time.After(ONE_SECOND):
  127. t.FailNow()
  128. case <-wait(wg):
  129. }
  130. }
  131. // Test running the same job twice.
  132. func TestRunningJobTwice(t *testing.T) {
  133. wg := &sync.WaitGroup{}
  134. wg.Add(2)
  135. cron := New()
  136. cron.AddFunc("0 0 0 1 1 ?", func() {})
  137. cron.AddFunc("0 0 0 31 12 ?", func() {})
  138. cron.AddFunc("* * * * * ?", func() { wg.Done() })
  139. cron.Start()
  140. defer cron.Stop()
  141. select {
  142. case <-time.After(2 * ONE_SECOND):
  143. t.FailNow()
  144. case <-wait(wg):
  145. }
  146. }
  147. func TestRunningMultipleSchedules(t *testing.T) {
  148. wg := &sync.WaitGroup{}
  149. wg.Add(2)
  150. cron := New()
  151. cron.AddFunc("0 0 0 1 1 ?", func() {})
  152. cron.AddFunc("0 0 0 31 12 ?", func() {})
  153. cron.AddFunc("* * * * * ?", func() { wg.Done() })
  154. cron.Schedule(Every(time.Minute), FuncJob(func() {}))
  155. cron.Schedule(Every(time.Second), FuncJob(func() { wg.Done() }))
  156. cron.Schedule(Every(time.Hour), FuncJob(func() {}))
  157. cron.Start()
  158. defer cron.Stop()
  159. select {
  160. case <-time.After(2 * ONE_SECOND):
  161. t.FailNow()
  162. case <-wait(wg):
  163. }
  164. }
  165. // Test that the cron is run in the local time zone (as opposed to UTC).
  166. func TestLocalTimezone(t *testing.T) {
  167. wg := &sync.WaitGroup{}
  168. wg.Add(1)
  169. now := time.Now().Local()
  170. spec := fmt.Sprintf("%d %d %d %d %d ?",
  171. now.Second()+1, now.Minute(), now.Hour(), now.Day(), now.Month())
  172. cron := New()
  173. cron.AddFunc(spec, func() { wg.Done() })
  174. cron.Start()
  175. defer cron.Stop()
  176. select {
  177. case <-time.After(ONE_SECOND):
  178. t.FailNow()
  179. case <-wait(wg):
  180. }
  181. }
  182. // Test that calling stop before start silently returns without
  183. // blocking the stop channel.
  184. func TestStopWithoutStart(t *testing.T) {
  185. cron := New()
  186. cron.Stop()
  187. }
  188. type testJob struct {
  189. wg *sync.WaitGroup
  190. name string
  191. }
  192. func (t testJob) Run() {
  193. t.wg.Done()
  194. }
  195. // Simple test using Runnables.
  196. func TestJob(t *testing.T) {
  197. wg := &sync.WaitGroup{}
  198. wg.Add(1)
  199. cron := New()
  200. cron.AddJob("0 0 0 30 Feb ?", testJob{wg, "job0"})
  201. cron.AddJob("0 0 0 1 1 ?", testJob{wg, "job1"})
  202. cron.AddJob("* * * * * ?", testJob{wg, "job2"})
  203. cron.AddJob("1 0 0 1 1 ?", testJob{wg, "job3"})
  204. cron.Schedule(Every(5*time.Second+5*time.Nanosecond), testJob{wg, "job4"})
  205. cron.Schedule(Every(5*time.Minute), testJob{wg, "job5"})
  206. cron.Start()
  207. defer cron.Stop()
  208. select {
  209. case <-time.After(ONE_SECOND):
  210. t.FailNow()
  211. case <-wait(wg):
  212. }
  213. // Ensure the entries are in the right order.
  214. expecteds := []string{"job2", "job4", "job5", "job1", "job3", "job0"}
  215. var actuals []string
  216. for _, entry := range cron.Entries() {
  217. actuals = append(actuals, entry.Job.(testJob).name)
  218. }
  219. for i, expected := range expecteds {
  220. if actuals[i] != expected {
  221. t.Errorf("Jobs not in the right order. (expected) %s != %s (actual)", expecteds, actuals)
  222. t.FailNow()
  223. }
  224. }
  225. }
  226. func wait(wg *sync.WaitGroup) chan bool {
  227. ch := make(chan bool)
  228. go func() {
  229. wg.Wait()
  230. ch <- true
  231. }()
  232. return ch
  233. }
  234. func stop(cron *Cron) chan bool {
  235. ch := make(chan bool)
  236. go func() {
  237. cron.Stop()
  238. ch <- true
  239. }()
  240. return ch
  241. }