136 lines
4.1 KiB
Go
136 lines
4.1 KiB
Go
package raft_test
|
|
|
|
import (
|
|
"flag"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/influxdb/influxdb/raft"
|
|
)
|
|
|
|
var (
|
|
goschedTimeout = flag.Duration("gosched", 100*time.Millisecond, "gosched() delay")
|
|
)
|
|
|
|
// DefaultTime represents the time that the test clock is initialized to.
|
|
// Defaults to midnight on Jan 1, 2000 UTC
|
|
var DefaultTime = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
// Ensure the AfterApplyInterval returns a channel that fires after the default apply interval.
|
|
func TestClock_AfterApplyInterval(t *testing.T) {
|
|
c := raft.NewClock()
|
|
c.ApplyInterval = 10 * time.Millisecond
|
|
t0 := time.Now()
|
|
<-c.AfterApplyInterval()
|
|
if d := time.Since(t0); d < c.ApplyInterval {
|
|
t.Fatalf("channel fired too soon: %v", d)
|
|
}
|
|
}
|
|
|
|
// Ensure the AfterElectionTimeout returns a channel that fires after the clock's election timeout.
|
|
func TestClock_AfterElectionTimeout(t *testing.T) {
|
|
c := raft.NewClock()
|
|
c.ElectionTimeout = 10 * time.Millisecond
|
|
t0 := time.Now()
|
|
<-c.AfterElectionTimeout()
|
|
if d := time.Since(t0); d < c.ElectionTimeout {
|
|
t.Fatalf("channel fired too soon: %v", d)
|
|
}
|
|
}
|
|
|
|
// Ensure the AfterHeartbeatInterval returns a channel that fires after the clock's heartbeat interval.
|
|
func TestClock_AfterHeartbeatInterval(t *testing.T) {
|
|
c := raft.NewClock()
|
|
c.HeartbeatInterval = 10 * time.Millisecond
|
|
t0 := time.Now()
|
|
<-c.AfterHeartbeatInterval()
|
|
if d := time.Since(t0); d < c.HeartbeatInterval {
|
|
t.Fatalf("channel fired too soon: %v", d)
|
|
}
|
|
}
|
|
|
|
// Ensure the AfterReconnectTimeout returns a channel that fires after the clock's reconnect interval.
|
|
func TestClock_AfterReconnectTimeout(t *testing.T) {
|
|
c := raft.NewClock()
|
|
c.ReconnectTimeout = 10 * time.Millisecond
|
|
t0 := time.Now()
|
|
<-c.AfterReconnectTimeout()
|
|
if d := time.Since(t0); d < c.ReconnectTimeout {
|
|
t.Fatalf("channel fired too soon: %v", d)
|
|
}
|
|
}
|
|
|
|
// Ensure the clock can return the current time.
|
|
func TestClock_Now(t *testing.T) {
|
|
now := raft.NewClock().Now()
|
|
if exp := time.Now(); exp.Sub(now) > 1*time.Second {
|
|
t.Fatalf("clock time is different than wall time: exp=%v, got=%v", exp, now)
|
|
}
|
|
}
|
|
|
|
// Clock represents a testable clock.
|
|
type Clock struct {
|
|
now time.Time
|
|
applyChan chan chan struct{}
|
|
electionChan chan chan struct{}
|
|
heartbeatChan chan chan struct{}
|
|
reconnectChan chan chan struct{}
|
|
|
|
NowFunc func() time.Time
|
|
AfterApplyIntervalFunc func() <-chan chan struct{}
|
|
AfterElectionTimeoutFunc func() <-chan chan struct{}
|
|
AfterHeartbeatIntervalFunc func() <-chan chan struct{}
|
|
AfterReconnectTimeoutFunc func() <-chan chan struct{}
|
|
}
|
|
|
|
// NewClock returns an instance of Clock with default.
|
|
func NewClock() *Clock {
|
|
c := &Clock{
|
|
now: DefaultTime,
|
|
applyChan: make(chan chan struct{}, 0),
|
|
electionChan: make(chan chan struct{}, 0),
|
|
heartbeatChan: make(chan chan struct{}, 0),
|
|
reconnectChan: make(chan chan struct{}, 0),
|
|
}
|
|
|
|
// Set default functions.
|
|
c.NowFunc = func() time.Time { return c.now }
|
|
c.AfterApplyIntervalFunc = func() <-chan chan struct{} { return c.applyChan }
|
|
c.AfterElectionTimeoutFunc = func() <-chan chan struct{} { return c.electionChan }
|
|
c.AfterHeartbeatIntervalFunc = func() <-chan chan struct{} { return c.heartbeatChan }
|
|
c.AfterReconnectTimeoutFunc = func() <-chan chan struct{} { return c.reconnectChan }
|
|
return c
|
|
}
|
|
|
|
func (c *Clock) apply() {
|
|
ch := make(chan struct{}, 0)
|
|
c.applyChan <- ch
|
|
<-ch
|
|
}
|
|
|
|
func (c *Clock) election() {
|
|
ch := make(chan struct{}, 0)
|
|
c.electionChan <- ch
|
|
<-ch
|
|
}
|
|
|
|
func (c *Clock) heartbeat() {
|
|
ch := make(chan struct{}, 0)
|
|
c.heartbeatChan <- ch
|
|
<-ch
|
|
}
|
|
|
|
func (c *Clock) reconnect() {
|
|
ch := make(chan struct{}, 0)
|
|
c.reconnectChan <- ch
|
|
<-ch
|
|
}
|
|
|
|
func (c *Clock) Now() time.Time { return c.NowFunc() }
|
|
func (c *Clock) AfterApplyInterval() <-chan chan struct{} { return c.AfterApplyIntervalFunc() }
|
|
func (c *Clock) AfterElectionTimeout() <-chan chan struct{} { return c.AfterElectionTimeoutFunc() }
|
|
func (c *Clock) AfterHeartbeatInterval() <-chan chan struct{} { return c.AfterHeartbeatIntervalFunc() }
|
|
func (c *Clock) AfterReconnectTimeout() <-chan chan struct{} { return c.AfterReconnectTimeoutFunc() }
|
|
|
|
func gosched() { time.Sleep(*goschedTimeout) }
|