interaction_env_handler.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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 rafttest
  15. import (
  16. "fmt"
  17. "strconv"
  18. "testing"
  19. "github.com/cockroachdb/datadriven"
  20. )
  21. // Handle is the entrypoint for data-driven interaction testing. Commands and
  22. // parameters are parsed from the supplied TestData. Errors during data parsing
  23. // are reported via the supplied *testing.T; errors from the raft nodes and the
  24. // storage engine are reported to the output buffer.
  25. func (env *InteractionEnv) Handle(t *testing.T, d datadriven.TestData) string {
  26. env.Output.Reset()
  27. var err error
  28. switch d.Cmd {
  29. case "_breakpoint":
  30. // This is a helper case to attach a debugger to when a problem needs
  31. // to be investigated in a longer test file. In such a case, add the
  32. // following stanza immediately before the interesting behavior starts:
  33. //
  34. // _breakpoint:
  35. // ----
  36. // ok
  37. //
  38. // and set a breakpoint on the `case` above.
  39. case "add-nodes":
  40. // Example:
  41. //
  42. // add-nodes <number-of-nodes-to-add> voters=(1 2 3) learners=(4 5) index=2 content=foo
  43. err = env.handleAddNodes(t, d)
  44. case "campaign":
  45. // Example:
  46. //
  47. // campaign <id-of-candidate>
  48. err = env.handleCampaign(t, d)
  49. case "compact":
  50. // Example:
  51. //
  52. // compact <id> <new-first-index>
  53. err = env.handleCompact(t, d)
  54. case "deliver-msgs":
  55. // Deliver the messages for a given recipient.
  56. //
  57. // Example:
  58. //
  59. // deliver-msgs <idx>
  60. err = env.handleDeliverMsgs(t, d)
  61. case "process-ready":
  62. // Example:
  63. //
  64. // process-ready 3
  65. err = env.handleProcessReady(t, d)
  66. case "log-level":
  67. // Set the log level. NONE disables all output, including from the test
  68. // harness (except errors).
  69. //
  70. // Example:
  71. //
  72. // log-level WARN
  73. err = env.handleLogLevel(t, d)
  74. case "raft-log":
  75. // Print the Raft log.
  76. //
  77. // Example:
  78. //
  79. // raft-log 3
  80. err = env.handleRaftLog(t, d)
  81. case "stabilize":
  82. // Deliver messages to and run process-ready on the set of IDs until
  83. // no more work is to be done.
  84. //
  85. // Example:
  86. //
  87. // stabilize 1 4
  88. err = env.handleStabilize(t, d)
  89. case "status":
  90. // Print Raft status.
  91. //
  92. // Example:
  93. //
  94. // status 5
  95. err = env.handleStatus(t, d)
  96. case "tick-heartbeat":
  97. // Tick a heartbeat interval.
  98. //
  99. // Example:
  100. //
  101. // tick-heartbeat 3
  102. err = env.handleTickHeartbeat(t, d)
  103. case "propose":
  104. // Propose an entry.
  105. //
  106. // Example:
  107. //
  108. // propose 1 foo
  109. err = env.handlePropose(t, d)
  110. case "propose-conf-change":
  111. // Propose a configuration change.
  112. //
  113. // Example:
  114. //
  115. // propose-conf-change transition=explicit
  116. // v1 v3 l4 r5
  117. //
  118. // Example:
  119. //
  120. // propose-conf-change v1=true
  121. // v5
  122. err = env.handleProposeConfChange(t, d)
  123. default:
  124. err = fmt.Errorf("unknown command")
  125. }
  126. if err != nil {
  127. env.Output.WriteString(err.Error())
  128. }
  129. // NB: the highest log level suppresses all output, including that of the
  130. // handlers. This comes in useful during setup which can be chatty.
  131. // However, errors are always logged.
  132. if env.Output.Len() == 0 {
  133. return "ok"
  134. }
  135. if env.Output.Lvl == len(lvlNames)-1 {
  136. if err != nil {
  137. return err.Error()
  138. }
  139. return "ok (quiet)"
  140. }
  141. return env.Output.String()
  142. }
  143. func firstAsInt(t *testing.T, d datadriven.TestData) int {
  144. t.Helper()
  145. n, err := strconv.Atoi(d.CmdArgs[0].Key)
  146. if err != nil {
  147. t.Fatal(err)
  148. }
  149. return n
  150. }
  151. func firstAsNodeIdx(t *testing.T, d datadriven.TestData) int {
  152. t.Helper()
  153. n := firstAsInt(t, d)
  154. return n - 1
  155. }
  156. func ints(t *testing.T, d datadriven.TestData) []int {
  157. var ints []int
  158. for i := 0; i < len(d.CmdArgs); i++ {
  159. if len(d.CmdArgs[i].Vals) != 0 {
  160. continue
  161. }
  162. n, err := strconv.Atoi(d.CmdArgs[i].Key)
  163. if err != nil {
  164. t.Fatal(err)
  165. }
  166. ints = append(ints, n)
  167. }
  168. return ints
  169. }