Skip to content

Commit 53cdd0f

Browse files
committed
[test] Extend tests for stage transitions
1 parent e030b68 commit 53cdd0f

File tree

5 files changed

+264
-77
lines changed

5 files changed

+264
-77
lines changed

internal/app/controller/controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ func (r *GameController) Run() (err error) {
6262
}
6363

6464
func (r *GameController) OnNewEvent(event Event) {
65-
err := r.Engine.Process(event)
65+
cmd, err := r.Engine.Process(event)
6666
if err != nil {
6767
log.Println("Could not process event:", event, err)
6868
} else {
69-
r.publish(event.Command)
69+
r.publish(cmd)
7070
}
7171
}
7272

internal/app/controller/engine.go

Lines changed: 69 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ func (e *Engine) UndoLastAction() {
5252
}
5353
}
5454

55-
func (e *Engine) Process(event Event) error {
56-
err := e.processEvent(event)
55+
func (e *Engine) Process(event Event) (*EventCommand, error) {
56+
cmd, err := e.processEvent(event)
5757
if err == nil {
5858
e.StateHistory = append(e.StateHistory, *e.State)
5959
}
60-
return err
60+
return cmd, err
6161
}
6262

6363
func (e *Engine) loadStages() {
@@ -91,7 +91,7 @@ func (e *Engine) updateTimes(delta time.Duration) {
9191
}
9292
}
9393

94-
func (e *Engine) processEvent(event Event) error {
94+
func (e *Engine) processEvent(event Event) (*EventCommand, error) {
9595
if event.Command != nil {
9696
return e.processCommand(event.Command)
9797
} else if event.Modify != nil {
@@ -103,62 +103,65 @@ func (e *Engine) processEvent(event Event) error {
103103
} else if event.Trigger != nil {
104104
return e.processTrigger(event.Trigger)
105105
}
106-
return errors.New("unknown event")
106+
return nil, errors.New("unknown event")
107107
}
108108

109-
func (e *Engine) processCommand(c *EventCommand) error {
109+
func (e *Engine) processCommand(c *EventCommand) (*EventCommand, error) {
110110
switch c.Type {
111111
case CommandHalt:
112112
e.State.GameState = GameStateHalted
113113
e.State.GameStateFor = nil
114114
case CommandStop:
115115
e.State.GameState = GameStateStopped
116116
e.State.GameStateFor = nil
117-
case CommandForceStart, CommandNormalStart, CommandDirect, CommandIndirect:
117+
case CommandNormalStart:
118+
e.State.GameState = GameStateRunning
119+
e.State.GameStateFor = nil
120+
e.updatePreStages()
121+
case CommandForceStart, CommandDirect, CommandIndirect:
118122
e.State.GameState = GameStateRunning
119123
e.State.GameStateFor = nil
120124
case CommandKickoff:
121125
if c.ForTeam == nil {
122-
return errors.New("Team required for kickoff")
126+
return nil, errors.New("Team required for kickoff")
123127
}
124128
e.State.GameState = GameStatePreKickoff
125129
e.State.GameStateFor = c.ForTeam
126130
case CommandPenalty:
127131
if c.ForTeam == nil {
128-
return errors.New("Team required for penalty")
132+
return nil, errors.New("Team required for penalty")
129133
}
130134
e.State.GameState = GameStatePrePenalty
131135
e.State.GameStateFor = c.ForTeam
132136
case CommandBallPlacement:
133137
if c.ForTeam == nil {
134-
return errors.New("Team required for ball placement")
138+
return nil, errors.New("Team required for ball placement")
135139
}
136140
e.State.GameState = GameStateBallPlacement
137141
e.State.GameStateFor = c.ForTeam
138142
case CommandGoal:
139143
if c.ForTeam == nil {
140-
return errors.New("Team required for goal")
144+
return nil, errors.New("Team required for goal")
141145
}
142146
e.State.TeamState[*c.ForTeam].Goals++
143147
case CommandTimeout:
144148
if c.ForTeam == nil {
145-
return errors.New("Team required for timeout")
149+
return nil, errors.New("Team required for timeout")
146150
}
147151
e.State.TeamState[*c.ForTeam].TimeoutsLeft--
148152
e.State.GameState = GameStateTimeout
149153
e.State.GameStateFor = c.ForTeam
150154
default:
151-
return errors.Errorf("Unknown command: %v", c)
155+
return nil, errors.Errorf("Unknown command: %v", c)
152156
}
153157

154-
e.updatePreStages()
155158
log.Printf("Processed command %v", *c)
156-
return nil
159+
return c, nil
157160
}
158161

159-
func (e *Engine) processModify(m *EventModifyValue) error {
162+
func (e *Engine) processModify(m *EventModifyValue) (*EventCommand, error) {
160163
if m.ForTeam.Unknown() {
161-
return errors.Errorf("Unknown team: %v", m.ForTeam)
164+
return nil, errors.Errorf("Unknown team: %v", m.ForTeam)
162165
}
163166
teamState := e.State.TeamState[m.ForTeam]
164167
if m.Goals != nil {
@@ -179,7 +182,7 @@ func (e *Engine) processModify(m *EventModifyValue) error {
179182
} else if m.YellowCardTime != nil {
180183
cardId := m.YellowCardTime.CardID
181184
if cardId < 0 || cardId >= len(teamState.YellowCardTimes) {
182-
return errors.Errorf("Invalid card index: %v", cardId)
185+
return nil, errors.Errorf("Invalid card index: %v", cardId)
183186
}
184187
if duration, err := strToDuration(m.YellowCardTime.Duration); err == nil {
185188
teamState.YellowCardTimes[cardId] = duration
@@ -188,78 +191,90 @@ func (e *Engine) processModify(m *EventModifyValue) error {
188191
if duration, err := strToDuration(*m.TimeoutTimeLeft); err == nil {
189192
teamState.TimeoutTimeLeft = duration
190193
} else {
191-
return err
194+
return nil, err
192195
}
193196
} else {
194-
return errors.Errorf("Unknown modify: %v", m)
197+
return nil, errors.Errorf("Unknown modify: %v", m)
195198
}
196199
log.Printf("Processed modification %v", m)
197-
return nil
200+
return nil, nil
198201
}
199202

200-
func (e *Engine) processStage(s *EventStage) error {
203+
func (e *Engine) processStage(s *EventStage) (*EventCommand, error) {
201204
if e.State.GameState != GameStateHalted && e.State.GameState != GameStateStopped {
202-
return errors.New("The game state must be halted or stopped to change the stage")
205+
return nil, errors.New("The game state must be halted or stopped to change the stage")
203206
}
204207

205-
index, err := e.State.Stage.index()
206-
if err != nil {
207-
return err
208-
}
208+
var cmd *EventCommand
209209
if s.StageOperation == StageNext {
210-
nextIndex := index + 1
211-
if nextIndex >= len(Stages) {
212-
return errors.New("No next stage")
213-
}
214-
e.updateStage(Stages[nextIndex])
210+
cmd = e.updateStage(e.State.Stage.Next())
215211
} else if s.StageOperation == StagePrevious {
216-
nextIndex := index - 1
217-
if nextIndex < 0 {
218-
return errors.New("No previous stage")
219-
}
220-
e.updateStage(Stages[nextIndex])
212+
cmd = e.updateStage(e.State.Stage.Previous())
221213
} else {
222-
return errors.Errorf("Unknown stage operation: %v", s.StageOperation)
214+
return nil, errors.Errorf("Unknown stage operation: %v", s.StageOperation)
223215
}
224216

225217
log.Printf("Processed stage %v", s.StageOperation)
226218

227-
return nil
219+
return cmd, nil
228220
}
229221

230-
func (e *Engine) updateStage(stage Stage) {
231-
e.State.Stage = stage
222+
func (e *Engine) updateStage(stage Stage) (cmd *EventCommand) {
232223

233-
e.State.StageTimeLeft = e.StageTimes[e.State.Stage]
224+
e.State.StageTimeLeft = e.StageTimes[stage]
234225
e.State.StageTimeElapsed = 0
235226

236-
if e.State.Stage == StageFirstHalf {
227+
if !e.State.Stage.IsPreStage() {
228+
e.State.GameState = GameStateHalted
229+
e.State.GameStateFor = nil
230+
cmd = &EventCommand{nil, CommandHalt}
231+
}
232+
233+
if stage == StageFirstHalf {
237234
e.MatchTimeStart = time.Now()
238235
}
239-
if e.State.Stage == StageOvertimeFirstHalfPre {
236+
237+
if stage == StageOvertimeFirstHalfPre {
240238
e.State.TeamState[TeamYellow].TimeoutsLeft = e.config.Overtime.Timeouts
241239
e.State.TeamState[TeamYellow].TimeoutTimeLeft = e.config.Overtime.TimeoutDuration
242240
e.State.TeamState[TeamBlue].TimeoutsLeft = e.config.Overtime.Timeouts
243241
e.State.TeamState[TeamBlue].TimeoutTimeLeft = e.config.Overtime.TimeoutDuration
244242
}
243+
244+
e.State.Stage = stage
245+
return
246+
}
247+
248+
func (e *Engine) updatePreStages() {
249+
250+
switch e.State.Stage {
251+
case StagePreGame:
252+
e.updateStage(StageFirstHalf)
253+
case StageSecondHalfPre:
254+
e.updateStage(StageSecondHalf)
255+
case StageOvertimeFirstHalfPre:
256+
e.updateStage(StageOvertimeFirstHalf)
257+
case StageOvertimeSecondHalfPre:
258+
e.updateStage(StageOvertimeSecondHalf)
259+
}
245260
}
246261

247-
func (e *Engine) processCard(card *EventCard) error {
262+
func (e *Engine) processCard(card *EventCard) (*EventCommand, error) {
248263
if card.ForTeam != TeamYellow && card.ForTeam != TeamBlue {
249-
return errors.Errorf("Unknown team: %v", card.ForTeam)
264+
return nil, errors.Errorf("Unknown team: %v", card.ForTeam)
250265
}
251266
if card.Type != CardTypeYellow && card.Type != CardTypeRed {
252-
return errors.Errorf("Unknown card type: %v", card.Type)
267+
return nil, errors.Errorf("Unknown card type: %v", card.Type)
253268
}
254269
teamState := e.State.TeamState[card.ForTeam]
255270
if card.Operation == CardOperationAdd {
256-
return addCard(card, teamState, e.config.YellowCardDuration)
271+
return nil, addCard(card, teamState, e.config.YellowCardDuration)
257272
} else if card.Operation == CardOperationRevoke {
258-
return revokeCard(card, teamState)
273+
return nil, revokeCard(card, teamState)
259274
} else if card.Operation == CardOperationModify {
260-
return modifyCard(card, teamState)
275+
return nil, modifyCard(card, teamState)
261276
}
262-
return errors.Errorf("Unknown operation: %v", card.Operation)
277+
return nil, errors.Errorf("Unknown operation: %v", card.Operation)
263278
}
264279

265280
func modifyCard(card *EventCard, teamState *TeamInfo) error {
@@ -286,7 +301,7 @@ func addCard(card *EventCard, teamState *TeamInfo, duration time.Duration) error
286301
return nil
287302
}
288303

289-
func (e *Engine) processTrigger(t *EventTrigger) error {
304+
func (e *Engine) processTrigger(t *EventTrigger) (*EventCommand, error) {
290305
if t.Type == TriggerResetMatch {
291306
e.ResetGame()
292307

@@ -297,10 +312,10 @@ func (e *Engine) processTrigger(t *EventTrigger) error {
297312
} else if t.Type == TriggerUndo {
298313
e.UndoLastAction()
299314
} else {
300-
return errors.Errorf("Unknown trigger: %v", t.Type)
315+
return nil, errors.Errorf("Unknown trigger: %v", t.Type)
301316
}
302317
log.Printf("Processed trigger %v", t.Type)
303-
return nil
318+
return nil, nil
304319
}
305320

306321
func revokeCard(card *EventCard, teamState *TeamInfo) error {
@@ -327,22 +342,6 @@ func revokeCard(card *EventCard, teamState *TeamInfo) error {
327342
return nil
328343
}
329344

330-
func (e *Engine) updatePreStages() {
331-
proceedPreStages := e.State.GameState != GameStateHalted
332-
if proceedPreStages {
333-
switch e.State.Stage {
334-
case StagePreGame:
335-
e.updateStage(StageFirstHalf)
336-
case StageSecondHalfPre:
337-
e.updateStage(StageSecondHalf)
338-
case StageOvertimeFirstHalfPre:
339-
e.updateStage(StageOvertimeFirstHalf)
340-
case StageOvertimeSecondHalfPre:
341-
e.updateStage(StageOvertimeSecondHalf)
342-
}
343-
}
344-
}
345-
346345
func strToDuration(s string) (duration time.Duration, err error) {
347346
duration = 0
348347
err = nil

internal/app/controller/state.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type Stage string
3737

3838
const (
3939
// StagePreGame before game has started
40-
StagePreGame Stage = "Pre-Game"
40+
StagePreGame Stage = "Pre-First Half"
4141
// StageFirstHalf in first half
4242
StageFirstHalf Stage = "First Half"
4343
// StageHalfTime in half time
@@ -93,6 +93,38 @@ func (s Stage) index() (int, error) {
9393
return 0, errors.Errorf("unknown stage: %v", s)
9494
}
9595

96+
func (s Stage) Next() Stage {
97+
index, err := s.index()
98+
if err != nil {
99+
return s
100+
}
101+
nextIndex := index + 1
102+
if nextIndex >= len(Stages) {
103+
return s
104+
}
105+
return Stages[nextIndex]
106+
}
107+
108+
func (s Stage) Previous() Stage {
109+
index, err := s.index()
110+
if err != nil {
111+
return s
112+
}
113+
nextIndex := index - 1
114+
if nextIndex < 0 {
115+
return s
116+
}
117+
return Stages[nextIndex]
118+
}
119+
120+
func (s Stage) IsPreStage() bool {
121+
switch s {
122+
case StagePreGame, StageSecondHalfPre, StageOvertimeFirstHalfPre, StageOvertimeSecondHalfPre:
123+
return true
124+
}
125+
return false
126+
}
127+
96128
// GameState of a game
97129
type GameState string
98130

internal/app/controller/testdata/transition_commands.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
[
22
{
33
"state": {
4-
"stage": "Pre-Game",
4+
"stage": "First Half",
55
"gameState": "Halted",
66
"gameStateForTeam": null,
77
"gameTimeElapsed": 0,
8-
"gameTimeLeft": 0,
8+
"gameTimeLeft": 300000000000,
99
"matchDuration": 0,
1010
"teamState": {
1111
"Blue": {

0 commit comments

Comments
 (0)