@@ -138,138 +138,181 @@ def stopping(bot, state):
138138 assert res [0 ] == "success"
139139
140140
141- @pytest .mark .parametrize ("checkpoint" , range (12 ))
142- def test_client_broken (zmq_context , checkpoint ):
143- # This test runs a test game against a (malicious) server client
144- # (a malicious subprocess client is harder to test)
145- # Depending on the checkpoint selected, the broken test client will
146- # run up to a particular point and then send a malicious message.
141+ def dealer_good (q , * , num_requests , timeout ):
142+ zmq_context = zmq .Context ()
143+ sock = zmq_context .socket (zmq .DEALER )
144+ poll = zmq .Poller ()
147145
148- # Depending on whether this message occurs in the game setup stage
149- # or during the game run, this will either set the phase to FAILURE or
150- # let the good team win. Pelita itself should not break in the process.
146+ port = sock .bind_to_random_port ('tcp://127.0.0.1' )
147+ q .put (port )
148+
149+ poll .register (sock , zmq .POLLIN )
150+ _available_socks = poll .poll (timeout = timeout )
151+ request = sock .recv_json ()
152+ assert request ['REQUEST' ]
153+ sock .send_json ({'__status__' : 'ok' , '__data__' : {'team_name' : 'good player' }})
154+
155+ _available_socks = poll .poll (timeout = timeout )
156+ set_initial = sock .recv_json (flags = zmq .NOBLOCK )
157+ if set_initial ['__action__' ] == 'exit' :
158+ return
159+ assert set_initial ['__action__' ] == "set_initial"
160+ sock .send_json ({'__uuid__' : set_initial ['__uuid__' ], '__return__' : None })
161+
162+ for _i in range (num_requests ):
163+ _available_socks = poll .poll (timeout = timeout )
164+ game_state = sock .recv_json (flags = zmq .NOBLOCK )
165+ msg_id = game_state ['__uuid__' ]
151166
152- timeout = 3000
167+ action = game_state ['__action__' ]
168+ if action == 'exit' :
169+ return
170+ assert set_initial ['__action__' ] == "set_initial"
153171
154- q1 = queue . Queue ()
155- q2 = queue . Queue ( )
172+ current_pos = game_state [ '__data__' ][ 'game_state' ][ 'team' ][ 'bot_positions' ][ game_state [ '__data__' ][ 'game_state' ][ 'bot_turn' ]]
173+ sock . send_json ({ '__uuid__' : msg_id , '__return__' : { 'move' : current_pos }} )
156174
157- def dealer_good (q ):
158- zmq_context = zmq .Context ()
159- sock = zmq_context .socket (zmq .DEALER )
160- poll = zmq .Poller ()
175+ _available_socks = poll .poll (timeout = timeout )
176+ exit_state = sock .recv_json (flags = zmq .NOBLOCK )
161177
162- port = sock .bind_to_random_port ('tcp://127.0.0.1' )
163- q .put (port )
178+ assert exit_state ['__action__' ] == 'exit'
164179
165- poll .register (sock , zmq .POLLIN )
166- _available_socks = poll .poll (timeout = timeout )
167- request = sock .recv_json ()
168- assert request ['REQUEST' ]
169- sock .send_json ({'__status__' : 'ok' , '__data__' : {'team_name' : 'good player' }})
180+ def dealer_bad (q , * , team_name = None , num_requests , checkpoint , timeout ):
181+ zmq_context = zmq .Context ()
182+ sock = zmq_context .socket (zmq .DEALER )
183+ poll = zmq .Poller ()
170184
185+ port = sock .bind_to_random_port ('tcp://127.0.0.1' )
186+ q .put (port )
187+
188+ poll .register (sock , zmq .POLLIN )
189+ # we set our recv to raise, if there is no message (zmq.NOBLOCK),
190+ # so we do not need to care to check whether something is in the _available_socks
191+ _available_socks = poll .poll (timeout = timeout )
192+
193+ request = sock .recv_json (flags = zmq .NOBLOCK )
194+ assert request ['REQUEST' ]
195+ if checkpoint == 1 :
196+ sock .send_string ("" )
197+ return
198+ elif checkpoint == 2 :
199+ sock .send_json ({'__status__' : 'ok' })
200+ return
201+ else :
202+ if team_name is None :
203+ team_name = f'bad <{ checkpoint } >'
204+ sock .send_json ({'__status__' : 'ok' , '__data__' : {'team_name' : team_name }})
205+
206+ _available_socks = poll .poll (timeout = timeout )
207+
208+ set_initial = sock .recv_json (flags = zmq .NOBLOCK )
209+
210+ if checkpoint == 3 :
211+ sock .send_string ("" )
212+ return
213+ elif checkpoint == 4 :
214+ sock .send_json ({'__uuid__' : 'ok' })
215+ return
216+ else :
217+ sock .send_json ({'__uuid__' : set_initial ['__uuid__' ], '__data__' : None })
218+
219+ for _i in range (num_requests ):
171220 _available_socks = poll .poll (timeout = timeout )
172- set_initial = sock .recv_json (flags = zmq .NOBLOCK )
173- if set_initial ['__action__' ] == 'exit' :
221+ game_state = sock .recv_json (flags = zmq .NOBLOCK )
222+ msg_id = game_state ['__uuid__' ]
223+
224+ action = game_state ['__action__' ]
225+ if action == 'exit' :
174226 return
175- assert set_initial ['__action__' ] == "set_initial"
176- sock .send_json ({'__uuid__' : set_initial ['__uuid__' ], '__return__' : None })
177227
178- for _i in range (8 ):
179- _available_socks = poll .poll (timeout = timeout )
180- game_state = sock .recv_json (flags = zmq .NOBLOCK )
228+ current_pos = game_state ['__data__' ]['game_state' ]['team' ]['bot_positions' ][game_state ['__data__' ]['game_state' ]['bot_turn' ]]
229+ if checkpoint == 5 :
230+ sock .send_string ("No json" )
231+ return
232+ elif checkpoint == 6 :
233+ # This is an acceptable message that will never match a request
234+ # We can send the correct message afterwards and the match continues
235+ sock .send_json ({'__uuid__' : "Bad" , '__return__' : "Nothing" })
236+ sock .send_json ({'__uuid__' : msg_id , '__return__' : {'move' : current_pos }})
237+ elif checkpoint == 7 :
238+ sock .send_json ({'__uuid__' : msg_id , '__return__' : {'move' : [0 , 0 ]}})
239+ return
240+ elif checkpoint == 8 :
241+ sock .send_json ({'__uuid__' : msg_id , '__return__' : {'move' : "NOTHING" }})
242+ return
243+ elif checkpoint == 9 :
244+ # cannot become a tuple
245+ sock .send_json ({'__uuid__' : msg_id , '__return__' : {'move' : 12345 }})
246+ return
247+ elif checkpoint == 10 :
248+ sock .send_json ({'__uuid__' : msg_id , '__return__' : "NOT A DICT" })
249+ return
250+ else :
251+ sock .send_json ({'__uuid__' : msg_id , '__return__' : {'move' : current_pos }})
181252
182- action = game_state ['__action__' ]
183- if action == 'exit' :
184- return
185- assert set_initial ['__action__' ] == "set_initial"
186253
187- current_pos = game_state [ '__data__' ][ 'game_state' ][ 'team' ][ 'bot_positions' ][ game_state [ '__data__' ][ 'game_state' ][ 'bot_turn' ]]
188- sock . send_json ({ '__uuid__' : game_state [ '__uuid__' ], '__return__' : { 'move' : current_pos }})
254+ def test_bad_team_name_is_currently_not_tested_in_backend ( zmq_context ):
255+ timeout = 3000
189256
190- _available_socks = poll . poll ( timeout = timeout )
191- exit_state = sock . recv_json ( flags = zmq . NOBLOCK )
257+ q1 = queue . Queue ( )
258+ q2 = queue . Queue ( )
192259
193- assert exit_state ['__action__' ] == 'exit'
260+ with concurrent .futures .ThreadPoolExecutor (max_workers = 2 ) as executor :
261+ players = []
262+ players .append (executor .submit (dealer_good , q1 , num_requests = 8 , timeout = timeout ))
263+ players .append (executor .submit (dealer_bad , q2 , team_name = "Long bad team name 123456789 123456789!!" , checkpoint = 0 , num_requests = 8 , timeout = timeout ))
194264
195- def dealer_bad (q ):
196- zmq_context = zmq .Context ()
197- sock = zmq_context .socket (zmq .DEALER )
198- poll = zmq .Poller ()
265+ port1 = q1 .get ()
266+ port2 = q2 .get ()
199267
200- port = sock .bind_to_random_port ('tcp://127.0.0.1' )
201- q .put (port )
268+ layout = {'walls' : ((0 , 0 ), (0 , 1 ), (0 , 2 ), (0 , 3 ), (0 , 4 ), (0 , 5 ), (1 , 0 ), (1 , 5 ), (2 , 0 ), (2 , 5 ), (3 , 0 ), (3 , 2 ), (3 , 3 ), (3 , 5 ), (4 , 0 ), (4 , 2 ), (4 , 3 ), (4 , 5 ), (5 , 0 ), (5 , 5 ), (6 , 0 ), (6 , 5 ), (7 , 0 ), (7 , 1 ), (7 , 2 ), (7 , 3 ), (7 , 4 ), (7 , 5 )), 'food' : [(1 , 1 ), (1 , 2 ), (2 , 1 ), (2 , 2 ), (5 , 3 ), (5 , 4 ), (6 , 3 ), (6 , 4 )], 'bots' : [(1 , 3 ), (6 , 2 ), (1 , 4 ), (6 , 1 )], 'shape' : (8 , 6 )}
202269
203- poll .register (sock , zmq .POLLIN )
204- # we set our recv to raise, if there is no message (zmq.NOBLOCK),
205- # so we do not need to care to check whether something is in the _available_socks
206- _available_socks = poll .poll (timeout = timeout )
270+ game_state = setup_game ([
271+ f'pelita://127.0.0.1:{ port1 } /PLAYER1' ,
272+ f'pelita://127.0.0.1:{ port2 } /PLAYER2'
273+ ],
274+ layout_dict = layout ,
275+ max_rounds = 2 ,
276+ timeout_length = 1 ,
277+ )
207278
208- request = sock .recv_json (flags = zmq .NOBLOCK )
209- assert request ['REQUEST' ]
210- if checkpoint == 1 :
211- sock .send_string ("" )
212- return
213- elif checkpoint == 2 :
214- sock .send_json ({'__status__' : 'ok' })
215- return
216- else :
217- sock .send_json ({'__status__' : 'ok' , '__data__' : {'team_name' : f'bad <{ checkpoint } >' }})
279+ # check that the game_state ends in the expected phase
280+ # assert game_state['game_phase'] == 'FAILURE'
218281
219- _available_socks = poll .poll (timeout = timeout )
282+ while game_state ['game_phase' ] == 'RUNNING' :
283+ game_state = play_turn (game_state )
220284
221- set_initial = sock . recv_json ( flags = zmq . NOBLOCK )
285+ assert game_state [ 'team_names' ] == [ 'good player' , 'Long bad team name 123456789 123456789!!' ]
222286
223- if checkpoint == 3 :
224- sock .send_string ("" )
225- return
226- elif checkpoint == 4 :
227- sock .send_json ({'__uuid__' : 'ok' })
228- return
229- else :
230- sock .send_json ({'__uuid__' : set_initial ['__uuid__' ], '__data__' : None })
231-
232- for _i in range (8 ):
233- _available_socks = poll .poll (timeout = timeout )
234- game_state = sock .recv_json (flags = zmq .NOBLOCK )
235-
236- action = game_state ['__action__' ]
237- if action == 'exit' :
238- return
239-
240- current_pos = game_state ['__data__' ]['game_state' ]['team' ]['bot_positions' ][game_state ['__data__' ]['game_state' ]['bot_turn' ]]
241- if checkpoint == 5 :
242- sock .send_string ("No json" )
243- return
244- elif checkpoint == 6 :
245- # This is an acceptable message that will never match a request
246- # We can send the correct message afterwards and the match continues
247- sock .send_json ({'__uuid__' : "Bad" , '__return__' : "Nothing" })
248- sock .send_json ({'__uuid__' : game_state ['__uuid__' ], '__return__' : {'move' : current_pos }})
249- elif checkpoint == 7 :
250- sock .send_json ({'__uuid__' : game_state ['__uuid__' ], '__return__' : {'move' : [0 , 0 ]}})
251- return
252- elif checkpoint == 8 :
253- sock .send_json ({'__uuid__' : game_state ['__uuid__' ], '__return__' : {'move' : "NOTHING" }})
254- return
255- elif checkpoint == 9 :
256- # cannot become a tuple
257- sock .send_json ({'__uuid__' : game_state ['__uuid__' ], '__return__' : {'move' : 12345 }})
258- return
259- elif checkpoint == 10 :
260- sock .send_json ({'__uuid__' : game_state ['__uuid__' ], '__return__' : "NOT A DICT" })
261- return
262- else :
263- sock .send_json ({'__uuid__' : game_state ['__uuid__' ], '__return__' : {'move' : current_pos }})
287+ # check that no player had an uncaught exception
288+ for player in concurrent .futures .as_completed (players ):
289+ assert player .exception () is None , traceback .print_exception (player .exception (), limit = None , file = None , chain = True )
290+
291+
292+ @pytest .mark .parametrize ("checkpoint" , range (12 ))
293+ def test_client_broken (zmq_context , checkpoint ):
294+ # This test runs a test game against a (malicious) server client
295+ # (a malicious subprocess client is harder to test)
296+ # Depending on the checkpoint selected, the broken test client will
297+ # run up to a particular point and then send a malicious message.
298+
299+ # Depending on whether this message occurs in the game setup stage
300+ # or during the game run, this will either set the phase to FAILURE or
301+ # let the good team win. Pelita itself should not break in the process.
302+
303+ timeout = 3000
304+
305+ q1 = queue .Queue ()
306+ q2 = queue .Queue ()
264307
265308 with concurrent .futures .ThreadPoolExecutor (max_workers = 2 ) as executor :
266309 players = []
267- players .append (executor .submit (dealer_good , q1 ))
310+ players .append (executor .submit (dealer_good , q1 , num_requests = 8 , timeout = timeout ))
268311
269312 if checkpoint == 0 :
270- players .append (executor .submit (dealer_good , q2 ))
313+ players .append (executor .submit (dealer_good , q2 , num_requests = 8 , timeout = timeout ))
271314 else :
272- players .append (executor .submit (dealer_bad , q2 ))
315+ players .append (executor .submit (dealer_bad , q2 , checkpoint = checkpoint , num_requests = 8 , timeout = timeout ))
273316
274317 port1 = q1 .get ()
275318 port2 = q2 .get ()
0 commit comments