Skip to content

Commit 34a8c8f

Browse files
committed
[test] Add more tests for ball placement procedure
1 parent e4d0932 commit 34a8c8f

8 files changed

+460
-4
lines changed

internal/app/config/division.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,16 @@ const (
66
DivA Division = "DivA"
77
DivB Division = "DivB"
88
)
9+
10+
// Division contain all known command enum constants
11+
var Divisions = []Division{DivA, DivB}
12+
13+
// Valid checks if the Division enum value is among the known values
14+
func (g Division) Valid() bool {
15+
for _, division := range Divisions {
16+
if division == g {
17+
return true
18+
}
19+
}
20+
return false
21+
}

internal/app/controller/engine_test.go

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package controller
33
import (
44
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/config"
55
"github.com/go-test/deep"
6+
"github.com/pkg/errors"
67
"gopkg.in/yaml.v2"
78
"io/ioutil"
89
"log"
@@ -69,7 +70,9 @@ func Test_engine(t *testing.T) {
6970
}
7071
for _, f := range files {
7172
if !f.IsDir() && strings.HasPrefix(f.Name(), "engine_test_") {
72-
processTransitionFile(t, f.Name())
73+
t.Run(f.Name(), func(t *testing.T) {
74+
processTransitionFile(t, f.Name())
75+
})
7376
}
7477
}
7578
}
@@ -98,13 +101,19 @@ func processTransitionFile(t *testing.T, fileName string) {
98101
}
99102

100103
// apply the initial state to the engine
104+
if err := stateTransitions.InitialState.valid(); err != nil {
105+
t.Fatal(err)
106+
}
101107
stateTransitions.InitialState.applyTo(e.State)
102108
// initialize the expected state with the current engine state
103109
expectedState := e.State.DeepCopy()
104110

105111
for i, s := range stateTransitions.Transitions {
106112

107113
if s.Event != nil {
114+
if err := s.Event.valid(); err != nil {
115+
t.Fatal(err)
116+
}
108117
if err := e.Process(*s.Event); err != nil {
109118
t.Fatalf("Could not process event '%v': %v", *s.Event, err)
110119
}
@@ -116,6 +125,9 @@ func processTransitionFile(t *testing.T, fileName string) {
116125
}
117126

118127
if s.ExpectedStateDiff != nil {
128+
if err := s.ExpectedStateDiff.valid(); err != nil {
129+
t.Fatal(err)
130+
}
119131
// apply the expected state diff to the expected state
120132
s.ExpectedStateDiff.applyTo(&expectedState)
121133
}
@@ -136,6 +148,71 @@ func initialTime() time.Time {
136148
return ts
137149
}
138150

151+
func (e *Event) valid() error {
152+
if e.Card != nil && !e.Card.ForTeam.Valid() {
153+
return errors.Errorf("Event.Card.ForTeam has an invalid value: %v", e.Card.ForTeam)
154+
}
155+
if e.Command != nil {
156+
if e.Command.ForTeam != nil && !e.Command.ForTeam.Valid() {
157+
return errors.Errorf("Event.Command.ForTeam has an invalid value: %v", e.Command.ForTeam)
158+
}
159+
if !e.Command.Type.Valid() {
160+
return errors.Errorf("Event.Command.Type has an invalid value: %v", e.Command.Type)
161+
}
162+
}
163+
if e.GameEvent != nil && !e.GameEvent.Type.Valid() {
164+
return errors.Errorf("Event.GameEvent.Type has an invalid value: %v", e.GameEvent.Type)
165+
}
166+
return nil
167+
}
168+
169+
func (t *TestState) valid() error {
170+
if t.Stage != nil && !t.Stage.Valid() {
171+
return errors.Errorf("TestState.Stage has an invalid value: %v", t.Stage)
172+
}
173+
if t.Command != nil && !t.Command.Valid() {
174+
return errors.Errorf("TestState.Command has an invalid value: %v", t.Command)
175+
}
176+
if t.CommandFor != nil && !t.CommandFor.Valid() {
177+
return errors.Errorf("TestState.CommandFor has an invalid value: %v", t.CommandFor)
178+
}
179+
for i, gameEvent := range t.GameEvents {
180+
if !gameEvent.Type.Valid() {
181+
return errors.Errorf("TestState.GameEvents[%v].Type has an invalid value: %v", i, gameEvent.Type)
182+
}
183+
}
184+
for team := range t.TeamState {
185+
if !team.Valid() {
186+
return errors.Errorf("TestState.TeamState has an invalid key: %v", team)
187+
}
188+
}
189+
if t.Division != nil && !t.Division.Valid() {
190+
return errors.Errorf("TestState.Division has an invalid value: %v", t.Division)
191+
}
192+
if t.NextCommand != nil && !t.NextCommand.Valid() {
193+
return errors.Errorf("TestState.NextCommand has an invalid value: %v", t.NextCommand)
194+
}
195+
if t.NextCommandFor != nil && !t.NextCommandFor.Valid() {
196+
return errors.Errorf("TestState.NextCommandFor has an invalid value: %v", t.NextCommandFor)
197+
}
198+
if t.GameEventBehavior != nil {
199+
for gameEventType, gameEventBehavior := range *t.GameEventBehavior {
200+
if !gameEventType.Valid() {
201+
return errors.Errorf("TestState.GameEventBehavior has an invalid key: %v", gameEventType)
202+
}
203+
if !gameEventBehavior.Valid() {
204+
return errors.Errorf("TestState.GameEventBehavior[%v] has an invalid value: %v", gameEventType, gameEventBehavior)
205+
}
206+
}
207+
}
208+
for _, proposal := range t.GameEventProposals {
209+
if !proposal.GameEvent.Type.Valid() {
210+
return errors.Errorf("TestState.GameEventProposals.GameEvent.Type has an invalid value: %v", proposal.GameEvent.Type)
211+
}
212+
}
213+
return nil
214+
}
215+
139216
func (t *TestState) applyTo(s *State) {
140217
if t.Stage != nil {
141218
s.Stage = *t.Stage
@@ -170,9 +247,8 @@ func (t *TestState) applyTo(s *State) {
170247
if t.Division != nil {
171248
s.Division = *t.Division
172249
}
173-
if t.PlacementPos != nil {
174-
s.PlacementPos = t.PlacementPos
175-
}
250+
// special case: always apply placement pos in order to make it resettable
251+
s.PlacementPos = t.PlacementPos
176252
if t.AutoContinue != nil {
177253
s.AutoContinue = *t.AutoContinue
178254
}

internal/app/controller/gameEvent.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,60 @@ const (
5959
GameEventUnsportingBehaviorMajor GameEventType = "unsportingBehaviorMajor"
6060
)
6161

62+
// GameStates contain all known command enum constants
63+
var GameEventTypes = []GameEventType{
64+
GameEventNone,
65+
GameEventPrepared,
66+
GameEventNoProgressInGame,
67+
GameEventPlacementFailedByTeamInFavor,
68+
GameEventPlacementFailedByOpponent,
69+
GameEventPlacementSucceeded,
70+
GameEventBotSubstitution,
71+
GameEventTooManyRobots,
72+
GameEventBallLeftFieldTouchLine,
73+
GameEventBallLeftFieldGoalLine,
74+
GameEventPossibleGoal,
75+
GameEventGoal,
76+
GameEventIndirectGoal,
77+
GameEventChippedGoal,
78+
GameEventAimlessKick,
79+
GameEventKickTimeout,
80+
GameEventKeeperHeldBall,
81+
GameEventAttackerDoubleTouchedBall,
82+
GameEventAttackerInDefenseArea,
83+
GameEventAttackerTouchedKeeper,
84+
GameEventBotDribbledBallTooFar,
85+
GameEventBotKickedBallTooFast,
86+
GameEventAttackerTooCloseToDefenseArea,
87+
GameEventBotInterferedPlacement,
88+
GameEventBotCrashDrawn,
89+
GameEventBotCrashUnique,
90+
GameEventBotCrashUniqueSkipped,
91+
GameEventBotPushedBot,
92+
GameEventBotPushedBotSkipped,
93+
GameEventBotHeldBallDeliberately,
94+
GameEventBotTippedOver,
95+
GameEventBotTooFastInStop,
96+
GameEventDefenderTooCloseToKickPoint,
97+
GameEventDefenderInDefenseAreaPartially,
98+
GameEventDefenderInDefenseArea,
99+
GameEventMultipleCards,
100+
GameEventMultiplePlacementFailures,
101+
GameEventMultipleFouls,
102+
GameEventUnsportingBehaviorMinor,
103+
GameEventUnsportingBehaviorMajor,
104+
}
105+
106+
// Valid checks if the GameEventType enum value is among the known values
107+
func (g GameEventType) Valid() bool {
108+
for _, gameState := range GameEventTypes {
109+
if gameState == g {
110+
return true
111+
}
112+
}
113+
return false
114+
}
115+
62116
// GameEvent combines the type of a game event with the corresponding detail structures
63117
type GameEvent struct {
64118
Type GameEventType `json:"type"`

internal/app/controller/state.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ func NewTeam(team refproto.Team) Team {
6565
return TeamUnknown
6666
}
6767

68+
// Teams contain all known team enum constants
69+
var Teams = []Team{TeamYellow, TeamBlue, TeamBoth, TeamUnknown}
70+
71+
// Valid checks if the Team enum value is among the known values
72+
func (t Team) Valid() bool {
73+
for _, team := range Teams {
74+
if team == t {
75+
return true
76+
}
77+
}
78+
return false
79+
}
80+
6881
// Stage represents the different stages of a game
6982
type Stage string
7083

@@ -117,6 +130,16 @@ var Stages = []Stage{
117130
StagePostGame,
118131
}
119132

133+
// Valid checks if the Stage enum value is among the known values
134+
func (s Stage) Valid() bool {
135+
for _, stage := range Stages {
136+
if stage == s {
137+
return true
138+
}
139+
}
140+
return false
141+
}
142+
120143
func (s Stage) index() (int, error) {
121144
for i, v := range Stages {
122145
if v == s {
@@ -194,6 +217,31 @@ const (
194217
CommandBallPlacement RefCommand = "ballPlacement"
195218
)
196219

220+
// Commands contain all known command enum constants
221+
var RefCommands = []RefCommand{
222+
CommandUnknown,
223+
CommandHalt,
224+
CommandStop,
225+
CommandNormalStart,
226+
CommandForceStart,
227+
CommandDirect,
228+
CommandIndirect,
229+
CommandKickoff,
230+
CommandPenalty,
231+
CommandTimeout,
232+
CommandBallPlacement,
233+
}
234+
235+
// Valid checks if the RefCommand enum value is among the known values
236+
func (c RefCommand) Valid() bool {
237+
for _, command := range RefCommands {
238+
if command == c {
239+
return true
240+
}
241+
}
242+
return false
243+
}
244+
197245
func (c RefCommand) ContinuesGame() bool {
198246
switch c {
199247
case CommandNormalStart,
@@ -217,6 +265,10 @@ func (c RefCommand) NeedsTeam() bool {
217265
}
218266
}
219267

268+
func (c RefCommand) IsFreeKick() bool {
269+
return c == CommandDirect || c == CommandIndirect
270+
}
271+
220272
// GameState of a game
221273
type GameState string
222274

@@ -237,6 +289,27 @@ const (
237289
GameStateBallPlacement GameState = "Ball Placement"
238290
)
239291

292+
// GameStates contain all known command enum constants
293+
var GameStates = []GameState{
294+
GameStateHalted,
295+
GameStateStopped,
296+
GameStateRunning,
297+
GameStatePreKickoff,
298+
GameStatePrePenalty,
299+
GameStateTimeout,
300+
GameStateBallPlacement,
301+
}
302+
303+
// Valid checks if the GameState enum value is among the known values
304+
func (g GameState) Valid() bool {
305+
for _, gameState := range GameStates {
306+
if gameState == g {
307+
return true
308+
}
309+
}
310+
return false
311+
}
312+
240313
// TeamInfo about a team
241314
type TeamInfo struct {
242315
Name string `json:"name" yaml:"name"`
@@ -270,6 +343,19 @@ const (
270343
GameEventBehaviorOff GameEventBehavior = "off"
271344
)
272345

346+
// GameEventBehaviors contain all known command enum constants
347+
var GameEventBehaviors = []GameEventBehavior{GameEventBehaviorOn, GameEventBehaviorMajority, GameEventBehaviorOff}
348+
349+
// Valid checks if the GameEventBehavior enum value is among the known values
350+
func (b GameEventBehavior) Valid() bool {
351+
for _, behavior := range GameEventBehaviors {
352+
if behavior == b {
353+
return true
354+
}
355+
}
356+
return false
357+
}
358+
273359
// GameEventProposal holds a proposal for a game event from an autoRef
274360
type GameEventProposal struct {
275361
ProposerId string `json:"proposerId"`
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
initialState:
2+
stage: First Half
3+
command: stop
4+
division: DivA
5+
stageTimeLeft: 5m
6+
transitions:
7+
- event:
8+
command:
9+
type: ballPlacement
10+
forTeam: Yellow
11+
location:
12+
x: 4.2
13+
y: -1.5
14+
expectedStateDiff:
15+
command: ballPlacement
16+
commandFor: Yellow
17+
placementPos:
18+
x: 4.2
19+
y: -1.5
20+
- tick: 1s # make sure that the stage time is not proceeded
21+

0 commit comments

Comments
 (0)