source: geniuswebcore/test/geniusweb/protocol/session/saop/SAOPTest.py@ 73

Last change on this file since 73 was 73, checked in by Bart Vastenhouw, 3 years ago

Fix for IssueValue hashcode.

File size: 16.6 KB
Line 
1from datetime import datetime, timedelta
2import time
3import unittest
4from unittest.mock import Mock
5
6from tudelft.utilities.listener.Listener import Listener
7from tudelft.utilities.repository.NoResourcesNowException import NoResourcesNowException
8from tudelft_utilities_logging.ReportToLogger import ReportToLogger
9from uri.uri import URI
10
11from geniusweb.actions.Accept import Accept
12from geniusweb.actions.Action import Action
13from geniusweb.actions.EndNegotiation import EndNegotiation
14from geniusweb.actions.Offer import Offer
15from geniusweb.actions.PartyId import PartyId
16from geniusweb.deadline.Deadline import Deadline
17from geniusweb.deadline.DeadlineTime import DeadlineTime
18from geniusweb.events.CurrentState import CurrentState
19from geniusweb.inform.ActionDone import ActionDone
20from geniusweb.inform.Agreements import Agreements
21from geniusweb.inform.Finished import Finished
22from geniusweb.inform.Inform import Inform
23from geniusweb.inform.Settings import Settings
24from geniusweb.inform.YourTurn import YourTurn
25from geniusweb.progress.Progress import Progress
26from geniusweb.protocol.ProtocolException import ProtocolException
27from geniusweb.protocol.partyconnection.ProtocolToPartyConn import ProtocolToPartyConn
28from geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory import ProtocolToPartyConnFactory
29from geniusweb.protocol.partyconnection.ProtocolToPartyConnections import ProtocolToPartyConnections
30from geniusweb.protocol.session.TeamInfo import TeamInfo
31from geniusweb.protocol.session.saop.SAOP import SAOP
32from geniusweb.protocol.session.saop.SAOPSettings import SAOPSettings
33from geniusweb.protocol.session.saop.SAOPState import SAOPState
34from geniusweb.references.Parameters import Parameters
35from geniusweb.references.PartyRef import PartyRef
36from geniusweb.references.PartyWithParameters import PartyWithParameters
37from geniusweb.references.PartyWithProfile import PartyWithProfile
38from geniusweb.references.ProfileRef import ProfileRef
39from geniusweb.references.ProtocolRef import ProtocolRef
40
41
42class SAOPTest(unittest.TestCase):
43 '''
44 This test tests inner workings of the SAOP protocol, AKA 'white box' testing.
45 This adds brittleness to this test, because these internal workings may be
46 modified as long as the public API remains working. This approach was
47 necessary because fully testing the public API directly would lead to
48 excessive amounts of Mocking and unfocused tests. (with "focused test" we
49 mean that a junit test should test a small part of the code and thus aid in
50 locating the actual issue).
51 '''
52 PARTY2ID = PartyId("party2")
53 PARTY1ID = PartyId("party1")
54 parameters = Parameters()
55 SAOPPROTOCOL = ProtocolRef(URI("SAOP"))
56
57 NOW = 1000
58 DEADLINE =1000
59 party1ref = PartyRef(URI("party1"))
60 party2ref = PartyRef(URI("party2"))
61 partywithparam1 = PartyWithParameters(party1ref, parameters)
62 partywithparam2 = PartyWithParameters(party2ref, parameters)
63
64
65 def setUp(self):
66 self.state = Mock(SAOPState)
67 self.connectedstate = Mock(SAOPState)
68 self.failstate = Mock(SAOPState)
69 self.finalstate = Mock(SAOPState)
70
71 self.settings = Mock(SAOPSettings)
72 self.team1 = Mock(TeamInfo)
73 self.team2 = Mock(TeamInfo)
74 self.conn1 = Mock(ProtocolToPartyConn)
75 self.conn2 = Mock(ProtocolToPartyConn);
76 self.progress = Mock(Progress)
77 self.testlistener = Mock(Listener)
78
79 self.deadlinetime = Mock(DeadlineTime)
80
81
82 self.profile1 = Mock(ProfileRef)
83 self.profile2 = Mock(ProfileRef)
84
85 pwp1 = PartyWithProfile(self.partywithparam1, self.profile1);
86 pwp2 = PartyWithProfile(self.partywithparam2, self.profile2);
87 self.team1.getParties=Mock(return_value=[pwp1])
88 self.team2.getParties=Mock(return_value=[pwp2])
89
90 partyprofiles = {}
91 partyprofiles[self.PARTY1ID]= self.team1
92 partyprofiles[self.PARTY2ID]= self.team2
93
94
95 self.pwpmap = {}
96 self.pwpmap[self.PARTY1ID]=pwp1
97 self.pwpmap[self.PARTY2ID]=pwp2
98 self.deadlinetime.getDuration=Mock(return_value=self.DEADLINE)
99
100 teams = []
101 teams.append(self.team1);
102 teams.append(self.team2);
103 self.settings.getTeams=Mock(return_value=teams)
104 self.settings.getAllParties=Mock(return_value=[pwp1, pwp2])
105 self.settings.getDeadline=Mock(return_value=self.deadlinetime)
106
107 self.factory = Mock(ProtocolToPartyConnFactory)
108 # connections = Mock(List);
109 connections = [self.conn1, self.conn2]
110 self.factory.connectAll=Mock(return_value=connections)
111
112 # hack the state.with(connection) stuff simplistically
113
114 self.conn1.getParty=Mock(return_value=self.PARTY1ID)
115 self.conn2.getParty=Mock(return_value=self.PARTY2ID)
116
117 self.MockState(self.connectedstate, "connected state", True)
118 self.MockState(self.state, "running state",True);
119
120 self.MockState(self.finalstate, "final state",True);
121 self.finalstate.isFinal=Mock(return_value=True)
122 self.MockState(self.failstate, "fail state", True);
123 self.failstate.isFinal=Mock(return_value=True)
124
125 self.connectionswithparties = ProtocolToPartyConnections([self.conn1, self.conn2])
126
127 # HACK thenReturn twice, so that 2nd call to iterator() returns new
128 # iterator instead of the old one
129 profilesmap = {}
130 profilesmap[self.PARTY1ID]= pwp1
131 profilesmap[self.PARTY2ID]= pwp2
132 self.state.getPartyProfiles=Mock(return_value=profilesmap)
133
134 self.saop = SAOP(self.state, ReportToLogger("test"),
135 self.connectionswithparties)
136 self.saop.addListener(self.testlistener)
137
138
139 def MockState(self, state:SAOPState , asText:str, isConnected:bool ) :
140 '''
141 All states are more or less the same, but are slightly modified
142
143 @param state the state to Mock (must be already
144 @param asText short name for the state, for debugging
145 @param bool true if parties are connected
146 '''
147 state.getSettings=Mock(return_value=self.settings) #type:ignore
148 if isConnected:
149 state.getConnections=Mock(return_value=[self.PARTY1ID, self.PARTY2ID]) #type:ignore
150 else:
151 state.getConnections=Mock(return_value=[]) #type:ignore
152 # when(state.getConnections()).thenReturn(connectionswithparties);
153
154 state.getProgress=Mock(return_value=self.progress) #type:ignore
155 state.WithParty=Mock(return_value=self.connectedstate) #type:ignore
156 state._getNextActor=Mock(return_value=self.PARTY1ID) #type:ignore
157 state.WithAction= Mock(side_effect=lambda pid, act: #type:ignore
158 self.finalstate if isinstance(act, EndNegotiation) else\
159 state )
160
161 state.WithProgress=Mock(return_value=state) #type:ignore
162 state.__repr__=Mock(return_value=asText) #type:ignore
163 state.WithException=Mock(return_value=self.failstate) #type:ignore
164 state.getAgreements=Mock(return_value=Agreements()) #type:ignore
165
166 #explicit default. This differs from Java, where default bools to false
167 state.isFinal=Mock(return_value=False) #type:ignore
168 state.getPartyProfiles = Mock(return_value=self.pwpmap) #type:ignore
169
170 def testConstructor(self):
171 state1 = Mock(SAOPState)
172 dl = Mock(Deadline)
173 set = Mock(SAOPSettings)
174 state1.getSettings=Mock(return_value=set)
175 set.getDeadline=Mock(return_value=dl)
176 dl.getDuration=Mock(return_value=1000)
177 SAOP(state1, ReportToLogger("test"), self.connectionswithparties)
178 self.assertEqual([], self.state.WithException.call_args_list)
179 self.assertEqual([], self. testlistener.notifyChange.call_args_list)
180
181
182 def testConnect(self):
183 self.saop._connect(self.factory)
184
185 self.assertEqual(self.connectedstate, self.saop.getState())
186 self.assertEqual([], self.state.WithException.call_args_list)
187 self.assertEqual([], self.testlistener.notifyChange.call_args_list)
188
189
190 def testConnectFailingConnection(self):
191 # override the factory connect to throw IOException. This exception
192 # should boil up and cause the connect to fail.
193 def __raise(refs):
194 raise ConnectionError("Refusing connection")
195 self.factory.connectAll=Mock(side_effect=__raise)
196 this=self
197 self.assertRaises(ConnectionError, lambda:this.saop._connect(this.factory))
198 # following was never reached?
199 #verify(state, times(0)).with(any(ProtocolException));
200 #verify(testlistener, times(0)).notifyChange(any(CurrentState));
201
202 def testConnectRetry(self):
203 # override the factory connect to throw NoResourcesNowException and
204 # then succeed 2nd time.
205 this=self
206
207 firsttime=True
208 def __throwsThenSucceeds():
209 if firsttime:
210 firsttime=False
211 raise NoResourcesNowException("Refusing connection",\
212 datetime.now() + timedelta(seconds=0.5))
213 return this.connections
214 self.factory.connect=Mock(side_effect=__throwsThenSucceeds)
215
216 self.saop._connect(self.factory)
217
218 self.assertEqual(self.connectedstate, self.saop.getState())
219 #verify(state, times(0)).with(any(ProtocolException));
220 self.assertEqual([], [e \
221 for e in self.state.WithException.call_args_list\
222 if not isinstance( e,ProtocolException)])
223 self.assertEqual([],self.testlistener.notifyChange.call_args_list)
224
225 def testSetup(self) :
226 self.saop._setupParties()
227
228 # were the connections attached properly?
229 self.assertEqual(1, len(self.conn1.addListener.call_args_list))
230 self.assertEqual(1, len(self.conn2.addListener.call_args_list))
231
232 # were the settings send to each party?
233 self.assertEqual(1, len([call for call in self.conn1.send.call_args_list \
234 if isinstance(call[0][0],Settings)]))
235 self.assertEqual(
236 Settings(self.PARTY1ID, self.profile1, self.SAOPPROTOCOL, self.progress, self.parameters),
237 self.conn1.send.call_args_list[0][0][0])
238
239 self.assertEqual(1, len([call for call in self.conn2.send.call_args_list \
240 if isinstance(call[0][0],Settings)]))
241 self.assertEqual(
242 Settings(self.PARTY2ID, self.profile2, self.SAOPPROTOCOL, self.progress, self.parameters),
243 self.conn2.send.call_args_list[0][0][0])
244
245 self.assertEqual([], self.state.WithException.call_args_list)
246 self.assertEqual([], [v for v in self.testlistener.notifyChange.call_args_list if isinstance(v, CurrentState)])
247
248
249 def testActionRequestWrongActor(self):
250 self.saop._actionRequest(self.conn2, Mock(EndNegotiation))
251 self.assertEqual(1, len([call for call in self.state.WithException.call_args_list\
252 if isinstance(call[0][0], ProtocolException) ]))
253 self.assertEqual(1, len([call for call in self.testlistener.notifyChange.call_args_list
254 if isinstance(call[0][0], CurrentState)]))
255
256 def testActionRequest(self):
257 self.saop._nextTurn()
258
259 self.state._getNextActor=Mock(return_value=self.PARTY1ID)
260 self.state.withAction = Mock(return_value=self.finalstate)
261
262 self.saop._actionRequest(self.conn1, Mock(EndNegotiation))
263
264 self.assertEqual([] , [call for call in self.state.WithException.call_args_list
265 if isinstance(call[0][0], ProtocolException)])
266 #verify(state, times(1)).with(eq(PARTY1ID), any(EndNegotiation));
267 self.assertEqual(1, len([call for call in self.state.WithAction.call_args_list
268 if call[0][0]==self.PARTY1ID and\
269 isinstance(call[0][1], EndNegotiation)]))
270 # verify broadcast
271 self.assertEqual(1, len([call for call in self.conn1.send.call_args_list
272 if isinstance(call[0][0], ActionDone)]))
273 self.assertEqual(1, len([call for call in self.conn2.send.call_args_list
274 if isinstance(call[0][0], ActionDone)]))
275
276 # state.getNextActor() is frozen to PARTY1 by our Mocking
277 #verify(conn1, times(1)).send(isA(YourTurn));
278 self.assertEqual(1, len([call for call in self.conn1.send.call_args_list
279 if isinstance(call[0][0], YourTurn)]))
280 #verify(conn2, times(0)).send(isA(YourTurn));
281 self.assertEqual(0, len([call for call in self.conn2.send.call_args_list
282 if isinstance(call[0][0], YourTurn)]))
283 #listener should be informed about final state.
284 #verify(testlistener, times(0)).notifyChange(any(CurrentState));
285 self.assertEqual(1, len([call for call in self.testlistener.notifyChange.call_args_list
286 if isinstance(call[0][0], CurrentState)]))
287
288 def testFinalActionRequest(self):
289 this=self
290 #FIXME double mock??
291 #self.state.getNextActor=Mock(return_value=self.PARTY1ID)
292 def __nextstate(pid:PartyId, act:Action):
293 if isinstance(act, EndNegotiation):
294 return this.finalstate
295
296 self.state.withAction=Mock(side_effect=__nextstate)
297 # when(finalstate.getAgreements()).thenReturn(Mock(Agreements));
298
299 self.saop._nextTurn() # ensure we send YourTurn to party1.
300 self.saop._actionRequest(self.conn1, Mock(EndNegotiation))
301
302 #verify(state, times(0)).with(any(ProtocolException));
303 self.assertEqual([], [call for call in self.state.WithException.call_args_list
304 if isinstance(call[0][0], ProtocolException) ])
305 #verify(state, times(1)).with(eq(PARTY1ID), any(EndNegotiation));
306 self.assertEqual(1, len([call for call in self.state.WithAction.call_args_list\
307 if isinstance(call[0][1], EndNegotiation)]))
308
309 # verify broadcast
310 #erify(conn1, times(1)).send(isA(Finished));
311 self.assertEqual(1, len([call for call in self.conn1.send.call_args_list
312 if isinstance(call[0][0], Finished)]))
313
314 #verify(conn2, times(1)).send(isA(Finished));
315 self.assertEqual(1, len([call for call in self.conn2.send.call_args_list
316 if isinstance(call[0][0], Finished)]))
317
318 #verify(testlistener, times(1)).notifyChange(any(CurrentState));
319 self.assertEqual(1, len([call for call in self.testlistener.notifyChange.call_args_list
320 if isinstance(call[0][0], CurrentState)]))
321
322 #verify(conn1, times(1)).send(isA(YourTurn));
323 self.assertEqual(1, len([call for call in self.conn1.send.call_args_list
324 if isinstance(call[0][0], YourTurn)]))
325 #verify(conn2, times(0)).send(isA(YourTurn));
326 self.assertEqual( [] , [call for call in self.conn2.send.call_args_list
327 if isinstance(call[0][0], YourTurn)])
328
329 def testStart(self):
330 self.saop.start(self.factory)
331
332 def testActionFailNextYourturn(self):
333 this=self
334 def __throwExc(inf:Inform):
335 if isinstance(inf, YourTurn):
336 raise ConnectionError("fail sending yourturn")
337 self.conn1.send=Mock(side_effect=__throwExc)
338 # CHECK is the state really final after an exception?
339 self.state.WithException=Mock(return_value=this.finalstate)
340 # when(state.getAgreements()).thenReturn(Mock(Agreements));
341 self.saop._actionRequest(self.conn1, Mock(Accept))
342
343
344 #verify(state, times(1)).with(any(ProtocolException));
345 self.assertEqual( 1, len([call for call in self.state.WithException.call_args_list
346 if isinstance(call[0][0], ProtocolException) ]))
347 #verify(testlistener, times(1)).notifyChange(any(CurrentState));
348 self.assertEqual(1, len([call for call in self.testlistener.notifyChange.call_args_list
349 if isinstance(call[0][0], CurrentState)]))
350
351 def testActionNotTheTurn(self) :
352 def __throwExc(inf:Inform):
353 if isinstance(inf, YourTurn):
354 raise ConnectionError("fail sending yourturn")
355 self.conn1.send=Mock(side_effect=__throwExc)
356
357 # when(failstate.getAgreements()).thenReturn(Mock(Agreements));
358 # not turn of conn2.
359 self.saop._actionRequest(self.conn2, Mock(EndNegotiation))
360
361 #verify(state, times(1)).with(any(ProtocolException));
362 self.assertEqual( 1, len([call for call in self.state.WithException.call_args_list
363 if isinstance(call[0][0], ProtocolException) ]))
364 #verify(testlistener, times(1)).notifyChange(any(CurrentState));
365 self.assertEqual(1, len([call for call in self.testlistener.notifyChange.call_args_list
366 if isinstance(call[0][0], CurrentState)]))
367
368
369 def testActionInFinalState(self):
370 saop = SAOP(self.finalstate, ReportToLogger("test"),
371 self.connectionswithparties);
372 # when(finalstate.getAgreements()).thenReturn(Mock(Agreements));
373
374 saop._actionRequest(self.conn1, Mock(Offer))
375
376 #verify(finalstate, times(0)).with(any(ProtocolException));
377 self.assertEqual( [], [call for call in self.state.WithException.call_args_list
378 if isinstance(call[0][0], ProtocolException) ])
379 #verify(testlistener, times(0)).notifyChange(any());
380 self.assertEqual([], self.testlistener.notifyChange.call_args_list)
381
382 def testDescription(self):
383 self.assertNotEquals(None,self.saop.getDescription())
384
385 def testAddParticipant(self):
386 self.assertRaises(ValueError, lambda:self.saop.addParticipant(None))
387
388 def testDeadlineTimer(self):
389 unconnectedstate = Mock(SAOPState)
390 self.MockState(unconnectedstate, "unconnected state", False)
391 saopempty = SAOP(unconnectedstate, ReportToLogger("test"),
392 ProtocolToPartyConnections([]))
393 saopempty.addListener(self.testlistener)
394
395 saopempty.start(self.factory);
396 # verify(testlistener, times(0)).notifyChange(any(CurrentState.class));
397 self.assertEqual([], [call for call in self.testlistener.notifyChange.call_args_list
398 if isinstance(call[0][0], CurrentState) ])
399
400 #verify(conn1, times(1)).send(isA(YourTurn.class));
401 self.assertEqual(1, len([call for call in self.conn1.send.call_args_list
402 if isinstance(call[0][0], YourTurn)]))
403 time.sleep( (self.DEADLINE + SAOP._TIME_MARGIN + 100)/1000.)
404 #verify(testlistener, times(1)).notifyChange(any(CurrentState.class));
405 self.assertEqual(1, len([call for call in self.testlistener.notifyChange.call_args_list
406 if isinstance(call[0][0], CurrentState)]))
407 #verify(conn1, times(1)).send(isA(Finished.class));
408 self.assertEqual(1, len([call for call in self.conn1.send.call_args_list
409 if isinstance(call[0][0], Finished)]))
410
411
Note: See TracBrowser for help on using the repository browser.