123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /*
- * Public Domain Software
- *
- * I (Matthias Ladkau) am the author of the source code in this file.
- * I have placed the source code in this file in the public domain.
- *
- * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
- */
- package timeutil
- import (
- "bytes"
- "fmt"
- "strings"
- "testing"
- "time"
- )
- /*
- func TestExampleCron(t *testing.T) {
- // For manual testing with real Cron object
- c := NewCron()
- c.Register("* * * * * *", func() {
- fmt.Println(time.Now().UTC())
- })
- c.Start() // Start cron thread
- time.Sleep(5 * time.Second)
- c.Stop() // Shutdown cron thread
- }
- */
- func TestCron(t *testing.T) {
- c := NewCron()
- // Check repeated running and stopping
- if c.stopChan != nil {
- t.Error("Unexpected cron state")
- return
- }
- c.Start()
- if c.stopChan == nil {
- t.Error("Unexpected cron state")
- return
- }
- c.Stop()
- if c.stopChan != nil {
- t.Error("Unexpected cron state")
- return
- }
- c.Start()
- if c.stopChan == nil {
- t.Error("Unexpected cron state")
- return
- }
- c.Stop()
- if c.stopChan != nil {
- t.Error("Unexpected cron state")
- return
- }
- // Test now with a cron testing object for a day
- var buf bytes.Buffer
- c = NewTestingCronDay()
- c.Register("0 12 12,8 * * *", func() {
- buf.WriteString(fmt.Sprintf("Test ... %v\n", c.NowFunc().UTC()))
- })
- c.Start()
- WaitTestingCron(c)
- if buf.String() != `
- Test ... 2000-01-01 08:12:00 +0000 UTC
- Test ... 2000-01-01 12:12:00 +0000 UTC
- `[1:] {
- t.Error("Unexpected result:", buf.String())
- return
- }
- if NewTestingCronWeek() == nil {
- t.Error("Unexpected result")
- return
- }
- if NewTestingCronMonth() == nil {
- t.Error("Unexpected result")
- return
- }
- // Make sure it is impossible to create a never ending TestingNow object
- startTime, _ := time.Parse(time.RFC3339, "2000-01-01T00:00:00Z")
- endTime, _ := time.Parse(time.RFC3339, "2000-01-07T23:59:59Z")
- _, err := NewTestingNow(endTime, startTime)
- if err == nil || err.Error() != "End time 2000-01-01 00:00:00 +0000 UTC is not after start time 2000-01-07 23:59:59 +0000 UTC" {
- t.Error("Unexpected result:", err)
- return
- }
- }
- func TestCronSpec(t *testing.T) {
- // Do some table testing - spec vs. expected human readable string
- // vs. expected error message vs. expected different spec string (sorted)
- testDaysTable := [][]string{
- // Test * wildcard and numbers
- {"* * * * * *", "every day", "", ""},
- {"* * * 23 * *", "on 23rd of every month", "", ""},
- {"* * * * 12 *", "in December", "", ""},
- {"* * * * * 0", "on Sunday", "", ""},
- {"* * * 12,19,31,23 * *", "on 12th, 19th, 23rd and 31st of every month", "", "* * * 12,19,23,31 * *"},
- {"* * * * 12,11 *", "in November and December", "", "* * * * 11,12 *"},
- {"* * * * * 4,5,6", "on Thursday, Friday and Saturday", "", ""},
- {"* * * * 1,5,8 *", "in January, May and August", "", ""},
- {"* * * 1,2,3,4,18 12 *", "on 1st, 2nd, 3rd, 4th and 18th of December", "", ""},
- {"* * * 1,2,3,4,18 12 4,3", "on Wednesday and Thursday and 1st, 2nd, 3rd, 4th and 18th of December", "", "* * * 1,2,3,4,18 12 3,4"},
- {"* * * 1,2 * 3,4", "on Wednesday and Thursday and 1st and 2nd of every month", "", ""},
- {"* * * 4,5 1,2 *", "on 4th and 5th of January and February", "", ""},
- {"* * * * 1,2 3,4", "on Wednesday and Thursday in January and February", "", ""},
- // Test multiple-of entries
- {"* * * *%23,*%1 * *", "every day", "", "* * * * * *"},
- {"* * * *%23,*%2 * *", "on every 2nd day and every 23rd day of every month", "", "* * * *%2,*%23 * *"},
- {"* * * * *%2,*%12 *", "in every 2nd month and every 12th month", "", ""},
- {"* * * * * *%2,*%7,3", "on every 2nd day of the week, every 7th day of the week and Wednesday", "", ""},
- // Test mix of normal and multiple-of entries
- {"* * * 1,*%2,3,4,*%11,18 11,*%4,12 4,*%3,1", "on every 3rd day of the week, Monday and Thursday and every 2nd day, every 11th day, 1st, 3rd, 4th and 18th of every 4th month, November and December", "", "* * * *%2,*%11,1,3,4,18 *%4,11,12 *%3,1,4"},
- // Test errors
- {"* * * * *", "", "Cron spec must have 6 entries separated by space", ""},
- {"* * * * * x", "", "Cron entries must be a number, '*' or *% and a number", ""},
- }
- for i, row := range testDaysTable {
- res, err := checkDaysSpec(row[0], row[3])
- if err != nil {
- if row[2] == "" || row[2] != err.Error() {
- t.Errorf("Unexpected error for row %v %v: %v", (i + 1), row, err)
- return
- }
- } else if res != row[1] {
- t.Errorf("Unexpected result for row %v %v\nexpected: %v\ngot: %v",
- (i + 1), row, row[1], res)
- return
- }
- }
- testTimeTable := [][]string{
- // Test * wildcard and numbers
- {"* * * * * *", "every second", "", ""},
- {"55 * * * * *", "at second 55 of every minute", "", ""},
- {"* 0 * * * *", "every second of the beginning of every hour", "", ""},
- {"* * 12 * * *", "every second of hour 12:00", "", ""},
- {"0 * * * * *", "at the beginning of every minute", "", ""},
- {"0,59 * * * * *", "at the beginning and the end of every minute", "", ""},
- {"* 0,59 * * * *", "every second of the beginning and the end of every hour", "", ""},
- {"1,3,55 * * * * *", "at second 1, second 3 and second 55 of every minute", "", ""},
- {"0 0,59,30 * * * *", "at the beginning, minute 30 and the end of every hour", "", "0 0,30,59 * * * *"},
- {"* * 0,12,23 * * *", "every second of hour 00:00, hour 12:00 and hour 23:00", "", ""},
- {"0 * 0,12,23 * * *", "every minute of hour 00:00, hour 12:00 and hour 23:00", "", ""},
- {"0 1 0,12,23 * * *", "at minute 1 of hour 00:00, hour 12:00 and hour 23:00", "", ""},
- {"22,33 * 4 * * *", "at second 22 and second 33 of every minute of hour 04:00", "", ""},
- {"22,33 8 4 * * *", "at second 22 and second 33 of minute 8 of hour 04:00", "", ""},
- {"22,33 8 * * * *", "at second 22 and second 33 of minute 8 of every hour", "", ""},
- {"* 8,9,10 2,3 * * *", "every second of minute 8, minute 9 and minute 10 of hour 02:00 and hour 03:00", "", ""},
- // Test multiple-of entries
- {"*%20,*%21 * * * * *", "at every 20th second and every 21st second of every minute", "", ""},
- {"* *%2,*%55 * * * *", "every second of every 2nd minute and every 55th minute of every hour", "", ""},
- {"* * *%13,*%14 * * *", "every second of every 13th hour and every 14th hour", "", ""},
- // Test mix of normal and multiple-of entries
- {"*%2,*%11,1,3,4,18 *%4,11,12 *%3,1,4 * * *", "at every 2nd second, every 11th second, second 1, second 3, second 4 and second 18 of every 4th minute, minute 11 and minute 12 of every 3rd hour, hour 01:00 and hour 04:00", "", ""},
- // Test errors
- {"* 60 * * * *", "", "Cron minute entry must be smaller or equal than 59", ""},
- {"* * 2,5,25 * * *", "", "Cron hour entry must be smaller or equal than 23", ""},
- {"* * * 0 * *", "", "Cron day of month entry must be greater or equal than 1", ""},
- {"* * * *%a * *", "", "Cron day of month entry needs a number after '*%'", ""},
- {"* * * *%0 * *", "", "Cron day of month entry must be greater or equal than 1", ""},
- {"* * * * * x", "", "Cron entries must be a number, '*' or *% and a number", ""},
- }
- for i, row := range testTimeTable {
- res, err := checkTimeSpec(row[0], row[3])
- if err != nil {
- if row[2] == "" || row[2] != err.Error() {
- t.Errorf("Unexpected error for row %v %v: %v", (i + 1), row, err)
- return
- }
- } else if res != row[1] {
- t.Errorf("Unexpected result for row %v %v\nexpected: %v\ngot: %v",
- (i + 1), row, row[1], res)
- return
- }
- }
- // Test combination
- if ss, err := NewCronSpec("* * * * * *"); ss.String() != "every second every day" {
- t.Error("Unexpected string:", ss, err)
- return
- }
- if ss, err := NewCronSpec("1 2 3 4 5 6"); ss.String() != "at second 1 of minute 2 of hour 03:00 on Saturday and 4th of May" {
- t.Error("Unexpected string:", ss, err)
- return
- }
- ss, err := NewCronSpec("1,2 3,4 5,6 7,8 1 6")
- if err != nil || ss.String() != "at second 1 and second 2 of minute 3 and minute 4 of hour 05:00 and hour 06:00 on Saturday and 7th and 8th of January" {
- t.Error("Unexpected string:", ss, err)
- return
- }
- if res := strings.Join(ss.Generate2000Examples(3), "\n"); res != `
- 2000-01-08 05:03:01 +0000 UTC
- 2000-01-08 05:03:02 +0000 UTC
- 2000-01-08 05:04:01 +0000 UTC`[1:] {
- t.Error("Unexpected result:", res)
- return
- }
- ss, err = NewCronSpec("* * * * * *")
- if err != nil || ss.String() != "every second every day" {
- t.Error("Unexpected string:", ss, err)
- return
- }
- if res := strings.Join(ss.Generate2000Examples(3), "\n"); res != `
- 2000-01-01 00:00:00 +0000 UTC
- 2000-01-01 00:00:01 +0000 UTC
- 2000-01-01 00:00:02 +0000 UTC`[1:] {
- t.Error("Unexpected result:", res)
- return
- }
- ss, err = NewCronSpec("0 0 12 *%2 *%2 *")
- if err != nil || ss.String() != "at the beginning of hour 12:00 on every 2nd day of every 2nd month" {
- t.Error("Unexpected string:", ss, err)
- return
- }
- if res := strings.Join(ss.Generate2000Examples(3), "\n"); res != `
- 2000-02-02 12:00:00 +0000 UTC
- 2000-02-04 12:00:00 +0000 UTC
- 2000-02-06 12:00:00 +0000 UTC`[1:] {
- t.Error("Unexpected result:", res)
- return
- }
- }
- func checkDaysSpec(spec, newspec string) (string, error) {
- cs, err := NewCronSpec(spec)
- if err != nil {
- return "", err
- }
- if newspec == "" {
- if spec != cs.SpecString() {
- return "", fmt.Errorf("Spec string is not expected: %v != %v",
- spec, cs.SpecString())
- }
- } else {
- if newspec != cs.SpecString() {
- return "", fmt.Errorf("New spec string is not expected: %v != %v",
- newspec, cs.SpecString())
- }
- }
- return cs.DaysString(), nil
- }
- func checkTimeSpec(spec, newspec string) (string, error) {
- cs, err := NewCronSpec(spec)
- if err != nil {
- return "", err
- }
- if newspec == "" {
- if spec != cs.SpecString() {
- return "", fmt.Errorf("Spec string is not expected: %v != %v",
- spec, cs.SpecString())
- }
- } else {
- if newspec != cs.SpecString() {
- return "", fmt.Errorf("New spec string is not expected: %v != %v",
- newspec, cs.SpecString())
- }
- }
- return cs.TimeString(), nil
- }
|