Merge pull request #15045 from serathius/linearizability-model
tests: Refactor etcd modeldependabot/go_modules/go.uber.org/atomic-1.10.0
commit
a4c1b3a9e2
|
@ -47,25 +47,21 @@ type EtcdResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type EtcdState struct {
|
type EtcdState struct {
|
||||||
Key string
|
|
||||||
PossibleValues []ValueRevision
|
|
||||||
}
|
|
||||||
|
|
||||||
type ValueRevision struct {
|
|
||||||
Value string
|
|
||||||
Revision int64
|
Revision int64
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
var etcdModel = porcupine.Model{
|
var etcdModel = porcupine.Model{
|
||||||
Init: func() interface{} { return "{}" },
|
Init: func() interface{} { return "[]" },
|
||||||
Step: func(st interface{}, in interface{}, out interface{}) (bool, interface{}) {
|
Step: func(st interface{}, in interface{}, out interface{}) (bool, interface{}) {
|
||||||
var state EtcdState
|
var states []EtcdState
|
||||||
err := json.Unmarshal([]byte(st.(string)), &state)
|
err := json.Unmarshal([]byte(st.(string)), &states)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
ok, state := step(state, in.(EtcdRequest), out.(EtcdResponse))
|
ok, states := step(states, in.(EtcdRequest), out.(EtcdResponse))
|
||||||
data, err := json.Marshal(state)
|
data, err := json.Marshal(states)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -105,92 +101,93 @@ var etcdModel = porcupine.Model{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func step(state EtcdState, request EtcdRequest, response EtcdResponse) (bool, EtcdState) {
|
func step(states []EtcdState, request EtcdRequest, response EtcdResponse) (bool, []EtcdState) {
|
||||||
if len(state.PossibleValues) == 0 {
|
if len(states) == 0 {
|
||||||
state.Key = request.Key
|
return true, initStates(request, response)
|
||||||
if ok, v := initValueRevision(request, response); ok {
|
|
||||||
state.PossibleValues = append(state.PossibleValues, v)
|
|
||||||
}
|
|
||||||
return true, state
|
|
||||||
}
|
|
||||||
if state.Key != request.Key {
|
|
||||||
panic("multiple keys not supported")
|
|
||||||
}
|
}
|
||||||
if response.Err != nil {
|
if response.Err != nil {
|
||||||
for _, v := range state.PossibleValues {
|
// Add addition states for failed request in case of failed request was persisted.
|
||||||
newV, _ := stepValue(v, request)
|
states = append(states, applyRequest(states, request)...)
|
||||||
state.PossibleValues = append(state.PossibleValues, newV)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
var i = 0
|
// Remove states that didn't lead to response we got.
|
||||||
for _, v := range state.PossibleValues {
|
states = filterStateMatchesResponse(states, request, response)
|
||||||
newV, expectedResponse := stepValue(v, request)
|
|
||||||
if expectedResponse == response {
|
|
||||||
state.PossibleValues[i] = newV
|
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
}
|
return len(states) > 0, states
|
||||||
state.PossibleValues = state.PossibleValues[:i]
|
|
||||||
}
|
|
||||||
return len(state.PossibleValues) > 0, state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initValueRevision(request EtcdRequest, response EtcdResponse) (ok bool, v ValueRevision) {
|
func applyRequest(states []EtcdState, request EtcdRequest) []EtcdState {
|
||||||
|
newStates := make([]EtcdState, 0, len(states))
|
||||||
|
for _, s := range states {
|
||||||
|
newState, _ := stepState(s, request)
|
||||||
|
newStates = append(newStates, newState)
|
||||||
|
}
|
||||||
|
return newStates
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterStateMatchesResponse(states []EtcdState, request EtcdRequest, response EtcdResponse) []EtcdState {
|
||||||
|
newStates := make([]EtcdState, 0, len(states))
|
||||||
|
for _, s := range states {
|
||||||
|
newState, expectResponse := stepState(s, request)
|
||||||
|
if expectResponse == response {
|
||||||
|
newStates = append(newStates, newState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newStates
|
||||||
|
}
|
||||||
|
|
||||||
|
func initStates(request EtcdRequest, response EtcdResponse) []EtcdState {
|
||||||
if response.Err != nil {
|
if response.Err != nil {
|
||||||
return false, ValueRevision{}
|
return []EtcdState{}
|
||||||
|
}
|
||||||
|
state := EtcdState{
|
||||||
|
Key: request.Key,
|
||||||
|
Revision: response.Revision,
|
||||||
}
|
}
|
||||||
switch request.Op {
|
switch request.Op {
|
||||||
case Get:
|
case Get:
|
||||||
return true, ValueRevision{
|
if response.GetData != "" {
|
||||||
Value: response.GetData,
|
state.Value = response.GetData
|
||||||
Revision: response.Revision,
|
|
||||||
}
|
}
|
||||||
case Put:
|
case Put:
|
||||||
return true, ValueRevision{
|
state.Value = request.PutData
|
||||||
Value: request.PutData,
|
|
||||||
Revision: response.Revision,
|
|
||||||
}
|
|
||||||
case Delete:
|
case Delete:
|
||||||
return true, ValueRevision{
|
|
||||||
Value: "",
|
|
||||||
Revision: response.Revision,
|
|
||||||
}
|
|
||||||
case Txn:
|
case Txn:
|
||||||
if response.TxnSucceeded {
|
if response.TxnSucceeded {
|
||||||
return true, ValueRevision{
|
state.Value = request.TxnNewData
|
||||||
Value: request.TxnNewData,
|
|
||||||
Revision: response.Revision,
|
|
||||||
}
|
}
|
||||||
}
|
return []EtcdState{}
|
||||||
return false, ValueRevision{}
|
|
||||||
default:
|
default:
|
||||||
panic("Unknown operation")
|
panic("Unknown operation")
|
||||||
}
|
}
|
||||||
|
return []EtcdState{state}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stepValue(v ValueRevision, request EtcdRequest) (ValueRevision, EtcdResponse) {
|
func stepState(s EtcdState, request EtcdRequest) (EtcdState, EtcdResponse) {
|
||||||
|
if s.Key != request.Key {
|
||||||
|
panic("multiple keys not supported")
|
||||||
|
}
|
||||||
resp := EtcdResponse{}
|
resp := EtcdResponse{}
|
||||||
switch request.Op {
|
switch request.Op {
|
||||||
case Get:
|
case Get:
|
||||||
resp.GetData = v.Value
|
resp.GetData = s.Value
|
||||||
case Put:
|
case Put:
|
||||||
v.Value = request.PutData
|
s.Value = request.PutData
|
||||||
v.Revision += 1
|
s.Revision += 1
|
||||||
case Delete:
|
case Delete:
|
||||||
if v.Value != "" {
|
if s.Value != "" {
|
||||||
v.Value = ""
|
s.Value = ""
|
||||||
v.Revision += 1
|
s.Revision += 1
|
||||||
resp.Deleted = 1
|
resp.Deleted = 1
|
||||||
}
|
}
|
||||||
case Txn:
|
case Txn:
|
||||||
if v.Value == request.TxnExpectData {
|
if s.Value == request.TxnExpectData {
|
||||||
v.Value = request.TxnNewData
|
s.Value = request.TxnNewData
|
||||||
v.Revision += 1
|
s.Revision += 1
|
||||||
resp.TxnSucceeded = true
|
resp.TxnSucceeded = true
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("unsupported operation")
|
panic("unsupported operation")
|
||||||
}
|
}
|
||||||
resp.Revision = v.Revision
|
resp.Revision = s.Revision
|
||||||
return v, resp
|
return s, resp
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue