瀏覽代碼

Add some tests for the job running.

Rob Figueiredo 13 年之前
父節點
當前提交
79c6cbe5a7
共有 3 個文件被更改,包括 72 次插入5 次删除
  1. 15 2
      README.md
  2. 2 2
      cron.go
  3. 55 1
      cron_test.go

+ 15 - 2
README.md

@@ -12,14 +12,16 @@ them in their own goroutines.
 c := new(Cron)
 c.Add("0 5 * * * *", func() { fmt.Println("Every 5 minutes") })
 c.Add("@hourly", func() { fmt.Println("Every hour") })
-go c.Run()  // Scheduler blocks until stopped, so run it in its own goroutine.
+go c.Start()  // Scheduler blocks until stopped, so run it in its own goroutine.
 ..
 // Funcs are invoked in their own goroutine, asynchronously.
+...
+// Funcs may also be added to a running Cron
+c.Add("@daily, func() { fmt.Println("Every day") })
 ..
 c.Stop()  // Stop the scheduler (does not stop any jobs already running).
 ```
 
-
 ## CRON Expression
 
 This section describes the specific format accepted by this cron.  Some snippets
@@ -93,3 +95,14 @@ provided by the [Go time package](http://www.golang.org/pkg/time)).
 Be aware that jobs scheduled during daylight-savings transitions will not be
 run!
 
+
+## Implementation
+
+Cron entries are stored in an array, sorted by their next activation time.  Cron
+sleeps until the next job is due to be run.
+
+Upon waking:
+* it runs each entry that is active on that second
+* it calculates the next run times for the jobs that were run
+* it re-sorts the array of entries by next activation time.
+* it goes to sleep until the soonest job.

+ 2 - 2
cron.go

@@ -32,8 +32,8 @@ func (s byTime) Less(i, j int) bool { return s[i].Next.Before(s[j].Next) }
 func New() *Cron {
 	return &Cron{
 		Entries: nil,
-		add:     make(chan *Entry),
-		stop:    make(chan struct{}),
+		add:     make(chan *Entry, 1),
+		stop:    make(chan struct{}, 1),
 	}
 }
 

+ 55 - 1
cron_test.go

@@ -2,7 +2,61 @@ package cron
 
 import (
 	"testing"
+	"time"
 )
 
-func TestActivation2(t *testing.T) {
+// Start and stop cron with no entries.
+func TestNoEntries(t *testing.T) {
+	cron := New()
+	done := startAndSignal(cron)
+	go cron.Stop()
+
+	select {
+	case <-time.After(1 * time.Second):
+		t.FailNow()
+	case <-done:
+	}
+}
+
+// Add a job, start cron, expect it runs.
+func TestAddBeforeRunning(t *testing.T) {
+	cron := New()
+	cron.Add("* * * * * ?", func() {
+		cron.Stop()
+	})
+	done := startAndSignal(cron)
+
+	// Give cron 2 seconds to run our job (which is always activated).
+	select {
+	case <-time.After(2 * time.Second):
+		t.FailNow()
+	case <-done:
+	}
+}
+
+// Start cron, add a job, expect it runs.
+func TestAddWhileRunning(t *testing.T) {
+	cron := New()
+	done := startAndSignal(cron)
+	go func() {
+		cron.Add("* * * * * ?", func() {
+			cron.Stop()
+		})
+	}()
+
+	select {
+	case <-time.After(2 * time.Second):
+		t.FailNow()
+	case <-done:
+	}
+}
+
+// Return a channel that signals when the cron's Start() method returns.
+func startAndSignal(cron *Cron) <-chan struct{} {
+	ch := make(chan struct{})
+	go func() {
+		cron.Start()
+		ch <- struct{}{}
+	}()
+	return ch
 }