138 lines
2.7 KiB
Go
138 lines
2.7 KiB
Go
|
|
package timer
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestTracker(t *testing.T) {
|
||
|
|
tk := Start()
|
||
|
|
time.Sleep(10 * time.Millisecond)
|
||
|
|
tk.Record("step1")
|
||
|
|
time.Sleep(20 * time.Millisecond)
|
||
|
|
tk.Record("step2")
|
||
|
|
|
||
|
|
laps := tk.Summarize()
|
||
|
|
if len(laps) != 2 {
|
||
|
|
t.Errorf("expected 2 laps, got %d", len(laps))
|
||
|
|
}
|
||
|
|
if laps[0].Label != "step1" || laps[1].Label != "step2" {
|
||
|
|
t.Errorf("invalid lap labels")
|
||
|
|
}
|
||
|
|
|
||
|
|
desc := tk.Describe()
|
||
|
|
if desc == "" {
|
||
|
|
t.Error("description should not be empty")
|
||
|
|
}
|
||
|
|
t.Log(desc)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRetry(t *testing.T) {
|
||
|
|
count := 0
|
||
|
|
err := Retry(func() error {
|
||
|
|
count++
|
||
|
|
if count < 3 {
|
||
|
|
return errors.New("fail")
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}, WithMaxRetries(3), WithBackoff(10*time.Millisecond, 2.0))
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
t.Errorf("expected success, got %v", err)
|
||
|
|
}
|
||
|
|
if count != 3 {
|
||
|
|
t.Errorf("expected 3 attempts, got %d", count)
|
||
|
|
}
|
||
|
|
|
||
|
|
count = 0
|
||
|
|
err = Retry(func() error {
|
||
|
|
count++
|
||
|
|
return errors.New("always fail")
|
||
|
|
}, WithMaxRetries(2), WithBackoff(5*time.Millisecond, 2.0))
|
||
|
|
|
||
|
|
if err == nil {
|
||
|
|
t.Error("expected error, got nil")
|
||
|
|
}
|
||
|
|
if count != 3 { // 0, 1, 2 = 3 times
|
||
|
|
t.Errorf("expected 3 attempts, got %d", count)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestTimeWheel(t *testing.T) {
|
||
|
|
tw := NewTimeWheel(10*time.Millisecond, 10)
|
||
|
|
tw.Start()
|
||
|
|
defer tw.Stop()
|
||
|
|
|
||
|
|
done := make(chan bool)
|
||
|
|
start := time.Now()
|
||
|
|
tw.AfterFunc(50*time.Millisecond, func() {
|
||
|
|
duration := time.Since(start)
|
||
|
|
if duration < 40*time.Millisecond {
|
||
|
|
t.Errorf("too early: %v", duration)
|
||
|
|
}
|
||
|
|
done <- true
|
||
|
|
})
|
||
|
|
|
||
|
|
select {
|
||
|
|
case <-done:
|
||
|
|
// success
|
||
|
|
case <-time.After(200 * time.Millisecond):
|
||
|
|
t.Fatal("timeout")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestFlow(t *testing.T) {
|
||
|
|
// Debounce test
|
||
|
|
count := 0
|
||
|
|
d := NewDebouncer(20*time.Millisecond, func() {
|
||
|
|
count++
|
||
|
|
})
|
||
|
|
d.Trigger()
|
||
|
|
d.Trigger()
|
||
|
|
d.Trigger()
|
||
|
|
time.Sleep(50 * time.Millisecond)
|
||
|
|
if count != 1 {
|
||
|
|
t.Errorf("debounce failed, count: %d", count)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Throttle test
|
||
|
|
count = 0
|
||
|
|
tr := NewThrottler(20*time.Millisecond, func() {
|
||
|
|
count++
|
||
|
|
})
|
||
|
|
tr.Trigger()
|
||
|
|
tr.Trigger()
|
||
|
|
tr.Trigger()
|
||
|
|
time.Sleep(10 * time.Millisecond)
|
||
|
|
if count != 1 {
|
||
|
|
t.Errorf("throttle initial failed, count: %d", count)
|
||
|
|
}
|
||
|
|
time.Sleep(30 * time.Millisecond)
|
||
|
|
tr.Trigger()
|
||
|
|
time.Sleep(10 * time.Millisecond)
|
||
|
|
if count != 2 {
|
||
|
|
t.Errorf("throttle interval failed, count: %d", count)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRunTimeout(t *testing.T) {
|
||
|
|
res, err := RunTimeout(50*time.Millisecond, func(tk *Tracker) (string, error) {
|
||
|
|
tk.Record("start")
|
||
|
|
time.Sleep(10 * time.Millisecond)
|
||
|
|
tk.Record("middle")
|
||
|
|
return "ok", nil
|
||
|
|
})
|
||
|
|
if err != nil || res != "ok" {
|
||
|
|
t.Errorf("RunTimeout failed: %v, %v", res, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
_, err = RunTimeout(20*time.Millisecond, func(tk *Tracker) (string, error) {
|
||
|
|
time.Sleep(50 * time.Millisecond)
|
||
|
|
return "late", nil
|
||
|
|
})
|
||
|
|
if err != ErrTimeout {
|
||
|
|
t.Errorf("expected ErrTimeout, got %v", err)
|
||
|
|
}
|
||
|
|
}
|