Index: /exampleparties/boulware/requirements.txt
===================================================================
--- /exampleparties/boulware/requirements.txt	(revision 94)
+++ /exampleparties/boulware/requirements.txt	(revision 95)
@@ -1,3 +1,3 @@
-https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/87/exampleparties/timedependentparty/dist/timedependentparty-1.2.0.tar.gz
+https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/94/exampleparties/timedependentparty/dist/timedependentparty-1.2.1.tar.gz
 mypy
 
Index: /exampleparties/boulware/setup.py
===================================================================
--- /exampleparties/boulware/setup.py	(revision 94)
+++ /exampleparties/boulware/setup.py	(revision 95)
@@ -1,5 +1,5 @@
 from setuptools import setup, find_packages
 
-ver = '1.2.0'
+ver = '1.2.1'
 
 setup(
@@ -11,5 +11,5 @@
     packages=find_packages(exclude=["test", "test.*", "test.*.*"]),
     package_data={ 'boulware': ['py.typed']    },
-    install_requires=[ "timedependentparty@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/87/exampleparties/timedependentparty/dist/timedependentparty-" + ver + ".tar.gz"],
+    install_requires=[ "timedependentparty@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/94/exampleparties/timedependentparty/dist/timedependentparty-" + ver + ".tar.gz"],
     py_modules=['party']
 )
Index: /exampleparties/conceder/requirements.txt
===================================================================
--- /exampleparties/conceder/requirements.txt	(revision 94)
+++ /exampleparties/conceder/requirements.txt	(revision 95)
@@ -1,3 +1,3 @@
-https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/87/exampleparties/timedependentparty/dist/timedependentparty-1.2.0.tar.gz
+https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/94/exampleparties/timedependentparty/dist/timedependentparty-1.2.1.tar.gz
 mypy
 
Index: /exampleparties/conceder/setup.py
===================================================================
--- /exampleparties/conceder/setup.py	(revision 94)
+++ /exampleparties/conceder/setup.py	(revision 95)
@@ -1,5 +1,5 @@
 from setuptools import setup, find_packages
 
-ver = '1.2.0'
+ver = '1.2.1'
 
 setup(
@@ -11,5 +11,5 @@
     packages=find_packages(exclude=["test", "test.*", "test.*.*"]),
     package_data={ 'conceder': ['py.typed']    },
-    install_requires=[ "timedependentparty@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/87/exampleparties/timedependentparty/dist/timedependentparty-" + ver + ".tar.gz"],
+    install_requires=[ "timedependentparty@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/94/exampleparties/timedependentparty/dist/timedependentparty-" + ver + ".tar.gz"],
     py_modules=['party']
 )
Index: /exampleparties/hardliner/requirements.txt
===================================================================
--- /exampleparties/hardliner/requirements.txt	(revision 94)
+++ /exampleparties/hardliner/requirements.txt	(revision 95)
@@ -1,3 +1,3 @@
-https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/87/exampleparties/timedependentparty/dist/timedependentparty-1.2.0.tar.gz
+https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/94/exampleparties/timedependentparty/dist/timedependentparty-1.2.1.tar.gz
 mypy
 
Index: /exampleparties/hardliner/setup.py
===================================================================
--- /exampleparties/hardliner/setup.py	(revision 94)
+++ /exampleparties/hardliner/setup.py	(revision 95)
@@ -1,5 +1,5 @@
 from setuptools import setup, find_packages
 
-ver = '1.2.0'
+ver = '1.2.1'
 
 setup(
@@ -11,5 +11,5 @@
     packages=find_packages(exclude=["test", "test.*", "test.*.*"]),
     package_data={ 'hardliner': ['py.typed']    },
-    install_requires=[ "timedependentparty@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/87/exampleparties/timedependentparty/dist/timedependentparty-" + ver + ".tar.gz"],
+    install_requires=[ "timedependentparty@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/94/exampleparties/timedependentparty/dist/timedependentparty-" + ver + ".tar.gz"],
     py_modules=['party']
 )
Index: /exampleparties/linear/requirements.txt
===================================================================
--- /exampleparties/linear/requirements.txt	(revision 94)
+++ /exampleparties/linear/requirements.txt	(revision 95)
@@ -1,3 +1,3 @@
-https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/87/exampleparties/timedependentparty/dist/timedependentparty-1.2.0.tar.gz
+https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/94/exampleparties/timedependentparty/dist/timedependentparty-1.2.1.tar.gz
 mypy
 
Index: /exampleparties/linear/setup.py
===================================================================
--- /exampleparties/linear/setup.py	(revision 94)
+++ /exampleparties/linear/setup.py	(revision 95)
@@ -1,5 +1,5 @@
 from setuptools import setup, find_packages
 
-ver = '1.2.0'
+ver = '1.2.1'
 
 setup(
@@ -11,5 +11,5 @@
     packages=find_packages(exclude=["test", "test.*", "test.*.*"]),
     package_data={ 'linear': ['py.typed']    },
-    install_requires=[ "timedependentparty@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/87/exampleparties/timedependentparty/dist/timedependentparty-" + ver + ".tar.gz"],
+    install_requires=[ "timedependentparty@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/94/exampleparties/timedependentparty/dist/timedependentparty-" + ver + ".tar.gz"],
     py_modules=['party']
 )
Index: /exampleparties/randomparty/requirements.txt
===================================================================
--- /exampleparties/randomparty/requirements.txt	(revision 94)
+++ /exampleparties/randomparty/requirements.txt	(revision 95)
@@ -1,3 +1,3 @@
-https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/86/geniuswebcore/dist/geniusweb-1.2.0.tar.gz
+https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/93/geniuswebcore/dist/geniusweb-1.2.1.tar.gz
 mypy
 
Index: /exampleparties/randomparty/setup.py
===================================================================
--- /exampleparties/randomparty/setup.py	(revision 94)
+++ /exampleparties/randomparty/setup.py	(revision 95)
@@ -1,5 +1,5 @@
 from setuptools import setup
 
-ver = '1.2.0'
+ver = '1.2.1'
 
 setup(
@@ -10,5 +10,5 @@
     author='W.Pasman',
     packages=['randomparty'],
-    install_requires=[ "geniusweb@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/86/geniuswebcore/dist/geniusweb-1.2.0.tar.gz"],
+    install_requires=[ "geniusweb@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/93/geniuswebcore/dist/geniusweb-1.2.1.tar.gz"],
     py_modules=['party']
 )
Index: /exampleparties/stupidparty/requirements.txt
===================================================================
--- /exampleparties/stupidparty/requirements.txt	(revision 94)
+++ /exampleparties/stupidparty/requirements.txt	(revision 95)
@@ -1,3 +1,3 @@
-https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/86/geniuswebcore/dist/geniusweb-1.2.0.tar.gz
+https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/93/geniuswebcore/dist/geniusweb-1.2.1.tar.gz
 mypy
 
Index: /exampleparties/stupidparty/setup.py
===================================================================
--- /exampleparties/stupidparty/setup.py	(revision 94)
+++ /exampleparties/stupidparty/setup.py	(revision 95)
@@ -1,5 +1,5 @@
 from setuptools import setup
 
-ver = '1.2.0'
+ver = '1.2.1'
 
 setup(
@@ -10,9 +10,10 @@
     author='W.Pasman',
     packages=['stupidparty'],
-    install_requires=[ "geniusweb@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/86/geniuswebcore/dist/geniusweb-1.2.0.tar.gz"],
+    install_requires=[ "geniusweb@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/93/geniuswebcore/dist/geniusweb-1.2.1.tar.gz"],
     py_modules=['party'],
 
     classifiers=[
         'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.9',
     ],
 )
Index: /exampleparties/timedependentparty/requirements.txt
===================================================================
--- /exampleparties/timedependentparty/requirements.txt	(revision 94)
+++ /exampleparties/timedependentparty/requirements.txt	(revision 95)
@@ -1,3 +1,3 @@
-https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/86/geniuswebcore/dist/geniusweb-1.2.0.tar.gz
+https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/93/geniuswebcore/dist/geniusweb-1.2.1.tar.gz
 mypy
 
Index: /exampleparties/timedependentparty/setup.py
===================================================================
--- /exampleparties/timedependentparty/setup.py	(revision 94)
+++ /exampleparties/timedependentparty/setup.py	(revision 95)
@@ -1,5 +1,5 @@
 from setuptools import setup, find_packages
 
-ver = '1.2.0'
+ver = '1.2.1'
 
 setup(
@@ -11,5 +11,5 @@
     packages=find_packages(exclude=["test", "test.*", "test.*.*"]),
     package_data={ 'timedependentparty': ['py.typed']    },
-    install_requires=[ "geniusweb@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/86/geniuswebcore/dist/geniusweb-" + ver + ".tar.gz"],
+    install_requires=[ "geniusweb@https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPython/export/93/geniuswebcore/dist/geniusweb-" + ver + ".tar.gz"],
     py_modules=['party']
 )
Index: /geniuswebcore/geniusweb/progress/ProgressRounds.py
===================================================================
--- /geniuswebcore/geniusweb/progress/ProgressRounds.py	(revision 94)
+++ /geniuswebcore/geniusweb/progress/ProgressRounds.py	(revision 95)
@@ -74,5 +74,5 @@
 			currentTimeMs > int(1000 * datetime.timestamp(self._endtime))
 
-	def advance(self) -> "ProgressRounds" :
+	def advance(self) -> "ProgressRounds":
 		'''
 		@return new ProgressRounds with round 1 advanced (or this, if
@@ -95,4 +95,3 @@
 	def __repr__(self):
 		return "ProgressRounds[" + str(self._currentRound) \
-			+" of " + str(self._duration) \
-			+"," + str(datetime.timestamp(self._endtime)) + "]"
+			+" of " + str(self._duration) + "]"
Index: /geniuswebcore/geniusweb/progress/ProgressTime.py
===================================================================
--- /geniuswebcore/geniusweb/progress/ProgressTime.py	(revision 94)
+++ /geniuswebcore/geniusweb/progress/ProgressTime.py	(revision 95)
@@ -4,5 +4,5 @@
 
 
-class ProgressTime (Progress) :
+class ProgressTime (Progress):
 	'''
 	keeps track of progress towards the Deadline and the time/rounds left.
@@ -11,5 +11,5 @@
 	'''
 
-	def __init__(self, duration: int, start:datetime) :
+	def __init__(self, duration: int, start:datetime):
 		'''
 		@param d     the duration, eg {@link DeadlineTime#getDuration()}. Must be
@@ -24,5 +24,5 @@
 
 	def get(self, currentTimeMs:int) -> float:
-		delta = currentTimeMs - datetime.timestamp(self._start);
+		delta = currentTimeMs - 1000 * datetime.timestamp(self._start);
 		# double should have ~53 digits of precision, and we're computing
 		# deltas here.
@@ -37,5 +37,5 @@
 
 	def isPastDeadline(self, currentTimeMs:int) -> bool:
-		return currentTimeMs > int(datetime.timestamp(self._start)*1000) + self._duration;
+		return currentTimeMs > int(datetime.timestamp(self._start) * 1000) + self._duration;
 
 	def getStart(self) -> datetime:
@@ -47,6 +47,5 @@
 		return self._start;
 
-
-	def getDuration(self) -> int :
+	def getDuration(self) -> int:
 		'''
 		@return duration in milliseconds.
@@ -55,9 +54,8 @@
 
 	def getTerminationTime(self) -> datetime:
-		return datetime.fromtimestamp( (int(datetime.timestamp(self._start)*1000) + self._duration)/1000.);
-	
+		return datetime.fromtimestamp((int(datetime.timestamp(self._start) * 1000) + self._duration) / 1000.);
 
 	def __repr__(self):
-		return "ProgressTime[" + str(datetime.timestamp(self._start))+ " , " + str(self._duration) + "ms]"
+		return "ProgressTime[" + str(int(1000 * datetime.timestamp(self._start))) + " , " + str(self._duration) + "ms]"
 
 	def __hash__(self):
Index: /geniuswebcore/geniusweb/protocol/NegoState.py
===================================================================
--- /geniuswebcore/geniusweb/protocol/NegoState.py	(revision 94)
+++ /geniuswebcore/geniusweb/protocol/NegoState.py	(revision 95)
@@ -1,3 +1,4 @@
 from abc import ABC
+from typing import List
 
 from pyson.JsonSubTypes import JsonSubTypes
@@ -6,4 +7,5 @@
 
 from geniusweb.protocol.NegoSettings import NegoSettings
+from geniusweb.protocol.session.SessionResult import SessionResult
 
 
@@ -20,4 +22,5 @@
 	record the final result as well
 	'''
+
 	def getSettings(self) -> NegoSettings: 
 		'''
@@ -25,5 +28,5 @@
 		'''
 
-	def isFinal(self, currentTimeMsLint)-> bool :
+	def isFinal(self, currentTimeMsLint) -> bool:
 		'''
 		@param currentTimeMs the current time in ms since 1970, see
@@ -34,2 +37,10 @@
 		        changes can occur, including no more protocol errors.
 		'''
+
+	def getResults(self) -> List[SessionResult]:
+		'''
+		@return List of the{@link SessionResult}s. Each SessionResult is a short
+		        report of the final outcome. Assumes {@link #isFinal(long)}.
+		        result may be undefined if not.
+		'''
+	
Index: /geniuswebcore/geniusweb/protocol/session/SessionResult.py
===================================================================
--- /geniuswebcore/geniusweb/protocol/session/SessionResult.py	(revision 94)
+++ /geniuswebcore/geniusweb/protocol/session/SessionResult.py	(revision 95)
@@ -12,5 +12,6 @@
 	all the details, only the final outcome and other global notes.
 	'''
-	def __init__(self, participants : Dict[PartyId, PartyWithProfile],
+
+	def __init__(self, participants: Dict[PartyId, PartyWithProfile],
 			agreements:Agreements , penalties:Dict[PartyId, float] ,
 			error:Optional[Exception]):
@@ -19,6 +20,10 @@
 		                    be null. Some of them may have entered later of left
 		                    early. This list should contain them all.
-		@param agreements   the final Agreements.
-		@param penalties    the penalties in [0,1] for each participant.
+		@param agreements   the final Agreements. The key is the party ID, value
+		                    is the bid that the party agreed on. Only parties
+		                    that reached an agreement are in this list.
+		@param penalties    Key is the party ID, the value is the penalties in
+		                    [0,1] for that participant. A penalty only applies to
+		                    an agreement, not to a reservation bid.
 		@param error        a fatal error that terminated the session. Non-fatal
 		                    errors (warnings) are not to be reported. Null if no
@@ -34,5 +39,5 @@
 		self._error = error
 
-	def getParticipants(self) ->Dict[PartyId, PartyWithProfile] :
+	def getParticipants(self) -> Dict[PartyId, PartyWithProfile]:
 		'''
 		@return the map with for each {@link PartyId} the
@@ -43,5 +48,5 @@
 		return dict(self._participants)
 
-	def getAgreements(self) -> Agreements :
+	def getAgreements(self) -> Agreements:
 		'''
 		@return the final {@link Agreements} of the session. May be empty, not
@@ -50,5 +55,5 @@
 		return self._agreements;
 
-	def getPenalties(self ) -> Dict[PartyId, float] :
+	def getPenalties(self) -> Dict[PartyId, float]:
 		'''
 	 	@return Map of penalties,
@@ -56,5 +61,5 @@
 		return dict(self._penalties)
 
-	def getError(self) -> Optional[Exception] :
+	def getError(self) -> Optional[Exception]:
 		'''
 		@return a fatal error that terminated the session. Non-fatal errors
@@ -67,10 +72,10 @@
 		return self._error
 
-	def __repr__(self)->str:
+	def __repr__(self) -> str:
 		return "SessionResult[" + toStr(self._participants) + "," + str(self._agreements) + ","\
-				+ toStr(self._penalties) + "," + str(self._error) + "]"
+				+toStr(self._penalties) + "," + str(self._error) + "]"
 	
 	def __hash__(self):
-		return hash(( toTuple(self._participants ), toTuple(self._penalties ), self._agreements))
+		return hash((toTuple(self._participants), toTuple(self._penalties), self._agreements))
 
 	def __eq__(self, other):
Index: /geniuswebcore/geniusweb/protocol/session/SessionState.py
===================================================================
--- /geniuswebcore/geniusweb/protocol/session/SessionState.py	(revision 94)
+++ /geniuswebcore/geniusweb/protocol/session/SessionState.py	(revision 95)
@@ -1,5 +1,4 @@
 from abc import abstractmethod
 from typing import List, Optional
-
 
 from geniusweb.actions.Action import Action
@@ -28,6 +27,7 @@
 	the protocol. As uaual, mark non-serializable fields as transient.
 	'''
+
 	@abstractmethod
-	def getSettings(self)->SessionSettings:
+	def getSettings(self) -> SessionSettings:
 		'''
 		@return the SessionSettings
@@ -35,5 +35,5 @@
 	
 	@abstractmethod
-	def getActions(self)->List[Action]: 
+	def getActions(self) -> List[Action]: 
 		'''
 		@return unmodifyable list of actions done so far, in the order in which
@@ -52,5 +52,5 @@
 	
 	@abstractmethod
-	def getAgreements(self)->Agreements :
+	def getAgreements(self) -> Agreements:
 		'''
 		@return the current standing agreements
@@ -58,9 +58,2 @@
 		'''
 	
-	@abstractmethod
-	def getResult(self)->Optional[SessionResult]:
-		'''
-		@return the {@link SessionResult} which is a short report of the final
-		        outcome. Assumes {@link #isFinal(long)}. result may be undefined
-		        if not.
-		'''
Index: /geniuswebcore/geniusweb/protocol/session/learn/LearnState.py
===================================================================
--- /geniuswebcore/geniusweb/protocol/session/learn/LearnState.py	(revision 94)
+++ /geniuswebcore/geniusweb/protocol/session/learn/LearnState.py	(revision 95)
@@ -12,4 +12,5 @@
 from geniusweb.references.PartyWithProfile import PartyWithProfile
 
+
 class LearnState (DefaultSessionState["LearnState", "LearnSettings"]):
 	'''
@@ -19,6 +20,6 @@
 	def __init__(self, actions:List[Action] , conns:List[PartyId] ,
 			progress:Optional[Progress] , settings:LearnSettings ,
-			partyprofiles:Dict[PartyId, PartyWithProfile]={}, 
-			e:Optional[ProtocolException]=None ):
+			partyprofiles:Dict[PartyId, PartyWithProfile]={},
+			e:Optional[ProtocolException]=None):
 		'''
 		@param actions       the actions done by the parties
@@ -50,24 +51,24 @@
 
 	def With(self, actions:List[Action] , conns:List[PartyId],
-			progr:Optional[Progress] ,  settings:LearnSettings, 
-			partyprofiles:Dict[PartyId, PartyWithProfile] , 
-			e:Optional[ProtocolException] ) -> "LearnState" :
+			progr:Optional[Progress] , settings:LearnSettings,
+			partyprofiles:Dict[PartyId, PartyWithProfile] ,
+			e:Optional[ProtocolException]) -> "LearnState":
 		return LearnState(actions, conns, progr, settings, partyprofiles,
 				e)
 
-	def getAgreements(self)->Agreements :
+	def getAgreements(self) -> Agreements:
 		return Agreements()
 
-	def getResult(self)->SessionResult :
-		return SessionResult(self.getPartyProfiles(), self.getAgreements(),
-				{}, None)
+	def getResults(self) -> List[SessionResult]:
+		return [SessionResult(self.getPartyProfiles(), self.getAgreements(),
+				{}, None)]
 
-	def isFinal(self, currentTimeMs:int) ->bool:
+	def isFinal(self, currentTimeMs:int) -> bool:
 		done = self._getDoneLearning()
 		# if done is empty we're probably still starting up.
 		return super().isFinal(currentTimeMs)\
-				or (len(done)!=0 and set(self.getConnections()).issubset(done))
+				or (len(done) != 0 and set(self.getConnections()).issubset(done))
 
-	def WithException(self, e:ProtocolException) ->"LearnState" :
+	def WithException(self, e:ProtocolException) -> "LearnState":
 		'''
 		@param e the error that occured
@@ -77,5 +78,5 @@
 				self.getSettings(), self.getPartyProfiles(), e)
 
-	def checkAction(self, actor:PartyId , action:Action ) ->Optional[str]:
+	def checkAction(self, actor:PartyId , action:Action) -> Optional[str]:
 		'''
 		@param actor  the known real actor that did this action
@@ -92,5 +93,5 @@
 		return None
 
-	def _getDoneLearning(self)->Set[PartyId] :
+	def _getDoneLearning(self) -> Set[PartyId]:
 		'''
 		@return set of parties that reported they are done with learning.
Index: /geniuswebcore/geniusweb/protocol/session/mopac/MOPACState.py
===================================================================
--- /geniuswebcore/geniusweb/protocol/session/mopac/MOPACState.py	(revision 94)
+++ /geniuswebcore/geniusweb/protocol/session/mopac/MOPACState.py	(revision 95)
@@ -56,5 +56,5 @@
 			progress:Optional[Progress] ,
 			settings:MOPACSettings,
-			partyprofiles:Dict[PartyId, PartyWithProfile] ): 
+			partyprofiles:Dict[PartyId, PartyWithProfile]): 
 		'''
 		@param phase         The Phase, or null if still initializing. Phase is
@@ -86,5 +86,5 @@
 		self._partyprofiles = partyprofiles
 
-	def initPhase(self,newprogress:Progress , now:int) -> "MOPACState" :
+	def initPhase(self, newprogress:Progress , now:int) -> "MOPACState":
 		'''
 		Sets the progress for this session and initial phase. Must be called
@@ -109,29 +109,29 @@
 				self._partyprofiles)
 
-	def getActions(self)->List[Action]:
+	def getActions(self) -> List[Action]:
 		return list(self._actions)
 
-	def getProgress(self)->Optional[Progress] :
+	def getProgress(self) -> Optional[Progress]:
 		return self._progress
 
-	def getAgreements(self)-> Agreements: 
+	def getAgreements(self) -> Agreements: 
 		return val(self._phase).getPartyStates().getAgreements();
 
-	def getSettings(self)->MOPACSettings :
+	def getSettings(self) -> MOPACSettings:
 		return self._settings
 
-	def getPartyProfiles(self)->Dict[PartyId, PartyWithProfile]:
+	def getPartyProfiles(self) -> Dict[PartyId, PartyWithProfile]:
 		return dict(self._partyprofiles)
 
-	def  isFinal(self, now:int)->bool:
+	def  isFinal(self, now:int) -> bool:
 		return self._phase != None and val(self._phase).isFinal(now) \
 			and not self.isNewPhasePossible(now)
 
-	def getResult(self)->SessionResult :
-		return SessionResult(self._partyprofiles, self.getAgreements(),
-				{}, None)
+	def getResults(self) -> List[SessionResult]:
+		return [SessionResult(self._partyprofiles, self.getAgreements(),
+				{}, None)]
 
 	@staticmethod
-	def  _getAvailablePhaseTime(aprogress:Progress , now:int) ->int: 
+	def  _getAvailablePhaseTime(aprogress:Progress , now:int) -> int: 
 		'''
 		@param progress the Progress that needs to be checked
@@ -140,8 +140,8 @@
 		        progress.
 		'''
-		return min(int(aprogress.getTerminationTime().timestamp()*1000) - now,
+		return min(int(aprogress.getTerminationTime().timestamp() * 1000) - now,
 				PHASE_MAXTIME)
 
-	def With(self, id:PartyId , partyprofile:PartyWithProfile ) -> "MOPACState" :
+	def With(self, id:PartyId , partyprofile:PartyWithProfile) -> "MOPACState":
 		'''
 		@param id           the new {@link PartyId}
@@ -157,8 +157,8 @@
 
 		newprofiles = dict(self._partyprofiles)
-		newprofiles[id]= partyprofile
-		return MOPACState(None, self._actions,self._progress, self._settings, newprofiles)
+		newprofiles[id] = partyprofile
+		return MOPACState(None, self._actions, self._progress, self._settings, newprofiles)
 	
-	def WithException(self, e:ProtocolException) ->"MOPACState" :
+	def WithException(self, e:ProtocolException) -> "MOPACState":
 		'''
 		@param e the {@link ProtocolException} that occured
@@ -166,8 +166,8 @@
 		        {@link #initPhase(Progress, long)} before using this
 		'''
-		return MOPACState(val(self._phase).WithException(e), self._actions, self._progress, 
-						self._settings,self._partyprofiles)
-
-	def nextPhase(self, now:int)->"MOPACState" :
+		return MOPACState(val(self._phase).WithException(e), self._actions, self._progress,
+						self._settings, self._partyprofiles)
+
+	def nextPhase(self, now:int) -> "MOPACState":
 		'''
 		Start the next phase. If new phase is OfferPhase, we increase progress.
@@ -178,12 +178,12 @@
 		@return new {@link MOPACState} with phase initialized for next phase.
 		'''
-		remainingNegoTime = int(val(self._progress).getTerminationTime().timestamp()*1000) - now
+		remainingNegoTime = int(val(self._progress).getTerminationTime().timestamp() * 1000) - now
 		newphase = val(self._phase).next(now, min(remainingNegoTime, PHASE_MAXTIME))
 
-		return MOPACState(newphase, self._actions, 
+		return MOPACState(newphase, self._actions,
 					MOPACState._increment(val(self._progress), val(self._phase)),
 				self.getSettings(), self._partyprofiles)
 
-	def isNewPhasePossible(self, now:int)->bool:
+	def isNewPhasePossible(self, now:int) -> bool:
 		'''
 		When this is called, all parties should have acted.
@@ -202,8 +202,8 @@
 						now) > PHASE_MINTIME
 
-	def getPhase(self)->Phase :
+	def getPhase(self) -> Phase:
 		return val(self._phase)
 
-	def WithAction(self, actor:PartyId,  action:Action, now:int)->"MOPACState" :
+	def WithAction(self, actor:PartyId, action:Action, now:int) -> "MOPACState":
 		'''
 		Check if action is allowed. Add action to the list of actions. Notice,
@@ -225,5 +225,5 @@
 
 	@staticmethod
-	def _increment(aprogress:Progress , aphase:Phase ) ->Progress :
+	def _increment(aprogress:Progress , aphase:Phase) -> Progress:
 		'''
 		@param aprogress the progress that might need to be advanced
@@ -232,14 +232,14 @@
 		        {@link OptIn}.
 		'''
-		if isinstance(aprogress,ProgressRounds) and isinstance(aphase,OptInPhase):
+		if isinstance(aprogress, ProgressRounds) and isinstance(aphase, OptInPhase):
 			return aprogress.advance()
 		return aprogress
 
-	def _getPowers(self)-> Dict[PartyId, int]:
+	def _getPowers(self) -> Dict[PartyId, int]:
 		'''
 		@return the power of all parties as set in their parameters, default =1.
 		    bad power values (non-integer, or <1) are ignored.
 		'''
-		map:Dict[PartyId,int] = {}
+		map:Dict[PartyId, int] = {}
 		for pid in self._partyprofiles:
 			if self._partyprofiles[pid].getParty().getParameters().containsKey("power"):
@@ -249,13 +249,13 @@
 					power = 1
 			else:
-				power=1
-			map[pid]=power
+				power = 1
+			map[pid] = power
 		return map
 
-	def __repr__(self)->str:
+	def __repr__(self) -> str:
 		return "MOPACState[" + str(self._phase) + "," + str(self._settings)\
-			+ "," + str(self._partyprofiles) + "," + str(self._progress) + "]"
-
-	def finishPhase(self)->"MOPACState" :
+			+"," + str(self._partyprofiles) + "," + str(self._progress) + "]"
+
+	def finishPhase(self) -> "MOPACState":
 		'''
 		@return a wrapped-up state, with all parties doen an action or kicked,
@@ -264,18 +264,18 @@
 		newphase = val(self._phase).finish()
 		newactions = list(self._actions)
-		newactions=newactions+ newphase.getPartyStates().getActions()
+		newactions = newactions + newphase.getPartyStates().getActions()
 		return MOPACState(newphase, newactions, self._progress, self._settings,
 				self._partyprofiles)
 
 	def __hash__(self):
-		return hash((tuple(self._actions), toTuple(self._partyprofiles), 
+		return hash((tuple(self._actions), toTuple(self._partyprofiles),
 					self._phase, self._progress, self._settings))
 	
 	def __eq__(self, other):
 		return isinstance(other, self.__class__) \
-			 and self._actions==other._actions\
-			 and self._partyprofiles==other._partyprofiles\
-			 and self._phase==other._phase\
-			 and self._progress==other._progress\
-			 and self._settings==other._settings
-
+			 and self._actions == other._actions\
+			 and self._partyprofiles == other._partyprofiles\
+			 and self._phase == other._phase\
+			 and self._progress == other._progress\
+			 and self._settings == other._settings
+
Index: /geniuswebcore/geniusweb/protocol/session/saop/SAOPState.py
===================================================================
--- /geniuswebcore/geniusweb/protocol/session/saop/SAOPState.py	(revision 94)
+++ /geniuswebcore/geniusweb/protocol/session/saop/SAOPState.py	(revision 95)
@@ -18,5 +18,6 @@
 from geniusweb.references.PartyWithProfile import PartyWithProfile
 
-class SAOPState (DefaultSessionState[ "SAOPState","SAOPSettings"]):  
+
+class SAOPState (DefaultSessionState[ "SAOPState", "SAOPSettings"]): 
 	'''
 	Immutable.
@@ -25,6 +26,6 @@
 	def __init__(self, actions:List[Action] , connections:List[PartyId] ,
 			progress:Optional[Progress] , settings: SAOPSettings ,
-			partyprofiles:Dict[PartyId, PartyWithProfile]={}, 
-			error:Optional[ProtocolException]=None) :
+			partyprofiles:Dict[PartyId, PartyWithProfile]={},
+			error:Optional[ProtocolException]=None):
 		'''
 		
@@ -45,12 +46,11 @@
 	def With(self, actions:List[Action] , conns: List[PartyId],
 			progr:Optional[Progress] , settings: SAOPSettings,
-			partyprofiles:Dict[PartyId, PartyWithProfile] ,  e:Optional[ProtocolException]) -> "SAOPState" :
+			partyprofiles:Dict[PartyId, PartyWithProfile] , e:Optional[ProtocolException]) -> "SAOPState":
 		assert isinstance(actions, list)
-		#assert isinstance(settings, SAOPSettings)
+		# assert isinstance(settings, SAOPSettings)
 		assert isinstance(partyprofiles, dict)
 		return SAOPState(actions, conns, progr, settings, partyprofiles, e);
 
-
-	def getAgreements(self) ->Agreements :
+	def getAgreements(self) -> Agreements:
 		agree = Agreements()
 		acts = self.getActions()
@@ -64,11 +64,11 @@
 
 		# check that the last n-1 are accepts.
-		allaccept = all( [ (isinstance(act, Accept) and bid==act.getBid())\
-					for act in acts[-(nparticipants-1):]] )
+		allaccept = all([ (isinstance(act, Accept) and bid == act.getBid())\
+					for act in acts[-(nparticipants - 1):]])
 		if allaccept:
 			agree = Agreements({party: bid for party in  self._getParties()})
 		return agree
 
-	def _getParties(self)->List[PartyId] :
+	def _getParties(self) -> List[PartyId]:
 		'''
 		@return all currently connected parties.
@@ -76,14 +76,14 @@
 		return self.getConnections()
 
-	def isFinal(self,  currentTimeMs:int)->bool:
+	def isFinal(self, currentTimeMs:int) -> bool:
 		acts = self.getActions()
 		return super().isFinal(currentTimeMs) \
-				or self.getAgreements().getMap()!={} \
-				or (not acts==[] and isinstance(acts[- 1], EndNegotiation))
+				or self.getAgreements().getMap() != {} \
+				or (not acts == [] and isinstance(acts[-1], EndNegotiation))
 
-	def WithAction(self, actor:PartyId , action:Action ) -> "SAOPState":
+	def WithAction(self, actor:PartyId , action:Action) -> "SAOPState":
 		return super().WithAction(actor, action).WithProgress(self.advanceProgress())
 
-	def checkAction(self, actor:PartyId, action:Action ) -> Optional[str]:
+	def checkAction(self, actor:PartyId, action:Action) -> Optional[str]:
 		msg = super().checkAction(actor, action)
 		if msg:
@@ -101,7 +101,7 @@
 			if bid != action.getBid():
 				return "Party accepts a bid differing from the last offer ="\
-						+ str(bid) + ", action=" + str(action) + ")"
+						+str(bid) + ", action=" + str(action) + ")"
 			return None
-		elif isinstance(action,Offer):
+		elif isinstance(action, Offer):
 			return None
 		elif isinstance(action, EndNegotiation):
@@ -109,6 +109,5 @@
 		return "Action " + str(action) + " is not allowed in SAOP"
 
-
-	def _getLastBid(self)-> Optional[ Bid ]:
+	def _getLastBid(self) -> Optional[ Bid ]:
 		'''
 		Check up to nparticipants-1 steps back if there was an offer.
@@ -121,10 +120,10 @@
 		while n > len(acts) - nparticipants and n >= 0:
 			action = acts[n]
-			if isinstance(action ,Offer):
+			if isinstance(action , Offer):
 				return action.getBid()
-			n=n-1
+			n = n - 1
 		return None
 
-	def _getNextActor(self)->PartyId :
+	def _getNextActor(self) -> PartyId:
 		'''
 		@return the next actor in the current state. Assumes 1 action per actor
@@ -134,5 +133,5 @@
 		return self.getConnections()[len(self.getActions()) % len(self.getConnections())]
 
-	def advanceProgress(self)-> Progress :
+	def advanceProgress(self) -> Progress:
 		'''
 		@return new progress state
@@ -140,5 +139,5 @@
 		newprogress = self.getProgress()
 		assert newprogress
-		if isinstance(newprogress,ProgressRounds) and self._isLastActor():
+		if isinstance(newprogress, ProgressRounds) and self._isLastActor():
 			newprogress = newprogress.advance()
 		return newprogress
@@ -151,5 +150,5 @@
 		return len(self.getActions()) % nparticipants == nparticipants - 1;
 
-	def getResult(self) -> SessionResult:
-		return SessionResult(self.getPartyProfiles(), self.getAgreements(),
-				{}, self.getError())
+	def getResults(self) -> List[SessionResult]:
+		return [SessionResult(self.getPartyProfiles(), self.getAgreements(),
+				{}, self.getError())]
Index: /geniuswebcore/setup.py
===================================================================
--- /geniuswebcore/setup.py	(revision 94)
+++ /geniuswebcore/setup.py	(revision 95)
@@ -1,5 +1,5 @@
 from setuptools import setup, find_packages
 
-ver = '1.2.0'
+ver = '1.2.1'
 
 setup(
Index: /geniuswebcore/test/geniusweb/inform/OptInTest.py
===================================================================
--- /geniuswebcore/test/geniusweb/inform/OptInTest.py	(revision 94)
+++ /geniuswebcore/test/geniusweb/inform/OptInTest.py	(revision 95)
@@ -1,7 +1,9 @@
 from datetime import datetime
 import json
+from typing import List
 import unittest
 
 from pyson.ObjectMapper import ObjectMapper
+from unitpy.GeneralTests import GeneralTests
 from uri.uri import URI 
 
@@ -13,6 +15,4 @@
 from geniusweb.issuevalue.Bid import Bid
 from geniusweb.issuevalue.DiscreteValue import DiscreteValue
-from unitpy.GeneralTests import GeneralTests
-from typing import List
 
 
@@ -20,14 +20,14 @@
     maxDiff = None
         
-    bid1 =  Bid({"iss":  DiscreteValue("val1")})
-    bid2 =  Bid({"iss":  DiscreteValue("val2")})
-    partyA =  PartyId("partyA")
-    partyB =  PartyId("partyB")
+    bid1 = Bid({"iss": DiscreteValue("val1")})
+    bid2 = Bid({"iss": DiscreteValue("val2")})
+    partyA = PartyId("partyA")
+    partyB = PartyId("partyB")
     voteA1 = Vote(partyA, bid1, 2, 9)
-    voteB1 =  Vote(partyB, bid1, 2, 9)
-    voteB2 =  Vote(partyB, bid2, 2, 9)
+    voteB1 = Vote(partyB, bid1, 2, 9)
+    voteB2 = Vote(partyB, bid2, 2, 9)
 
     votesA = Votes(partyA, set([voteA1]))
-    votesB = Votes(partyB,set([voteB1, voteB2]))
+    votesB = Votes(partyB, set([voteB1, voteB2]))
 
     optIn1 = OptIn([votesA, votesB])
@@ -36,11 +36,11 @@
     jackson = ObjectMapper();
 
-    asJson="{\"OptIn\":{\"votes\":[{\"Votes\":{\"actor\":\"partyA\",\"votes\":[{\"Vote\":{\"actor\":\"partyA\",\"bid\":{\"issuevalues\":{\"iss\":\"val1\"}},\"minPower\":2,\"maxPower\":9}}]}},{\"Votes\":{\"actor\":\"partyB\",\"votes\":[{\"Vote\":{\"actor\":\"partyB\",\"bid\":{\"issuevalues\":{\"iss\":\"val2\"}},\"minPower\":2,\"maxPower\":9}},{\"Vote\":{\"actor\":\"partyB\",\"bid\":{\"issuevalues\":{\"iss\":\"val1\"}},\"minPower\":2,\"maxPower\":9}}]}}]}}"
+    asJson = "{\"OptIn\":{\"votes\":[{\"Votes\":{\"actor\":\"partyA\",\"votes\":[{\"Vote\":{\"actor\":\"partyA\",\"bid\":{\"issuevalues\":{\"iss\":\"val1\"}},\"minPower\":2,\"maxPower\":9}}]}},{\"Votes\":{\"actor\":\"partyB\",\"votes\":[{\"Vote\":{\"actor\":\"partyB\",\"bid\":{\"issuevalues\":{\"iss\":\"val2\"}},\"minPower\":2,\"maxPower\":9}},{\"Vote\":{\"actor\":\"partyB\",\"bid\":{\"issuevalues\":{\"iss\":\"val1\"}},\"minPower\":2,\"maxPower\":9}}]}}]}}"
 
-    def getGeneralTestData(self) -> List[List[OptIn]] :
+    def getGeneralTestData(self) -> List[List[OptIn]]:
         return [[self.optIn1, self.optIn1a], [self.optIn2]]
         
     def getGeneralTestStrings(self) -> List[str]:
-        return ["OptIn.*Votes.*Vote.*partyA.*iss.*val1.*2.*Votes.*Vote.*partyB.*iss.*val2.*.*2.*Vote.*partyB.*iss.*val1.*2.*", 
+        return ["OptIn.*Votes.*Vote.*partyA.*iss.*val1.*2.*Votes.*Vote.*partyB.*iss.*val.*.*2.*Vote.*partyB.*iss.*val.*2.*",
                 "OptIn.*Votes.*Vote.*partyA.*iss.*val.*2.*"]
 
Index: /geniuswebcore/test/geniusweb/progress/ProgressRoundsTest.py
===================================================================
--- /geniuswebcore/test/geniusweb/progress/ProgressRoundsTest.py	(revision 94)
+++ /geniuswebcore/test/geniusweb/progress/ProgressRoundsTest.py	(revision 95)
@@ -1,65 +1,107 @@
-
 from datetime import datetime
+import json
+from typing import List
 import unittest
+from unittest.mock import Mock
 
 from pyson.ObjectMapper import ObjectMapper
+from unitpy.GeneralTests import GeneralTests
 
+from geniusweb import progress
+from geniusweb.deadline.DeadlineRounds import DeadlineRounds
 from geniusweb.progress.Progress import Progress
 from geniusweb.progress.ProgressRounds import ProgressRounds
+from geniusweb.progress.ProgressTime import ProgressTime
 
 
-class ProgressRoundsTest(unittest.TestCase):
-    pyson=ObjectMapper()
-    progress = ProgressRounds(12, 0, datetime.fromtimestamp(100000.9))
-    progress1 = ProgressRounds(12, 0, datetime.fromtimestamp(100000.9))
-    progress2 = ProgressRounds(11, 0, datetime.fromtimestamp(100000.9))
-    progresjson = {"ProgressRounds":{"duration":12,"currentRound":0,"endtime":100000900}}
-    final=ProgressRounds(12,12, datetime.fromtimestamp(100000.9));
+class ProgressRoundsTest(unittest.TestCase, GeneralTests[Progress]): 
+    deadline = Mock(DeadlineRounds)
+    TESTROUNDS = 10
+    TESTTIME = 10  # milliseconds!
 
-    def testDeserialization(self):
-        self.assertEqual(self.progress, self.pyson.parse(self.progresjson, Progress))
+    date = datetime.fromtimestamp(98765 / 1000)
+    date0 = datetime.fromtimestamp(100000 / 1000)
 
-    def testSerialization(self):
-        print(self.pyson.toJson(self.progress))
-        self.assertEqual(self.progresjson, self.pyson.toJson(self.progress))
-    
-    def testRepr(self):
-        self.assertEqual("ProgressRounds[0 of 12,100000.9]", repr(self.progress))
-        
-    def testEqual(self):
-        self.assertEqual(self.progress, self.progress1)
-        self.assertNotEqual(self.progress, self.progress2)
+    deadline1 = ProgressRounds(20, 0, date)
+    deadline2 = ProgressRounds(20, 0, date)
+    deadline3 = ProgressRounds(30, 0, date)
+    deadline4 = ProgressTime(20, date0)
+    deadline5 = ProgressTime(20, date)
+    jackson = ObjectMapper()
+    progressstring = "{\"ProgressRounds\":{\"duration\":10,\"currentRound\":0,\"endtime\":98765}}"
 
-        self.assertEqual(hash(self.progress), hash(self.progress1))
-        self.assertNotEqual(hash(self.progress), hash(self.progress2))
-        
-    def testDuration(self):
-        self.assertEqual(12, self.progress.getDuration())
-        
+    start = 1234
+    end = datetime.fromtimestamp((start + TESTTIME) / 1000)
+    pr = ProgressRounds(TESTROUNDS, 4, end)
+
+    def setUp(self):
+        self.deadline.getRounds = Mock(return_value=self.TESTROUNDS)
+        self.progress = ProgressRounds(self.deadline.getRounds(), 0, self.date)
+
+    def getGeneralTestData(self) -> List[List[Progress]]:
+        return [[self.deadline1, self.deadline2], [self.deadline3], [self.deadline4], [self.deadline5]]
+
+    def getGeneralTestStrings(self) -> List[str]:
+        return ["ProgressRounds\\[0.*20\\]",
+                "ProgressRounds\\[0.*30\\]", "ProgressTime\\[100000.*20ms\\]",
+                "ProgressTime\\[98765.*20ms\\]"]
+
+    def testInit(self):
+        self.assertEqual(0, self.progress.get(1))
+
+    def testCurrentRound(self):
+        self.assertEqual(0, self.progress.getCurrentRound())
+
     def testAdvance(self):
-        prog=self.progress.advance()
-        self.assertEqual(1, prog.getCurrentRound())
-        
-    def testAdvanceFinal(self):
-        self.final.advance()
-        
-    def testTerminationTime(self):
-        self.assertEqual(datetime.fromtimestamp(100000.9), self.progress.getTerminationTime())
-        
-    def testIsPastDeadline(self):
-        self.assertFalse(self.progress.isPastDeadline(500))        
-        self.assertFalse(self.progress.isPastDeadline(100000900))
-        self.assertTrue(self.progress.isPastDeadline(100000901))
-        
-        self.assertTrue(self.final.isPastDeadline(100))
-        
-    def testGet(self):
-        self.assertEqual(0, self.progress.get(100000900-1))
-        # get ignores the time deadline. 
-        self.assertEqual(0, self.progress.get(100000901))
-        
-        #check final if all rounds completed.
-        self.assertEqual(1, self.final.get(100000900-1))
-        
-        halfway = ProgressRounds(12,6, datetime.fromtimestamp(100000900));
-        self.assertEqual(0.5, halfway.get(100000900-1) )
+        p = self.progress
+        n = 0;
+        while n <= self.TESTROUNDS:
+            self.assertEqual((float(n) / self.TESTROUNDS), p.get(1))
+            n = n + 1
+            p = p.advance()
+
+    def testAdvanceWhenDeadlineReached(self):
+        p = ProgressRounds(self.TESTROUNDS, 9, self.date)
+        p = p.advance()
+        self.assertEqual(1, p.get(1))
+
+    def testSerialize(self):
+        print(json.dumps(self.jackson.toJson(self.progress)))
+        self.assertEqual(json.loads(self.progressstring), self.jackson.toJson(self.progress))
+
+    def testDeserialize(self):
+        self.assertEqual(self.progress,
+                self.jackson.parse(json.loads(self.progressstring), Progress))
+
+    def testIllegalArg1(self):
+        self.assertRaises(ValueError, lambda:ProgressRounds(10, 11, self.date))
+
+    def testIllegalArg2(self):
+        self.assertRaises(ValueError, lambda:ProgressRounds(10, -1, self.date))
+
+    def pastDeadlinetest(self):
+        self.assertFalse(self.pr.isPastDeadline(self.start));
+        self.assertTrue(self.pr.isPastDeadline(self.start + self.TESTTIME + 1))
+        self.assertFalse(self.pr.isPastDeadline(self.start + self.TESTTIME - 1))
+        self.assertTrue(ProgressRounds(self.TESTROUNDS, self.TESTROUNDS, self.end)
+                .isPastDeadline(self.start + self.TESTTIME + 1))
+
+    def terminationTimetest(self):
+        self.assertEquals(self.start + self.TESTTIME, self.pr.getTerminationTime().getTime())
+
+    def gettest(self):
+        # time does not matter for rounds-based deadline
+        self.assertEqual(0.4, self.pr.get(self.start));
+        self.assertEqual(0.4, self.pr.get(self.start + self.TESTTIME + 1))
+        self.assertEqual(0.4, self.pr.get(self.start + self.TESTTIME))
+        self.assertEqual(0.4, self.pr.get(self.start + self.TESTTIME / 2))
+        self.assertEqual(0.4, self.pr.get(self.start - 10))
+        self.assertEqual(0.4, self.pr.get(self.start + self.TESTTIME + 10))
+
+        self.assertEqual(0.1,
+                ProgressRounds(self.TESTROUNDS, 1, self.end).get(self.start))
+        self.assertEqual(1,
+                ProgressRounds(self.TESTROUNDS, self.TESTROUNDS, self.end).get(self.start));
+
+    def totalRoundstest(self):
+        self.assertEqual(10, self.pr.getTotalRounds())
Index: /geniuswebcore/test/geniusweb/progress/ProgressTimeTest.py
===================================================================
--- /geniuswebcore/test/geniusweb/progress/ProgressTimeTest.py	(revision 94)
+++ /geniuswebcore/test/geniusweb/progress/ProgressTimeTest.py	(revision 95)
@@ -1,48 +1,71 @@
-
 from datetime import datetime
+import json
+from typing import List
 import unittest
 
 from pyson.ObjectMapper import ObjectMapper
+from unitpy.GeneralTests import GeneralTests
 
 from geniusweb.progress.Progress import Progress
-from geniusweb.progress.ProgressRounds import ProgressRounds
 from geniusweb.progress.ProgressTime import ProgressTime
 
 
-class ProgressTimeTest(unittest.TestCase):
-    pyson=ObjectMapper()
-    progress = ProgressTime(1999, datetime.fromtimestamp(100000.9))
-    progress1 = ProgressTime(1999, datetime.fromtimestamp(100000.9))
-    progress2 = ProgressTime(999, datetime.fromtimestamp(100000.9))
-    progress3 = ProgressTime(1999, datetime.fromtimestamp(100001.9))
-    progresjson = {"ProgressTime":{"duration":1999, 'start':100000900}}
+class ProgressTimeTest (unittest.TestCase, GeneralTests[Progress]):
+	# we set T0 quite high because Python has trouble handling small datetime
+	T0 = 100000
+	TIMEZERO = datetime.fromtimestamp(T0 / 1000)
+	TESTTIME = 10  # milliseconds!
+	progress1 = ProgressTime(TESTTIME, TIMEZERO)
+	progress1a = ProgressTime(TESTTIME, TIMEZERO)
+	progress2 = ProgressTime(200, TIMEZERO)
 
-    def testDeserialization(self):
-        self.assertEqual(self.progress, self.pyson.parse(self.progresjson, Progress))
+	jackson = ObjectMapper()
+	progressstring = "{\"ProgressTime\":{\"duration\":10,\"start\":100000}}"
 
-    def testSerialization(self):
-        print(self.pyson.toJson(self.progress))
-        self.assertEqual(self.progresjson, self.pyson.toJson(self.progress))
-    
-    def testRepr(self):
-        self.assertEqual("ProgressTime[100000.9 , 1999ms]", repr(self.progress))
-        
-    def testEqual(self):
-        self.assertEqual(self.progress, self.progress1)
-        self.assertNotEqual(self.progress, self.progress2)
-        self.assertNotEqual(self.progress, self.progress3)
-        self.assertEqual(hash(self.progress), hash(self.progress1))
-        self.assertNotEqual(hash(self.progress), hash(self.progress2))
-        self.assertNotEqual(hash(self.progress), hash(self.progress3))
-        
-    def testPastDeadline(self):
-        self.assertFalse(self.progress.isPastDeadline(100000900+1999))        
-        self.assertTrue(self.progress.isPastDeadline(100000900+1999+1))
-        
-    def testTerminationTime(self):
-        self.assertEqual(datetime.fromtimestamp((100000900+1999)/1000), self.progress.getTerminationTime())
-        
-    def testGet(self):
-        self.assertEqual(0, self.progress.get(100000.9))
-        self.assertEqual(1, self.progress.get(100000.9+1999))
-        self.assertEqual(0.5, self.progress.get(100000.9+1999/2))
+	start = 1234
+	pr = ProgressTime(TESTTIME, datetime.fromtimestamp(start / 1000))
+
+	def getGeneralTestData(self) -> List[List[Progress]]:
+		return [[self.progress1, self.progress1a], [self.progress2]]
+	
+	def getGeneralTestStrings(self) -> List[str]:
+		return ["ProgressTime\\[100000.*10ms\\]",
+				"ProgressTime\\[100000.*200ms\\]"]
+
+	def testInit(self):
+		self.assertEqual(0, self.progress1.get(0))
+
+	def testAdvancingTime(self):
+		for n in range(0, self.TESTTIME):
+			self.assertEqual(float(n) / self.TESTTIME,
+					self.progress1.get(self.T0 + n))
+
+	def testAdvanceWhenDeadlineReached(self):
+		progress = ProgressTime(self.TESTTIME, self.TIMEZERO)
+		self.assertEqual(1, self.progress1.get(self.T0 + self.TESTTIME));
+
+	def testSerialize(self):
+		actual = self.jackson.toJson(self.progress1)
+		print(actual)
+		self.assertEqual(json.loads(self.progressstring), actual)
+
+	def testDeserialize(self):
+		newprog = self.jackson.parse(json.loads(self.progressstring), 	ProgressTime)
+		# we can't directly compare with progress since that's a hacked
+		# object...
+		self.assertEqual(self.TESTTIME, newprog.getDuration())
+		self.assertEqual(self.T0, 1000 * newprog.getStart().timestamp())
+
+	def pastDeadlinetest(self):
+		self.assertTrue(self.pr.isPastDeadline(self.start + self.TESTTIME + 1))
+		self.assertFalse(self.pr.isPastDeadline(self.start + self.TESTTIME - 1))
+
+	def terminationTimetest(self):
+		self.assertEqual(self.start + self.TESTTIME, self.pr.getTerminationTime().getTime())
+
+	def gettest(self):
+		self.assertEqual(0, self.pr.get(self.start))
+		self.assertEqual(1, self.pr.get(self.start + self.TESTTIME));
+		self.assertEqual(0.5, self.pr.get(self.start + self.TESTTIME / 2));
+		self.assertEqual(0, self.pr.get(self.start - 10))
+		self.assertEqual(1, self.pr.get(self.start + self.TESTTIME + 10))
Index: /geniuswebcore/test/geniusweb/protocol/session/learn/LearnStateTest.py
===================================================================
--- /geniuswebcore/test/geniusweb/protocol/session/learn/LearnStateTest.py	(revision 94)
+++ /geniuswebcore/test/geniusweb/protocol/session/learn/LearnStateTest.py	(revision 95)
@@ -25,7 +25,7 @@
 	PARTY2 = "party2"
 	PARTY1 = "party1"
-	party1id =  PartyId(PARTY1)
-	party2id =  PartyId(PARTY2)
-	deadline =  DeadlineTime(10000)
+	party1id = PartyId(PARTY1)
+	party2id = PartyId(PARTY2)
+	deadline = DeadlineTime(10000)
 	partyprofile1 = Mock(PartyWithProfile)
 	partyprofile2 = Mock(PartyWithProfile)
@@ -39,8 +39,7 @@
 		self.params = self.params.With("negotiationdata",
 				["12b5f909-0079-43ac-a8ac-a31794391012"])
-
 		
-		self.progressnotdone.isPastDeadline=Mock(return_value=False)
-		self.progressdone.isPastDeadline=Mock(return_value=True)
+		self.progressnotdone.isPastDeadline = Mock(return_value=False)
+		self.progressdone.isPastDeadline = Mock(return_value=True)
 
 		# define team1
@@ -62,16 +61,15 @@
 		participants = [self.team1, self.team2]
 		settings = LearnSettings(participants, self.deadline)
-		self.state =LearnState([],[],None, settings)
+		self.state = LearnState([], [], None, settings)
 
 		self.connectedstate = self.state.WithParty(self.party1id, self.partyprofile1)\
 				.WithParty(self.party2id, self.partyprofile2).WithProgress(self.progressnotdone)
 
-
 	def testsmokeTestNull(self):
-		self.assertRaises(ValueError, lambda:LearnState([],[],None, None))
+		self.assertRaises(ValueError, lambda:LearnState([], [], None, None))
 
 	def testsmokeTest(self):
 		settings = Mock(LearnSettings)
-		LearnState([],[],None,settings)
+		LearnState([], [], None, settings)
 
 	def testgetAgreementsTest(self):
@@ -97,9 +95,9 @@
 		self.assertNotEqual(None, self.connectedstate.checkAction(self.party1id,
 				EndNegotiation(self.party1id)))
-		self.assertNotEqual(None,self.connectedstate.checkAction(self.party1id,
+		self.assertNotEqual(None, self.connectedstate.checkAction(self.party1id,
 				LearningDone(self.party2id)))
 
 		# check a good action is allowed
-		self.assertEqual(None,self.connectedstate.checkAction(self.party1id,
+		self.assertEqual(None, self.connectedstate.checkAction(self.party1id,
 				LearningDone(self.party1id)))
 
@@ -107,5 +105,5 @@
 		learned = self.connectedstate.WithAction(self.party1id,
 				LearningDone(self.party1id))
-		self.assertNotEqual(None, 
+		self.assertNotEqual(None,
 				learned.checkAction(self.party1id, LearningDone(self.party1id)))
 
@@ -121,4 +119,4 @@
 
 	def testgetResultTest(self):
-		res = self.connectedstate.getResult()
+		res = self.connectedstate.getResults()[0]
 		self.assertEqual(0, len(res.getAgreements().getMap()))
