|
|
@@ -2,6 +2,7 @@ package raft
|
|
|
|
|
|
import (
|
|
|
"reflect"
|
|
|
+ "runtime"
|
|
|
"testing"
|
|
|
"time"
|
|
|
|
|
|
@@ -81,6 +82,58 @@ func TestNodeStepUnblock(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// TestBlockProposal ensures that node will block proposal when it does not
|
|
|
+// know who is the current leader; node will accept proposal when it knows
|
|
|
+// who is the current leader.
|
|
|
+func TestBlockProposal(t *testing.T) {
|
|
|
+ n := newNode()
|
|
|
+ r := newRaft(1, []int64{1}, 10, 1)
|
|
|
+ go n.run(r)
|
|
|
+ defer n.Stop()
|
|
|
+
|
|
|
+ errc := make(chan error, 1)
|
|
|
+ go func() {
|
|
|
+ errc <- n.Propose(context.TODO(), []byte("somedata"))
|
|
|
+ }()
|
|
|
+
|
|
|
+ forceGosched()
|
|
|
+ select {
|
|
|
+ case err := <-errc:
|
|
|
+ t.Errorf("err = %v, want blocking", err)
|
|
|
+ default:
|
|
|
+ }
|
|
|
+
|
|
|
+ n.Campaign(context.TODO())
|
|
|
+ forceGosched()
|
|
|
+ select {
|
|
|
+ case err := <-errc:
|
|
|
+ if err != nil {
|
|
|
+ t.Errorf("err = %v, want %v", err, nil)
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ t.Errorf("blocking proposal, want unblocking")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestReadyContainUpdates(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ rd Ready
|
|
|
+ wcontain bool
|
|
|
+ }{
|
|
|
+ {Ready{}, false},
|
|
|
+ {Ready{State: raftpb.State{Vote: 1}}, true},
|
|
|
+ {Ready{Entries: make([]raftpb.Entry, 1, 1)}, true},
|
|
|
+ {Ready{CommittedEntries: make([]raftpb.Entry, 1, 1)}, true},
|
|
|
+ {Ready{Messages: make([]raftpb.Message, 1, 1)}, true},
|
|
|
+ }
|
|
|
+
|
|
|
+ for i, tt := range tests {
|
|
|
+ if tt.rd.containsUpdates() != tt.wcontain {
|
|
|
+ t.Errorf("#%d: containUpdates = %v, want %v", i, tt.rd.containsUpdates(), tt.wcontain)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func TestNode(t *testing.T) {
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
defer cancel()
|
|
|
@@ -140,3 +193,31 @@ func TestNodeRestart(t *testing.T) {
|
|
|
default:
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+func TestIsStateEqual(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ st raftpb.State
|
|
|
+ we bool
|
|
|
+ }{
|
|
|
+ {emptyState, true},
|
|
|
+ {raftpb.State{Vote: 1}, false},
|
|
|
+ {raftpb.State{Commit: 1}, false},
|
|
|
+ {raftpb.State{Term: 1}, false},
|
|
|
+ {raftpb.State{LastIndex: 1}, false},
|
|
|
+ }
|
|
|
+
|
|
|
+ for i, tt := range tests {
|
|
|
+ if isStateEqual(tt.st, emptyState) != tt.we {
|
|
|
+ t.Errorf("#%d, equal = %v, want %v", i, isStateEqual(tt.st, emptyState), tt.we)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// WARNING: This is a hack.
|
|
|
+// Remove this when we are able to block/check the status of the go-routines.
|
|
|
+func forceGosched() {
|
|
|
+ // possibility enough to sched upto 10 go routines.
|
|
|
+ for i := 0; i < 10000; i++ {
|
|
|
+ runtime.Gosched()
|
|
|
+ }
|
|
|
+}
|