123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- // Copyright 2019 The etcd Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package rafttest
- import (
- "fmt"
- "strconv"
- "testing"
- "github.com/cockroachdb/datadriven"
- )
- // Handle is the entrypoint for data-driven interaction testing. Commands and
- // parameters are parsed from the supplied TestData. Errors during data parsing
- // are reported via the supplied *testing.T; errors from the raft nodes and the
- // storage engine are reported to the output buffer.
- func (env *InteractionEnv) Handle(t *testing.T, d datadriven.TestData) string {
- env.Output.Reset()
- var err error
- switch d.Cmd {
- case "_breakpoint":
- // This is a helper case to attach a debugger to when a problem needs
- // to be investigated in a longer test file. In such a case, add the
- // following stanza immediately before the interesting behavior starts:
- //
- // _breakpoint:
- // ----
- // ok
- //
- // and set a breakpoint on the `case` above.
- case "add-nodes":
- // Example:
- //
- // add-nodes <number-of-nodes-to-add> voters=(1 2 3) learners=(4 5) index=2 content=foo
- err = env.handleAddNodes(t, d)
- case "campaign":
- // Example:
- //
- // campaign <id-of-candidate>
- err = env.handleCampaign(t, d)
- case "compact":
- // Example:
- //
- // compact <id> <new-first-index>
- err = env.handleCompact(t, d)
- case "deliver-msgs":
- // Deliver the messages for a given recipient.
- //
- // Example:
- //
- // deliver-msgs <idx>
- err = env.handleDeliverMsgs(t, d)
- case "process-ready":
- // Example:
- //
- // process-ready 3
- err = env.handleProcessReady(t, d)
- case "log-level":
- // Set the log level. NONE disables all output, including from the test
- // harness (except errors).
- //
- // Example:
- //
- // log-level WARN
- err = env.handleLogLevel(t, d)
- case "raft-log":
- // Print the Raft log.
- //
- // Example:
- //
- // raft-log 3
- err = env.handleRaftLog(t, d)
- case "stabilize":
- // Deliver messages to and run process-ready on the set of IDs until
- // no more work is to be done.
- //
- // Example:
- //
- // stabilize 1 4
- err = env.handleStabilize(t, d)
- case "status":
- // Print Raft status.
- //
- // Example:
- //
- // status 5
- err = env.handleStatus(t, d)
- case "tick-heartbeat":
- // Tick a heartbeat interval.
- //
- // Example:
- //
- // tick-heartbeat 3
- err = env.handleTickHeartbeat(t, d)
- case "propose":
- // Propose an entry.
- //
- // Example:
- //
- // propose 1 foo
- err = env.handlePropose(t, d)
- case "propose-conf-change":
- // Propose a configuration change.
- //
- // Example:
- //
- // propose-conf-change transition=explicit
- // v1 v3 l4 r5
- //
- // Example:
- //
- // propose-conf-change v1=true
- // v5
- err = env.handleProposeConfChange(t, d)
- default:
- err = fmt.Errorf("unknown command")
- }
- if err != nil {
- env.Output.WriteString(err.Error())
- }
- // NB: the highest log level suppresses all output, including that of the
- // handlers. This comes in useful during setup which can be chatty.
- // However, errors are always logged.
- if env.Output.Len() == 0 {
- return "ok"
- }
- if env.Output.Lvl == len(lvlNames)-1 {
- if err != nil {
- return err.Error()
- }
- return "ok (quiet)"
- }
- return env.Output.String()
- }
- func firstAsInt(t *testing.T, d datadriven.TestData) int {
- t.Helper()
- n, err := strconv.Atoi(d.CmdArgs[0].Key)
- if err != nil {
- t.Fatal(err)
- }
- return n
- }
- func firstAsNodeIdx(t *testing.T, d datadriven.TestData) int {
- t.Helper()
- n := firstAsInt(t, d)
- return n - 1
- }
- func ints(t *testing.T, d datadriven.TestData) []int {
- var ints []int
- for i := 0; i < len(d.CmdArgs); i++ {
- if len(d.CmdArgs[i].Vals) != 0 {
- continue
- }
- n, err := strconv.Atoi(d.CmdArgs[i].Key)
- if err != nil {
- t.Fatal(err)
- }
- ints = append(ints, n)
- }
- return ints
- }
|