1// Copyright 2015 The etcd Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package raft 16 17import ( 18 "math" 19 "reflect" 20 "strings" 21 "testing" 22 23 pb "github.com/coreos/etcd/raft/raftpb" 24) 25 26var testFormatter EntryFormatter = func(data []byte) string { 27 return strings.ToUpper(string(data)) 28} 29 30func TestDescribeEntry(t *testing.T) { 31 entry := pb.Entry{ 32 Term: 1, 33 Index: 2, 34 Type: pb.EntryNormal, 35 Data: []byte("hello\x00world"), 36 } 37 38 defaultFormatted := DescribeEntry(entry, nil) 39 if defaultFormatted != "1/2 EntryNormal \"hello\\x00world\"" { 40 t.Errorf("unexpected default output: %s", defaultFormatted) 41 } 42 43 customFormatted := DescribeEntry(entry, testFormatter) 44 if customFormatted != "1/2 EntryNormal HELLO\x00WORLD" { 45 t.Errorf("unexpected custom output: %s", customFormatted) 46 } 47} 48 49func TestLimitSize(t *testing.T) { 50 ents := []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 6}} 51 tests := []struct { 52 maxsize uint64 53 wentries []pb.Entry 54 }{ 55 {math.MaxUint64, []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 6}}}, 56 // even if maxsize is zero, the first entry should be returned 57 {0, []pb.Entry{{Index: 4, Term: 4}}}, 58 // limit to 2 59 {uint64(ents[0].Size() + ents[1].Size()), []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}}}, 60 // limit to 2 61 {uint64(ents[0].Size() + ents[1].Size() + ents[2].Size()/2), []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}}}, 62 {uint64(ents[0].Size() + ents[1].Size() + ents[2].Size() - 1), []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}}}, 63 // all 64 {uint64(ents[0].Size() + ents[1].Size() + ents[2].Size()), []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 6}}}, 65 } 66 67 for i, tt := range tests { 68 if !reflect.DeepEqual(limitSize(ents, tt.maxsize), tt.wentries) { 69 t.Errorf("#%d: entries = %v, want %v", i, limitSize(ents, tt.maxsize), tt.wentries) 70 } 71 } 72} 73 74func TestIsLocalMsg(t *testing.T) { 75 tests := []struct { 76 msgt pb.MessageType 77 isLocal bool 78 }{ 79 {pb.MsgHup, true}, 80 {pb.MsgBeat, true}, 81 {pb.MsgUnreachable, true}, 82 {pb.MsgSnapStatus, true}, 83 {pb.MsgCheckQuorum, true}, 84 {pb.MsgTransferLeader, false}, 85 {pb.MsgProp, false}, 86 {pb.MsgApp, false}, 87 {pb.MsgAppResp, false}, 88 {pb.MsgVote, false}, 89 {pb.MsgVoteResp, false}, 90 {pb.MsgSnap, false}, 91 {pb.MsgHeartbeat, false}, 92 {pb.MsgHeartbeatResp, false}, 93 {pb.MsgTimeoutNow, false}, 94 {pb.MsgReadIndex, false}, 95 {pb.MsgReadIndexResp, false}, 96 {pb.MsgPreVote, false}, 97 {pb.MsgPreVoteResp, false}, 98 } 99 100 for i, tt := range tests { 101 got := IsLocalMsg(tt.msgt) 102 if got != tt.isLocal { 103 t.Errorf("#%d: got %v, want %v", i, got, tt.isLocal) 104 } 105 } 106} 107