fix lint errors
This commit is contained in:
parent
8b92e9e143
commit
85e39191b8
56
machine.go
56
machine.go
@ -3,65 +3,65 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type State string
|
||||
|
||||
type Stater interface {
|
||||
AddStates(name ...State)
|
||||
GetState(name string) State
|
||||
AddTransition(trigger string, source State, dest State)
|
||||
AddSubscription(transition string, subscription Subscriber) error
|
||||
AddModel(m Modeler)
|
||||
Trigger(transition string) error
|
||||
CurrentState() State
|
||||
AddStates(name ...State)
|
||||
GetState(name string) State
|
||||
AddTransition(trigger string, source State, dest State)
|
||||
AddSubscription(transition string, subscription Subscriber) error
|
||||
AddModel(m Modeler)
|
||||
Trigger(transition string) error
|
||||
CurrentState() State
|
||||
}
|
||||
|
||||
type Definition struct {
|
||||
states []State
|
||||
triggers map[string]Transitioner
|
||||
model Modeler
|
||||
states []State
|
||||
triggers map[string]Transitioner
|
||||
model Modeler
|
||||
}
|
||||
|
||||
func New(initial State) Stater {
|
||||
return &Definition{ model: NewModel(initial), triggers: make(map[string]Transitioner) }
|
||||
return &Definition{model: NewModel(initial), triggers: make(map[string]Transitioner)}
|
||||
}
|
||||
|
||||
func (d *Definition) AddStates(name ...State) {
|
||||
d.states = append(d.states, name...)
|
||||
d.states = append(d.states, name...)
|
||||
}
|
||||
|
||||
func (d *Definition) AddTransition(trigger string, source State, dest State) {
|
||||
d.triggers[trigger] = NewTransition(trigger, source, dest)
|
||||
d.triggers[trigger] = NewTransition(trigger, source, dest)
|
||||
}
|
||||
|
||||
func (d *Definition) GetState(name string) State {
|
||||
var r State
|
||||
for _,s := range(d.states) {
|
||||
if string(s) == name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return r
|
||||
var r State
|
||||
for _, s := range d.states {
|
||||
if string(s) == name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (d *Definition) AddModel(m Modeler) {
|
||||
d.model = m
|
||||
d.model = m
|
||||
}
|
||||
|
||||
func (d *Definition) Trigger(transition string) error {
|
||||
return d.triggers[transition].Run(d.model)
|
||||
return d.triggers[transition].Run(d.model)
|
||||
}
|
||||
|
||||
func (d *Definition) CurrentState() State {
|
||||
return d.model.InspectState()
|
||||
return d.model.InspectState()
|
||||
}
|
||||
|
||||
func (d *Definition) AddSubscription(transition string, subscription Subscriber) error {
|
||||
if t,ok := d.triggers[transition]; ok {
|
||||
t.Subscribe(subscription)
|
||||
if t, ok := d.triggers[transition]; ok {
|
||||
t.Subscribe(subscription)
|
||||
return nil
|
||||
}
|
||||
return errors.New("Transition does not exist")
|
||||
}
|
||||
return errors.New("Transition does not exist")
|
||||
}
|
||||
|
@ -2,68 +2,68 @@
|
||||
|
||||
package machine
|
||||
|
||||
import(
|
||||
"log"
|
||||
"testing"
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func setupStater(initial State) Stater {
|
||||
s := New(initial)
|
||||
if s == nil {
|
||||
log.Fatal("Failed creating new Stater")
|
||||
}
|
||||
return s
|
||||
s := New(initial)
|
||||
if s == nil {
|
||||
log.Fatal("Failed creating new Stater")
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func TestMachineStater(t *testing.T) {
|
||||
s := setupStater("disconnected")
|
||||
s := setupStater("disconnected")
|
||||
|
||||
s.AddStates("disconnected", "start_connection", "connected")
|
||||
r := s.GetState("connected")
|
||||
if r != "connected" {
|
||||
t.Errorf("Failed checking state: %s", r)
|
||||
}
|
||||
s.AddStates("disconnected", "start_connection", "connected")
|
||||
r := s.GetState("connected")
|
||||
if r != "connected" {
|
||||
t.Errorf("Failed checking state: %s", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMachineCurrentState(t *testing.T) {
|
||||
s := setupStater("disconnected")
|
||||
s := setupStater("disconnected")
|
||||
|
||||
s.AddStates("disconnected", "start_connection", "connected")
|
||||
r := s.CurrentState()
|
||||
if r != "disconnected" {
|
||||
t.Errorf("Failed checking state: %s", r)
|
||||
}
|
||||
s.AddStates("disconnected", "start_connection", "connected")
|
||||
r := s.CurrentState()
|
||||
if r != "disconnected" {
|
||||
t.Errorf("Failed checking state: %s", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMachineAddTransition(t *testing.T) {
|
||||
s := setupStater("disconnected")
|
||||
s.AddStates("disconnected", "start_connection", "connected")
|
||||
s.AddTransition("connect", "disconnected", "start_connection")
|
||||
s.AddModel(setupModel("disconnected"))
|
||||
// worker gets a trigger message
|
||||
assert.Nil(t, s.Trigger("connect"))
|
||||
s := setupStater("disconnected")
|
||||
s.AddStates("disconnected", "start_connection", "connected")
|
||||
s.AddTransition("connect", "disconnected", "start_connection")
|
||||
s.AddModel(setupModel("disconnected"))
|
||||
// worker gets a trigger message
|
||||
assert.Nil(t, s.Trigger("connect"))
|
||||
|
||||
// machine generates transition event mesages
|
||||
if s.CurrentState() != "start_connection" {
|
||||
t.Errorf("State transition failed for: connect - %s", s.CurrentState())
|
||||
}
|
||||
// machine generates transition event mesages
|
||||
if s.CurrentState() != "start_connection" {
|
||||
t.Errorf("State transition failed for: connect - %s", s.CurrentState())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMachineAddSubscription(t *testing.T) {
|
||||
x := setupSubscriber()
|
||||
s := setupStater("disconnected")
|
||||
s.AddStates("disconnected", "start_connection", "connected")
|
||||
s.AddTransition("connect", "disconnected", "start_connection")
|
||||
assert.Nil(t, s.AddSubscription("connect", x))
|
||||
s.AddModel(setupModel("disconnected"))
|
||||
s.Trigger("connect")
|
||||
exitMessage := <- *x.(*EventChannel)
|
||||
enterMessage := <- *x.(*EventChannel)
|
||||
if exitMessage.on == EXITSTATEEVENT && exitMessage.source == "disconnected" {
|
||||
if enterMessage.on == ENTERSTATEEVENT && enterMessage.dest == "start_connection" {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Errorf("Unexpected event message in state transition notification: exit: %s, enter: %s", exitMessage, enterMessage)
|
||||
x := setupSubscriber()
|
||||
s := setupStater("disconnected")
|
||||
s.AddStates("disconnected", "start_connection", "connected")
|
||||
s.AddTransition("connect", "disconnected", "start_connection")
|
||||
assert.Nil(t, s.AddSubscription("connect", x))
|
||||
s.AddModel(setupModel("disconnected"))
|
||||
s.Trigger("connect")
|
||||
exitMessage := <-*x.(*EventChannel)
|
||||
enterMessage := <-*x.(*EventChannel)
|
||||
if exitMessage.on == EXITSTATEEVENT && exitMessage.source == "disconnected" {
|
||||
if enterMessage.on == ENTERSTATEEVENT && enterMessage.dest == "start_connection" {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Errorf("Unexpected event message in state transition notification: exit: %s, enter: %s", exitMessage, enterMessage)
|
||||
}
|
||||
|
16
message.go
16
message.go
@ -3,28 +3,28 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
ENTERSTATEEVENT = iota
|
||||
EXITSTATEEVENT
|
||||
ENTERSTATEEVENT = iota
|
||||
EXITSTATEEVENT
|
||||
)
|
||||
|
||||
type Eventtype int
|
||||
|
||||
type EventMessage struct {
|
||||
on Eventtype
|
||||
source State
|
||||
dest State
|
||||
on Eventtype
|
||||
source State
|
||||
dest State
|
||||
}
|
||||
|
||||
type EventChannel chan EventMessage
|
||||
|
||||
func (e *EventChannel) Notify(m *EventMessage) {
|
||||
*e <- *m
|
||||
*e <- *m
|
||||
}
|
||||
|
||||
func (m EventMessage) String() string {
|
||||
return fmt.Sprintf("{on: %d, source: %s, dest: %s}", m.on, m.source, m.dest)
|
||||
return fmt.Sprintf("{on: %d, source: %s, dest: %s}", m.on, m.source, m.dest)
|
||||
}
|
||||
|
20
model.go
20
model.go
@ -3,29 +3,29 @@
|
||||
package machine
|
||||
|
||||
type Modeler interface {
|
||||
Trigger(transition Transitioner) error
|
||||
ChangeState(target State) State
|
||||
InspectState() State
|
||||
Trigger(transition Transitioner) error
|
||||
ChangeState(target State) State
|
||||
InspectState() State
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
state State
|
||||
state State
|
||||
}
|
||||
|
||||
func NewModel(initial State) Modeler {
|
||||
return &Model{ state: initial }
|
||||
return &Model{state: initial}
|
||||
}
|
||||
|
||||
func (m *Model) Trigger(transition Transitioner) error {
|
||||
return transition.Run(m)
|
||||
return transition.Run(m)
|
||||
}
|
||||
|
||||
func (m *Model) ChangeState(target State) State {
|
||||
oldState := m.state
|
||||
m.state = target
|
||||
return oldState
|
||||
oldState := m.state
|
||||
m.state = target
|
||||
return oldState
|
||||
}
|
||||
|
||||
func (m *Model) InspectState() State {
|
||||
return m.state
|
||||
return m.state
|
||||
}
|
||||
|
@ -3,27 +3,27 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func setupModel(initial State) Modeler {
|
||||
return NewModel(initial)
|
||||
return NewModel(initial)
|
||||
}
|
||||
|
||||
func TestMachineModel(t *testing.T) {
|
||||
m := setupModel("")
|
||||
if m == nil {
|
||||
t.Errorf("Failed creating new model")
|
||||
}
|
||||
m := setupModel("")
|
||||
if m == nil {
|
||||
t.Errorf("Failed creating new model")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMachineModelChangeState(t *testing.T) {
|
||||
m := setupModel("")
|
||||
oldState := m.ChangeState("connected")
|
||||
if oldState != "" {
|
||||
t.Errorf("Failed changing state in model")
|
||||
}
|
||||
if m.InspectState() != "connected" {
|
||||
t.Errorf("Failed changing state in model")
|
||||
}
|
||||
m := setupModel("")
|
||||
oldState := m.ChangeState("connected")
|
||||
if oldState != "" {
|
||||
t.Errorf("Failed changing state in model")
|
||||
}
|
||||
if m.InspectState() != "connected" {
|
||||
t.Errorf("Failed changing state in model")
|
||||
}
|
||||
}
|
||||
|
5
state.go
5
state.go
@ -3,10 +3,9 @@
|
||||
package machine
|
||||
|
||||
type mState struct {
|
||||
name string
|
||||
name string
|
||||
}
|
||||
|
||||
func NewState(name string) *mState {
|
||||
return &mState{ name: name }
|
||||
return &mState{name: name}
|
||||
}
|
||||
|
||||
|
@ -3,12 +3,12 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMachineState(t *testing.T) {
|
||||
s := NewState("connected")
|
||||
if s == nil {
|
||||
t.Errorf("Failed creating new state")
|
||||
}
|
||||
s := NewState("connected")
|
||||
if s == nil {
|
||||
t.Errorf("Failed creating new state")
|
||||
}
|
||||
}
|
||||
|
@ -3,5 +3,5 @@
|
||||
package machine
|
||||
|
||||
type Subscriber interface {
|
||||
Notify(m *EventMessage)
|
||||
Notify(m *EventMessage)
|
||||
}
|
||||
|
@ -3,45 +3,45 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
_ "errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Transitioner interface {
|
||||
Run(m Modeler) error
|
||||
Subscribe(s Subscriber)
|
||||
Run(m Modeler) error
|
||||
Subscribe(s Subscriber)
|
||||
}
|
||||
|
||||
type Transition struct {
|
||||
trigger string
|
||||
source State
|
||||
dest State
|
||||
subscriptions []Subscriber
|
||||
trigger string
|
||||
source State
|
||||
dest State
|
||||
subscriptions []Subscriber
|
||||
}
|
||||
|
||||
func NewTransition(trigger string, source State, dest State) Transitioner {
|
||||
return &Transition{ trigger: trigger, source: source, dest: dest }
|
||||
return &Transition{trigger: trigger, source: source, dest: dest}
|
||||
}
|
||||
|
||||
func (r *Transition) Run(m Modeler) error {
|
||||
currentState := m.InspectState()
|
||||
if currentState == r.source {
|
||||
res := m.ChangeState(r.dest)
|
||||
if res == currentState {
|
||||
r.Notify(EXITSTATEEVENT, currentState, r.dest)
|
||||
r.Notify(ENTERSTATEEVENT, currentState, r.dest)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New(fmt.Sprintf("Transition from %s to %s failed on model state %s", r.source, r.dest, currentState))
|
||||
currentState := m.InspectState()
|
||||
if currentState == r.source {
|
||||
res := m.ChangeState(r.dest)
|
||||
if res == currentState {
|
||||
r.Notify(EXITSTATEEVENT, currentState, r.dest)
|
||||
r.Notify(ENTERSTATEEVENT, currentState, r.dest)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("Transition from %s to %s failed on model state %s", r.source, r.dest, currentState)
|
||||
}
|
||||
|
||||
func (r *Transition) Subscribe(s Subscriber) {
|
||||
r.subscriptions = append(r.subscriptions, s)
|
||||
r.subscriptions = append(r.subscriptions, s)
|
||||
}
|
||||
|
||||
func (r *Transition) Notify(on Eventtype, source State, dest State) {
|
||||
for _,s := range r.subscriptions {
|
||||
s.Notify(&EventMessage{ on: on, source: source, dest: dest})
|
||||
}
|
||||
for _, s := range r.subscriptions {
|
||||
s.Notify(&EventMessage{on: on, source: source, dest: dest})
|
||||
}
|
||||
}
|
||||
|
@ -3,53 +3,53 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func setupTransition() Transitioner {
|
||||
t := NewTransition("open", "closed", "open")
|
||||
if t == nil {
|
||||
log.Fatal("Failed creating new transition")
|
||||
}
|
||||
return t
|
||||
t := NewTransition("open", "closed", "open")
|
||||
if t == nil {
|
||||
log.Fatal("Failed creating new transition")
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func setupSubscriber() Subscriber {
|
||||
c := make(EventChannel, 2)
|
||||
return &c
|
||||
c := make(EventChannel, 2)
|
||||
return &c
|
||||
}
|
||||
|
||||
func TestNewTransition(t *testing.T) {
|
||||
s := NewTransition("connect", "disconnected", "connected")
|
||||
if s == nil {
|
||||
t.Errorf("Failed creating new transition")
|
||||
}
|
||||
s := NewTransition("connect", "disconnected", "connected")
|
||||
if s == nil {
|
||||
t.Errorf("Failed creating new transition")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransitionExecution(t *testing.T) {
|
||||
s := setupTransition()
|
||||
m := setupModel("closed")
|
||||
assert.Nil(t, s.Run(m))
|
||||
state := m.InspectState()
|
||||
if state != "open" {
|
||||
t.Errorf("Failed to transition state: %s", state)
|
||||
}
|
||||
s := setupTransition()
|
||||
m := setupModel("closed")
|
||||
assert.Nil(t, s.Run(m))
|
||||
state := m.InspectState()
|
||||
if state != "open" {
|
||||
t.Errorf("Failed to transition state: %s", state)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransitionSubscribe(t *testing.T) {
|
||||
c := setupSubscriber()
|
||||
s := setupTransition()
|
||||
s.Subscribe(c)
|
||||
m := setupModel("closed")
|
||||
assert.Nil(t, s.Run(m))
|
||||
exitEvent := <- *c.(*EventChannel)
|
||||
enterEvent := <- *c.(*EventChannel)
|
||||
if exitEvent.on != EXITSTATEEVENT {
|
||||
t.Errorf("Invalid exit event")
|
||||
}
|
||||
if enterEvent.on != ENTERSTATEEVENT {
|
||||
t.Errorf("Invalid enter event")
|
||||
}
|
||||
c := setupSubscriber()
|
||||
s := setupTransition()
|
||||
s.Subscribe(c)
|
||||
m := setupModel("closed")
|
||||
assert.Nil(t, s.Run(m))
|
||||
exitEvent := <-*c.(*EventChannel)
|
||||
enterEvent := <-*c.(*EventChannel)
|
||||
if exitEvent.on != EXITSTATEEVENT {
|
||||
t.Errorf("Invalid exit event")
|
||||
}
|
||||
if enterEvent.on != ENTERSTATEEVENT {
|
||||
t.Errorf("Invalid enter event")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user