log_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. package raft
  2. import (
  3. "reflect"
  4. "testing"
  5. )
  6. // TestAppend ensures:
  7. // 1. If an existing entry conflicts with a new one (same index
  8. // but different terms), delete the existing entry and all that
  9. // follow it
  10. // 2.Append any new entries not already in the log
  11. func TestAppend(t *testing.T) {
  12. previousEnts := []Entry{{Term: 1}, {Term: 2}}
  13. tests := []struct {
  14. after int64
  15. ents []Entry
  16. windex int64
  17. wents []Entry
  18. }{
  19. {
  20. 2,
  21. []Entry{},
  22. 2,
  23. []Entry{{Term: 1}, {Term: 2}},
  24. },
  25. {
  26. 2,
  27. []Entry{{Term: 2}},
  28. 3,
  29. []Entry{{Term: 1}, {Term: 2}, {Term: 2}},
  30. },
  31. // conflicts with index 1
  32. {
  33. 0,
  34. []Entry{{Term: 2}},
  35. 1,
  36. []Entry{{Term: 2}},
  37. },
  38. // conflicts with index 2
  39. {
  40. 1,
  41. []Entry{{Term: 3}, {Term: 3}},
  42. 3,
  43. []Entry{{Term: 1}, {Term: 3}, {Term: 3}},
  44. },
  45. }
  46. for i, tt := range tests {
  47. log := newLog()
  48. log.ents = append(log.ents, previousEnts...)
  49. index := log.append(tt.after, tt.ents...)
  50. if index != tt.windex {
  51. t.Errorf("#%d: lastIndex = %d, want %d", i, index, tt.windex)
  52. }
  53. if g := log.entries(1); !reflect.DeepEqual(g, tt.wents) {
  54. t.Errorf("#%d: logEnts = %+v, want %+v", i, g, tt.wents)
  55. }
  56. }
  57. }
  58. // TestCompactionSideEffects ensures that all the log related funcationality works correctly after
  59. // a compaction.
  60. func TestCompactionSideEffects(t *testing.T) {
  61. var i int64
  62. lastIndex := int64(1000)
  63. log := newLog()
  64. for i = 0; i < lastIndex; i++ {
  65. log.append(int64(i), Entry{Term: int64(i + 1)})
  66. }
  67. log.compact(500)
  68. if log.lastIndex() != lastIndex {
  69. t.Errorf("lastIndex = %d, want %d", log.lastIndex(), lastIndex)
  70. }
  71. for i := log.offset; i <= log.lastIndex(); i++ {
  72. if log.term(i) != i {
  73. t.Errorf("term(%d) = %d, want %d", i, log.term(i), i)
  74. }
  75. }
  76. for i := log.offset; i <= log.lastIndex(); i++ {
  77. if !log.matchTerm(i, i) {
  78. t.Errorf("matchTerm(%d) = false, want true", i)
  79. }
  80. }
  81. prev := log.lastIndex()
  82. log.append(log.lastIndex(), Entry{Term: log.lastIndex() + 1})
  83. if log.lastIndex() != prev+1 {
  84. t.Errorf("lastIndex = %d, want = %d", log.lastIndex(), prev+1)
  85. }
  86. ents := log.entries(log.lastIndex())
  87. if len(ents) != 1 {
  88. t.Errorf("len(entries) = %d, want = %d", len(ents), 1)
  89. }
  90. }
  91. //TestCompaction ensures that the number of log entreis is correct after compactions.
  92. func TestCompaction(t *testing.T) {
  93. tests := []struct {
  94. app int
  95. compact []int64
  96. wleft []int
  97. wallow bool
  98. }{
  99. // out of upper bound
  100. {1000, []int64{1001}, []int{-1}, false},
  101. {1000, []int64{300, 500, 800, 900}, []int{701, 501, 201, 101}, true},
  102. // out of lower bound
  103. {1000, []int64{300, 299}, []int{701, -1}, false},
  104. }
  105. for i, tt := range tests {
  106. func() {
  107. defer func() {
  108. if r := recover(); r != nil {
  109. if tt.wallow == true {
  110. t.Errorf("%d: allow = %v, want %v", i, false, true)
  111. }
  112. }
  113. }()
  114. log := newLog()
  115. for i := 0; i < tt.app; i++ {
  116. log.append(int64(i), Entry{})
  117. }
  118. for j := 0; j < len(tt.compact); j++ {
  119. log.compact(tt.compact[j])
  120. if len(log.ents) != tt.wleft[j] {
  121. t.Errorf("#%d.%d len = %d, want %d", i, j, len(log.ents), tt.wleft[j])
  122. }
  123. }
  124. }()
  125. }
  126. }
  127. func TestLogRestore(t *testing.T) {
  128. var i int64
  129. log := newLog()
  130. for i = 0; i < 100; i++ {
  131. log.append(i, Entry{Term: i + 1})
  132. }
  133. index := int64(1000)
  134. term := int64(1000)
  135. log.restore(index, term)
  136. // only has the guard entry
  137. if len(log.ents) != 1 {
  138. t.Errorf("len = %d, want 0", len(log.ents))
  139. }
  140. if log.offset != index {
  141. t.Errorf("offset = %d, want %d", log.offset, index)
  142. }
  143. if log.applied != index {
  144. t.Errorf("applied = %d, want %d", log.applied, index)
  145. }
  146. if log.committed != index {
  147. t.Errorf("comitted = %d, want %d", log.committed, index)
  148. }
  149. if log.term(index) != term {
  150. t.Errorf("term = %d, want %d", log.term(index), term)
  151. }
  152. }
  153. func TestIsOutOfBounds(t *testing.T) {
  154. offset := int64(100)
  155. num := int64(100)
  156. l := &log{offset: offset, ents: make([]Entry, num)}
  157. tests := []struct {
  158. index int64
  159. w bool
  160. }{
  161. {offset - 1, true},
  162. {offset, false},
  163. {offset + num/2, false},
  164. {offset + num - 1, false},
  165. {offset + num, true},
  166. }
  167. for i, tt := range tests {
  168. g := l.isOutOfBounds(tt.index)
  169. if g != tt.w {
  170. t.Errorf("#%d: isOutOfBounds = %v, want %v", i, g, tt.w)
  171. }
  172. }
  173. }
  174. func TestAt(t *testing.T) {
  175. var i int64
  176. offset := int64(100)
  177. num := int64(100)
  178. l := &log{offset: offset}
  179. for i = 0; i < num; i++ {
  180. l.ents = append(l.ents, Entry{Term: i})
  181. }
  182. tests := []struct {
  183. index int64
  184. w *Entry
  185. }{
  186. {offset - 1, nil},
  187. {offset, &Entry{Term: 0}},
  188. {offset + num/2, &Entry{Term: num / 2}},
  189. {offset + num - 1, &Entry{Term: num - 1}},
  190. {offset + num, nil},
  191. }
  192. for i, tt := range tests {
  193. g := l.at(tt.index)
  194. if !reflect.DeepEqual(g, tt.w) {
  195. t.Errorf("#%d: at = %v, want %v", i, g, tt.w)
  196. }
  197. }
  198. }
  199. func TestSlice(t *testing.T) {
  200. var i int64
  201. offset := int64(100)
  202. num := int64(100)
  203. l := &log{offset: offset}
  204. for i = 0; i < num; i++ {
  205. l.ents = append(l.ents, Entry{Term: i})
  206. }
  207. tests := []struct {
  208. from int64
  209. to int64
  210. w []Entry
  211. }{
  212. {offset - 1, offset + 1, nil},
  213. {offset, offset + 1, []Entry{{Term: 0}}},
  214. {offset + num/2, offset + num/2 + 1, []Entry{{Term: num / 2}}},
  215. {offset + num - 1, offset + num, []Entry{{Term: num - 1}}},
  216. {offset + num, offset + num + 1, nil},
  217. {offset + num/2, offset + num/2, nil},
  218. {offset + num/2, offset + num/2 - 1, nil},
  219. }
  220. for i, tt := range tests {
  221. g := l.slice(tt.from, tt.to)
  222. if !reflect.DeepEqual(g, tt.w) {
  223. t.Errorf("#%d: from %d to %d = %v, want %v", i, tt.from, tt.to, g, tt.w)
  224. }
  225. }
  226. }