quick_test.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // Copyright 2019 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. package confchange
  15. import (
  16. "fmt"
  17. "math/rand"
  18. "reflect"
  19. "testing"
  20. "testing/quick"
  21. pb "go.etcd.io/etcd/raft/raftpb"
  22. "go.etcd.io/etcd/raft/tracker"
  23. )
  24. // TestConfChangeQuick uses quickcheck to verify that simple and joint config
  25. // changes arrive at the same result.
  26. func TestConfChangeQuick(t *testing.T) {
  27. cfg := &quick.Config{
  28. MaxCount: 1000,
  29. }
  30. // Log the first couple of runs to give some indication of things working
  31. // as intended.
  32. const infoCount = 5
  33. runWithJoint := func(c *Changer, ccs []pb.ConfChangeSingle) error {
  34. cfg, prs, err := c.EnterJoint(false /* autoLeave */, ccs...)
  35. if err != nil {
  36. return err
  37. }
  38. // Also do this with autoLeave on, just to check that we'd get the same
  39. // result.
  40. cfg2a, prs2a, err := c.EnterJoint(true /* autoLeave */, ccs...)
  41. if err != nil {
  42. return err
  43. }
  44. cfg2a.AutoLeave = false
  45. if !reflect.DeepEqual(cfg, cfg2a) || !reflect.DeepEqual(prs, prs2a) {
  46. return fmt.Errorf("cfg: %+v\ncfg2a: %+v\nprs: %+v\nprs2a: %+v",
  47. cfg, cfg2a, prs, prs2a)
  48. }
  49. c.Tracker.Config = cfg
  50. c.Tracker.Progress = prs
  51. cfg2b, prs2b, err := c.LeaveJoint()
  52. if err != nil {
  53. return err
  54. }
  55. // Reset back to the main branch with autoLeave=false.
  56. c.Tracker.Config = cfg
  57. c.Tracker.Progress = prs
  58. cfg, prs, err = c.LeaveJoint()
  59. if err != nil {
  60. return err
  61. }
  62. if !reflect.DeepEqual(cfg, cfg2b) || !reflect.DeepEqual(prs, prs2b) {
  63. return fmt.Errorf("cfg: %+v\ncfg2b: %+v\nprs: %+v\nprs2b: %+v",
  64. cfg, cfg2b, prs, prs2b)
  65. }
  66. c.Tracker.Config = cfg
  67. c.Tracker.Progress = prs
  68. return nil
  69. }
  70. runWithSimple := func(c *Changer, ccs []pb.ConfChangeSingle) error {
  71. for _, cc := range ccs {
  72. cfg, prs, err := c.Simple(cc)
  73. if err != nil {
  74. return err
  75. }
  76. c.Tracker.Config, c.Tracker.Progress = cfg, prs
  77. }
  78. return nil
  79. }
  80. type testFunc func(*Changer, []pb.ConfChangeSingle) error
  81. wrapper := func(invoke testFunc) func(setup initialChanges, ccs confChanges) (*Changer, error) {
  82. return func(setup initialChanges, ccs confChanges) (*Changer, error) {
  83. tr := tracker.MakeProgressTracker(10)
  84. c := &Changer{
  85. Tracker: tr,
  86. LastIndex: 10,
  87. }
  88. if err := runWithSimple(c, setup); err != nil {
  89. return nil, err
  90. }
  91. err := invoke(c, ccs)
  92. return c, err
  93. }
  94. }
  95. var n int
  96. f1 := func(setup initialChanges, ccs confChanges) *Changer {
  97. c, err := wrapper(runWithSimple)(setup, ccs)
  98. if err != nil {
  99. t.Fatal(err)
  100. }
  101. if n < infoCount {
  102. t.Log("initial setup:", Describe(setup...))
  103. t.Log("changes:", Describe(ccs...))
  104. t.Log(c.Tracker.Config)
  105. t.Log(c.Tracker.Progress)
  106. }
  107. n++
  108. return c
  109. }
  110. f2 := func(setup initialChanges, ccs confChanges) *Changer {
  111. c, err := wrapper(runWithJoint)(setup, ccs)
  112. if err != nil {
  113. t.Fatal(err)
  114. }
  115. return c
  116. }
  117. err := quick.CheckEqual(f1, f2, cfg)
  118. if err == nil {
  119. return
  120. }
  121. cErr, ok := err.(*quick.CheckEqualError)
  122. if !ok {
  123. t.Fatal(err)
  124. }
  125. t.Error("setup:", Describe(cErr.In[0].([]pb.ConfChangeSingle)...))
  126. t.Error("ccs:", Describe(cErr.In[1].([]pb.ConfChangeSingle)...))
  127. t.Errorf("out1: %+v\nout2: %+v", cErr.Out1, cErr.Out2)
  128. }
  129. type confChangeTyp pb.ConfChangeType
  130. func (confChangeTyp) Generate(rand *rand.Rand, _ int) reflect.Value {
  131. return reflect.ValueOf(confChangeTyp(rand.Intn(4)))
  132. }
  133. type confChanges []pb.ConfChangeSingle
  134. func genCC(num func() int, id func() uint64, typ func() pb.ConfChangeType) []pb.ConfChangeSingle {
  135. var ccs []pb.ConfChangeSingle
  136. n := num()
  137. for i := 0; i < n; i++ {
  138. ccs = append(ccs, pb.ConfChangeSingle{Type: typ(), NodeID: id()})
  139. }
  140. return ccs
  141. }
  142. func (confChanges) Generate(rand *rand.Rand, _ int) reflect.Value {
  143. num := func() int {
  144. return 1 + rand.Intn(9)
  145. }
  146. id := func() uint64 {
  147. // Note that num() >= 1, so we're never returning 1 from this method,
  148. // meaning that we'll never touch NodeID one, which is special to avoid
  149. // voterless configs altogether in this test.
  150. return 1 + uint64(num())
  151. }
  152. typ := func() pb.ConfChangeType {
  153. return pb.ConfChangeType(rand.Intn(len(pb.ConfChangeType_name)))
  154. }
  155. return reflect.ValueOf(genCC(num, id, typ))
  156. }
  157. type initialChanges []pb.ConfChangeSingle
  158. func (initialChanges) Generate(rand *rand.Rand, _ int) reflect.Value {
  159. num := func() int {
  160. return 1 + rand.Intn(5)
  161. }
  162. id := func() uint64 { return uint64(num()) }
  163. typ := func() pb.ConfChangeType {
  164. return pb.ConfChangeAddNode
  165. }
  166. // NodeID one is special - it's in the initial config and will be a voter
  167. // always (this is to avoid uninteresting edge cases where the simple conf
  168. // changes can't easily make progress).
  169. ccs := append([]pb.ConfChangeSingle{{Type: pb.ConfChangeAddNode, NodeID: 1}}, genCC(num, id, typ)...)
  170. return reflect.ValueOf(ccs)
  171. }