From b261a5edc1c87964bf99cda8f3e911bef4b6ae68 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Thu, 11 Sep 2014 20:32:55 -0700 Subject: [PATCH 1/6] raft: test node block proposal --- raft/node_test.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/raft/node_test.go b/raft/node_test.go index 8527214bd..5e11668da 100644 --- a/raft/node_test.go +++ b/raft/node_test.go @@ -81,6 +81,40 @@ func TestNodeStepUnblock(t *testing.T) { } } +// TestBlockProposal ensures that node will block proposal when it does not +// know who is the current leader; node will direct proposal when it knows +// who is the current leader. +func TestBlockProposal(t *testing.T) { + propsal := false + + n := newNode() + defer n.Stop() + r := newRaft(1, []int64{1}, 10, 1) + r.step = func(r *raft, m raftpb.Message) { + if m.Type == msgProp { + propsal = true + } + } + + go n.run(r) + go n.Propose(context.TODO(), []byte("somedata")) + // give some time for go routines sechduling ... + time.Sleep(time.Millisecond * 2) + if propsal { + t.Fatalf("proposal = %v, want %v", propsal, false) + } + + // assign a lead to raft. + // tick to update the node. + r.lead = 1 + n.Tick() + // give some time for go routines sechduling ... + time.Sleep(time.Millisecond * 2) + if !propsal { + t.Fatalf("proposal = %v, want %v", propsal, true) + } +} + func TestNode(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() From f9ef4538947f4b621412ced38f8f7c21c903973c Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Thu, 11 Sep 2014 22:13:28 -0700 Subject: [PATCH 2/6] raft: test contain updates --- raft/node_test.go | 69 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/raft/node_test.go b/raft/node_test.go index 5e11668da..fde09ecb9 100644 --- a/raft/node_test.go +++ b/raft/node_test.go @@ -2,8 +2,8 @@ package raft import ( "reflect" + "runtime" "testing" - "time" "github.com/coreos/etcd/raft/raftpb" "github.com/coreos/etcd/third_party/code.google.com/p/go.net/context" @@ -85,33 +85,51 @@ func TestNodeStepUnblock(t *testing.T) { // know who is the current leader; node will direct proposal when it knows // who is the current leader. func TestBlockProposal(t *testing.T) { - propsal := false - n := newNode() defer n.Stop() r := newRaft(1, []int64{1}, 10, 1) - r.step = func(r *raft, m raftpb.Message) { - if m.Type == msgProp { - propsal = true - } - } - go n.run(r) - go n.Propose(context.TODO(), []byte("somedata")) - // give some time for go routines sechduling ... - time.Sleep(time.Millisecond * 2) - if propsal { - t.Fatalf("proposal = %v, want %v", propsal, false) + + errc := make(chan error, 1) + go func() { + errc <- n.Propose(context.TODO(), []byte("somedata")) + }() + + mustEnoughSched() + select { + case err := <-errc: + t.Errorf("err = %v, want blocking", err) + default: } - // assign a lead to raft. - // tick to update the node. - r.lead = 1 - n.Tick() - // give some time for go routines sechduling ... - time.Sleep(time.Millisecond * 2) - if !propsal { - t.Fatalf("proposal = %v, want %v", propsal, true) + n.Campaign(context.TODO()) + mustEnoughSched() + 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) + } } } @@ -174,3 +192,10 @@ func TestNodeRestart(t *testing.T) { default: } } + +func mustEnoughSched() { + // possibility enough to sched upto 10 go routines. + for i := 0; i < 10000; i++ { + runtime.Gosched() + } +} From 0c09862494ece343fc6dc536cb722beb8b81ecd6 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Fri, 12 Sep 2014 11:48:08 -0700 Subject: [PATCH 3/6] raft: add isStateEqual test --- raft/node.go | 2 +- raft/node_test.go | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/raft/node.go b/raft/node.go index 275324306..8b727dded 100644 --- a/raft/node.go +++ b/raft/node.go @@ -35,7 +35,7 @@ type Ready struct { } func isStateEqual(a, b pb.State) bool { - return a.Term == b.Term && a.Vote == b.Vote && a.LastIndex == b.LastIndex + return a.Term == b.Term && a.Vote == b.Vote && a.Commit == b.Commit && a.LastIndex == b.LastIndex } func IsEmptyState(st pb.State) bool { diff --git a/raft/node_test.go b/raft/node_test.go index fde09ecb9..092bb1aba 100644 --- a/raft/node_test.go +++ b/raft/node_test.go @@ -4,6 +4,7 @@ import ( "reflect" "runtime" "testing" + "time" "github.com/coreos/etcd/raft/raftpb" "github.com/coreos/etcd/third_party/code.google.com/p/go.net/context" @@ -82,7 +83,7 @@ func TestNodeStepUnblock(t *testing.T) { } // TestBlockProposal ensures that node will block proposal when it does not -// know who is the current leader; node will direct proposal when it knows +// 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() @@ -193,6 +194,25 @@ func TestNodeRestart(t *testing.T) { } } +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) + } + } +} + func mustEnoughSched() { // possibility enough to sched upto 10 go routines. for i := 0; i < 10000; i++ { From 5c884c7797996a3e614e29d40c5dca9e5e29c9a0 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Fri, 12 Sep 2014 12:18:30 -0700 Subject: [PATCH 4/6] raft: better comment and naming --- raft/node_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/raft/node_test.go b/raft/node_test.go index 092bb1aba..563dbabd4 100644 --- a/raft/node_test.go +++ b/raft/node_test.go @@ -96,7 +96,7 @@ func TestBlockProposal(t *testing.T) { errc <- n.Propose(context.TODO(), []byte("somedata")) }() - mustEnoughSched() + forceGoSched() select { case err := <-errc: t.Errorf("err = %v, want blocking", err) @@ -104,7 +104,7 @@ func TestBlockProposal(t *testing.T) { } n.Campaign(context.TODO()) - mustEnoughSched() + forceGoSched() select { case err := <-errc: if err != nil { @@ -213,7 +213,9 @@ func TestIsStateEqual(t *testing.T) { } } -func mustEnoughSched() { +// 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() From 45f56a53778d90174930e7531fe83df1909d06f4 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Fri, 12 Sep 2014 12:27:18 -0700 Subject: [PATCH 5/6] raft: forceGoSched -> forceGosched --- raft/node_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/raft/node_test.go b/raft/node_test.go index 563dbabd4..82c2acded 100644 --- a/raft/node_test.go +++ b/raft/node_test.go @@ -96,7 +96,7 @@ func TestBlockProposal(t *testing.T) { errc <- n.Propose(context.TODO(), []byte("somedata")) }() - forceGoSched() + forceGosched() select { case err := <-errc: t.Errorf("err = %v, want blocking", err) @@ -104,7 +104,7 @@ func TestBlockProposal(t *testing.T) { } n.Campaign(context.TODO()) - forceGoSched() + forceGosched() select { case err := <-errc: if err != nil { @@ -215,7 +215,7 @@ func TestIsStateEqual(t *testing.T) { // WARNING: This is a hack. // Remove this when we are able to block/check the status of the go-routines. -func forceGoSched() { +func forceGosched() { // possibility enough to sched upto 10 go routines. for i := 0; i < 10000; i++ { runtime.Gosched() From ccee264b7d99922d8c46420d9a3fad233fe94498 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Fri, 12 Sep 2014 12:28:15 -0700 Subject: [PATCH 6/6] raft: move defer after run --- raft/node_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raft/node_test.go b/raft/node_test.go index 82c2acded..fc5645071 100644 --- a/raft/node_test.go +++ b/raft/node_test.go @@ -87,9 +87,9 @@ func TestNodeStepUnblock(t *testing.T) { // who is the current leader. func TestBlockProposal(t *testing.T) { n := newNode() - defer n.Stop() r := newRaft(1, []int64{1}, 10, 1) go n.run(r) + defer n.Stop() errc := make(chan error, 1) go func() {