diff --git a/tests/robustness/model/deterministic.go b/tests/robustness/model/deterministic.go index e01293853..12c40ff57 100644 --- a/tests/robustness/model/deterministic.go +++ b/tests/robustness/model/deterministic.go @@ -70,12 +70,8 @@ func (s etcdState) Step(request EtcdRequest, response EtcdResponse) (bool, etcdS // initState tries to create etcd state based on the first request. func initState(request EtcdRequest, response EtcdResponse) etcdState { - state := etcdState{ - Revision: response.Revision, - KeyValues: map[string]ValueRevision{}, - KeyLeases: map[string]int64{}, - Leases: map[int64]EtcdLease{}, - } + state := emptyState() + state.Revision = response.Revision switch request.Type { case Txn: if response.Txn.Failure { @@ -118,6 +114,15 @@ func initState(request EtcdRequest, response EtcdResponse) etcdState { return state } +func emptyState() etcdState { + return etcdState{ + Revision: 1, + KeyValues: map[string]ValueRevision{}, + KeyLeases: map[string]int64{}, + Leases: map[int64]EtcdLease{}, + } +} + // step handles a successful request, returning updated state and response it would generate. func (s etcdState) step(request EtcdRequest) (etcdState, EtcdResponse) { newKVs := map[string]ValueRevision{} diff --git a/tests/robustness/model/non_deterministic.go b/tests/robustness/model/non_deterministic.go index 318b47390..163ea8db9 100644 --- a/tests/robustness/model/non_deterministic.go +++ b/tests/robustness/model/non_deterministic.go @@ -71,11 +71,10 @@ type EtcdNonDeterministicResponse struct { func (states nonDeterministicState) Step(request EtcdRequest, response EtcdNonDeterministicResponse) (bool, nonDeterministicState) { if len(states) == 0 { - // states were not initialized - if response.Err != nil || response.ResultUnknown || response.Revision == 0 { - return true, nil + if response.Err == nil && !response.ResultUnknown { + return true, nonDeterministicState{initState(request, response.EtcdResponse)} } - return true, initNonDeterministicState(request, response) + states = nonDeterministicState{emptyState()} } var newStates nonDeterministicState if response.Err != nil { @@ -86,10 +85,6 @@ func (states nonDeterministicState) Step(request EtcdRequest, response EtcdNonDe return len(newStates) > 0, newStates } -func initNonDeterministicState(request EtcdRequest, response EtcdNonDeterministicResponse) nonDeterministicState { - return nonDeterministicState{initState(request, response.EtcdResponse)} -} - // stepFailedRequest duplicates number of states by considering request persisted and lost. func (states nonDeterministicState) stepFailedRequest(request EtcdRequest) nonDeterministicState { newStates := make(nonDeterministicState, 0, len(states)*2) diff --git a/tests/robustness/model/non_deterministic_test.go b/tests/robustness/model/non_deterministic_test.go index 154e6e20c..857cba5e0 100644 --- a/tests/robustness/model/non_deterministic_test.go +++ b/tests/robustness/model/non_deterministic_test.go @@ -21,6 +21,8 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" + + "go.etcd.io/etcd/api/v3/mvccpb" ) func TestModelNonDeterministic(t *testing.T) { @@ -30,6 +32,22 @@ func TestModelNonDeterministic(t *testing.T) { } nonDeterministicTestScenarios = append(nonDeterministicTestScenarios, []nonDeterministicModelTest{ + { + name: "First Put request fails, but is persisted", + operations: []nonDeterministicOperation{ + {req: putRequest("key1", "1"), resp: failedResponse(errors.New("failed"))}, + {req: putRequest("key2", "2"), resp: putResponse(3)}, + {req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3}}, 2, 3)}, + }, + }, + { + name: "First Put request fails, and is lost", + operations: []nonDeterministicOperation{ + {req: putRequest("key1", "1"), resp: failedResponse(errors.New("failed"))}, + {req: putRequest("key2", "2"), resp: putResponse(2)}, + {req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 1, 2)}, + }, + }, { name: "Put can fail and be lost before get", operations: []nonDeterministicOperation{