task handler unit test revamp & bugfix

revamped task handler unit tests

found bug when a False persistent kwarg is passed to the add method. Resolved it.

All evennia unit tests pass. Default run level and run level 2.
This commit is contained in:
davewiththenicehat 2021-04-26 08:59:35 -04:00
parent 84193dd9a7
commit 99568148c6
2 changed files with 197 additions and 146 deletions

View file

@ -67,7 +67,7 @@ class TaskHandlerTask:
d.pause() d.pause()
def unpause(self): def unpause(self):
"""Process all callbacks made since pause() was called.""" """Unpause a task, run the task if it has passed delay time."""
d = self.deferred d = self.deferred
if d: if d:
d.unpause() d.unpause()
@ -328,8 +328,9 @@ class TaskHandler(object):
# record the task to the tasks dictionary # record the task to the tasks dictionary
persistent = kwargs.get("persistent", False) persistent = kwargs.get("persistent", False)
if persistent: if "persistent" in kwargs:
del kwargs["persistent"] del kwargs["persistent"]
if persistent:
safe_args = [] safe_args = []
safe_kwargs = {} safe_kwargs = {}
@ -358,10 +359,10 @@ class TaskHandler(object):
else: else:
safe_kwargs[key] = value safe_kwargs[key] = value
self.tasks[task_id] = (comp_time, callback, safe_args, safe_kwargs, True, None) self.tasks[task_id] = (comp_time, callback, safe_args, safe_kwargs, persistent, None)
self.save() self.save()
else: # this is a non-persitent task else: # this is a non-persitent task
self.tasks[task_id] = (comp_time, callback, args, kwargs, True, None) self.tasks[task_id] = (comp_time, callback, args, kwargs, persistent, None)
# defer the task # defer the task
callback = self.do_task callback = self.do_task

View file

@ -323,163 +323,213 @@ class TestDelay(EvenniaTest):
Test utils.delay. Test utils.delay.
""" """
def test_delay(self): def setUp(self):
super().setUp()
# get a reference of TASK_HANDLER # get a reference of TASK_HANDLER
timedelay = 5 self.timedelay = 5
global _TASK_HANDLER global _TASK_HANDLER
if _TASK_HANDLER is None: if _TASK_HANDLER is None:
from evennia.scripts.taskhandler import TASK_HANDLER as _TASK_HANDLER from evennia.scripts.taskhandler import TASK_HANDLER as _TASK_HANDLER
_TASK_HANDLER.clock = task.Clock() _TASK_HANDLER.clock = task.Clock()
self.char1.ndb.dummy_var = False self.char1.ndb.dummy_var = False
# test a persistent deferral, that completes after delay time
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True) def tearDown(self):
# call the task early to test Task.call and TaskHandler.call_task super().tearDown()
result = t.call() _TASK_HANDLER.clear()
self.assertTrue(result)
del result def test_call_early(self):
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran') # call a task early with call
self.assertTrue(_TASK_HANDLER.active(t.get_id())) # test Task.get_id for pers in (True, False):
self.assertTrue(t.active()) t = utils.delay(self.timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.char1.ndb.dummy_var = False # Set variable to continue completion after delay time test. result = t.call()
_TASK_HANDLER.clock.advance(timedelay) # make time pass self.assertTrue(result)
self.assertTrue(t.called) # test Task.called property self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran') self.assertTrue(t.exists())
self.char1.ndb.dummy_var = False self.assertTrue(t.active())
# test a persistent deferral, that completes on a manual call self.char1.ndb.dummy_var = False
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True)
self.assertTrue(t.active()) def test_do_task(self):
result = t.do_task() # call the task early with do_task
self.assertTrue(result) for pers in (True, False):
self.assertFalse(t.exists()) t = utils.delay(self.timedelay, dummy_func, self.char1.dbref, persistent=pers)
_TASK_HANDLER.clock.advance(timedelay) # make time pass, important keep # call the task early to test Task.call and TaskHandler.call_task
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran') result = t.do_task()
self.char1.ndb.dummy_var = False self.assertTrue(result)
# test a non persisten deferral, that completes after delay time. self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.assertFalse(t.exists())
self.char1.ndb.dummy_var = False
def test_deferred_call(self):
# wait for deferred to call
timedelay = self.timedelay
for pers in (False, True):
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.assertTrue(t.active())
_TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.assertFalse(t.exists())
self.char1.ndb.dummy_var = False
def test_short_deferred_call(self):
# wait for deferred to call with a very short time
timedelay = .1
for pers in (False, True):
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.assertTrue(t.active())
_TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.assertFalse(t.exists())
self.char1.ndb.dummy_var = False
def test_active(self):
timedelay = self.timedelay
t = utils.delay(timedelay, dummy_func, self.char1.dbref) t = utils.delay(timedelay, dummy_func, self.char1.dbref)
self.assertTrue(_TASK_HANDLER.active(t.get_id()))
self.assertTrue(t.active()) self.assertTrue(t.active())
_TASK_HANDLER.clock.advance(timedelay) # make time pass _TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran') self.assertFalse(_TASK_HANDLER.active(t.get_id()))
self.char1.ndb.dummy_var = False
# test a non-persistent deferral, that completes on a manual call
t = utils.delay(timedelay, dummy_func, self.char1.dbref)
self.assertTrue(t.active())
result = t.do_task()
self.assertTrue(result)
self.assertFalse(t.exists())
_TASK_HANDLER.clock.advance(timedelay) # make time pass, important keep
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.char1.ndb.dummy_var = False
# test a non persisten deferral, with a short timedelay
t = utils.delay(.1, dummy_func, self.char1.dbref)
self.assertTrue(t.active())
_TASK_HANDLER.clock.advance(.1) # make time pass
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.char1.ndb.dummy_var = False
# test canceling a deferral.
# after this the task_id 1 remains used by this canceled but unused task
t = utils.delay(timedelay, dummy_func, self.char1.dbref)
self.assertTrue(t.active())
success = t.cancel()
self.assertFalse(t.active()) self.assertFalse(t.active())
self.assertTrue(success)
self.assertTrue(t.exists()) def test_called(self):
_TASK_HANDLER.clock.advance(timedelay) # make time pass timedelay = self.timedelay
self.assertEqual(self.char1.ndb.dummy_var, False)
self.char1.ndb.dummy_var = False
# test removing an active task
t = utils.delay(timedelay, dummy_func, self.char1.dbref) t = utils.delay(timedelay, dummy_func, self.char1.dbref)
self.assertTrue(t.active()) self.assertFalse(t.called)
success = t.remove()
self.assertFalse(t.active())
_TASK_HANDLER.clock.advance(timedelay) # make time pass _TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertEqual(self.char1.ndb.dummy_var, False) self.assertTrue(t.called)
self.assertFalse(t.exists())
self.char1.ndb.dummy_var = False def test_cancel(self):
# test removing a canceled task timedelay = self.timedelay
t = utils.delay(timedelay, dummy_func, self.char1.dbref) for pers in (False, True):
self.assertTrue(t.active()) t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
deferal_inst = t.get_deferred() self.assertTrue(t.active())
deferal_inst.cancel() success = t.cancel()
self.assertFalse(t.active()) self.assertFalse(t.active())
success = t.remove() self.assertTrue(success)
_TASK_HANDLER.clock.advance(timedelay) # make time pass self.assertTrue(t.exists())
self.assertEqual(self.char1.ndb.dummy_var, False) _TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertFalse(t.exists()) self.assertEqual(self.char1.ndb.dummy_var, False)
self.char1.ndb.dummy_var = False
# test pause, paused and unpause def test_remove(self):
t = utils.delay(timedelay, dummy_func, self.char1.dbref) timedelay = self.timedelay
self.assertTrue(t.active()) for pers in (False, True):
t.pause() t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.assertTrue(t.paused) self.assertTrue(t.active())
t.unpause() success = t.remove()
self.assertFalse(t.paused) self.assertTrue(success)
self.assertEqual(self.char1.ndb.dummy_var, False) self.assertFalse(t.active())
t.pause() self.assertFalse(t.exists())
_TASK_HANDLER.clock.advance(timedelay) # make time pass _TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertEqual(self.char1.ndb.dummy_var, False) self.assertEqual(self.char1.ndb.dummy_var, False)
t.unpause()
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran') def test_remove_canceled(self):
self.char1.ndb.dummy_var = False # remove a canceled task
# test automated removal of stale tasks. timedelay = self.timedelay
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True) for pers in (False, True):
t.cancel() t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.assertFalse(t.active()) self.assertTrue(t.active())
_TASK_HANDLER.clock.advance(timedelay) # make time pass success = t.cancel()
self.assertTrue(t.get_id() in _TASK_HANDLER.to_save) self.assertTrue(success)
self.assertTrue(t.get_id() in _TASK_HANDLER.tasks) self.assertTrue(t.exists())
self.assertFalse(t.active())
success = t.remove()
self.assertTrue(success)
self.assertFalse(t.exists())
_TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertEqual(self.char1.ndb.dummy_var, False)
def test_pause_unpause(self):
# remove a canceled task
timedelay = self.timedelay
for pers in (False, True):
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.assertTrue(t.active())
t.pause()
self.assertTrue(t.paused)
t.unpause()
self.assertFalse(t.paused)
self.assertEqual(self.char1.ndb.dummy_var, False)
t.pause()
_TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertEqual(self.char1.ndb.dummy_var, False)
t.unpause()
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.char1.ndb.dummy_var = False
def test_auto_stale_task_removal(self):
# automated removal of stale tasks.
timedelay = self.timedelay
for pers in (False, True):
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
t.cancel()
self.assertFalse(t.active())
_TASK_HANDLER.clock.advance(timedelay) # make time pass
if pers:
self.assertTrue(t.get_id() in _TASK_HANDLER.to_save)
self.assertTrue(t.get_id() in _TASK_HANDLER.tasks)
# Make task handler's now time, after the stale timeout
_TASK_HANDLER._now = datetime.now() + timedelta(seconds=_TASK_HANDLER.stale_timeout + timedelay + 1)
# add a task to test automatic removal # add a task to test automatic removal
_TASK_HANDLER._now = datetime.now() + timedelta(seconds=_TASK_HANDLER.stale_timeout + 6) # task handler time to 6 seconds after stale timeout t2 = utils.delay(timedelay, dummy_func, self.char1.dbref)
t2 = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True) if pers:
self.assertFalse(t.get_id() in _TASK_HANDLER.to_save) self.assertFalse(t.get_id() in _TASK_HANDLER.to_save)
self.assertFalse(t.get_id() in _TASK_HANDLER.tasks) self.assertFalse(t.get_id() in _TASK_HANDLER.tasks)
self.assertEqual(self.char1.ndb.dummy_var, False) self.assertEqual(self.char1.ndb.dummy_var, False)
# test manual cleanup _TASK_HANDLER.clear()
t2.cancel()
_TASK_HANDLER.clock.advance(timedelay) # advance twisted reactor time past callback time def test_manual_stale_task_removal(self):
_TASK_HANDLER._now = datetime.now() + timedelta(seconds=30) # set TaskHandler's time to 30 seconnds from now # manual removal of stale tasks.
# test before stale_timeout time timedelay = self.timedelay
_TASK_HANDLER.clean_stale_tasks() # cleanup of stale tasks in in the save method for pers in (False, True):
# still in the task handler because stale timeout has not been reached t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.assertTrue(t2.get_id() in _TASK_HANDLER.to_save) t.cancel()
self.assertTrue(t2.get_id() in _TASK_HANDLER.tasks) self.assertFalse(t.active())
# advance past stale timeout _TASK_HANDLER.clock.advance(timedelay) # make time pass
_TASK_HANDLER._now = datetime.now() + timedelta(seconds=_TASK_HANDLER.stale_timeout + 6) # task handler time to 6 seconds after stale timeout if pers:
_TASK_HANDLER.clean_stale_tasks() # cleanup of stale tasks in in the save method self.assertTrue(t.get_id() in _TASK_HANDLER.to_save)
self.assertFalse(t2.get_id() in _TASK_HANDLER.to_save) self.assertTrue(t.get_id() in _TASK_HANDLER.tasks)
self.assertFalse(t2.get_id() in _TASK_HANDLER.tasks) # Make task handler's now time, after the stale timeout
self.char1.ndb.dummy_var = False _TASK_HANDLER._now = datetime.now() + timedelta(seconds=_TASK_HANDLER.stale_timeout + timedelay + 1)
_TASK_HANDLER._now = False _TASK_HANDLER.clean_stale_tasks() # cleanup of stale tasks in in the save method
# if _TASK_HANDLER.stale_timeout is 0 or less, automatic cleanup should not run if pers:
self.assertFalse(t.get_id() in _TASK_HANDLER.to_save)
self.assertFalse(t.get_id() in _TASK_HANDLER.tasks)
self.assertEqual(self.char1.ndb.dummy_var, False)
_TASK_HANDLER.clear()
def test_disable_stale_removal(self):
# manual removal of stale tasks.
timedelay = self.timedelay
_TASK_HANDLER.stale_timeout = 0 _TASK_HANDLER.stale_timeout = 0
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True) for pers in (False, True):
t.cancel() t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.assertFalse(t.active()) t.cancel()
_TASK_HANDLER.clock.advance(timedelay) # advance twisted's reactor past callback time self.assertFalse(t.active())
self.assertTrue(t.get_id() in _TASK_HANDLER.to_save) _TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertTrue(t.get_id() in _TASK_HANDLER.tasks) if pers:
# add a task to test automatic removal self.assertTrue(t.get_id() in _TASK_HANDLER.to_save)
_TASK_HANDLER._now = datetime.now() + timedelta(seconds=_TASK_HANDLER.stale_timeout + 6) # task handler time to 6 seconds after stale timeout self.assertTrue(t.get_id() in _TASK_HANDLER.tasks)
t2 = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True) # Make task handler's now time, after the stale timeout
self.assertTrue(t.get_id() in _TASK_HANDLER.to_save) _TASK_HANDLER._now = datetime.now() + timedelta(seconds=_TASK_HANDLER.stale_timeout + timedelay + 1)
self.assertTrue(t.get_id() in _TASK_HANDLER.tasks) t2 = utils.delay(timedelay, dummy_func, self.char1.dbref)
self.assertEqual(self.char1.ndb.dummy_var, False) if pers:
_TASK_HANDLER.clear() self.assertTrue(t.get_id() in _TASK_HANDLER.to_save)
self.char1.ndb.dummy_var = False self.assertTrue(t.get_id() in _TASK_HANDLER.tasks)
_TASK_HANDLER._now = False self.assertEqual(self.char1.ndb.dummy_var, False)
# replicate a restart # manual removal should still work
_TASK_HANDLER.clear() _TASK_HANDLER.clean_stale_tasks() # cleanup of stale tasks in in the save method
_TASK_HANDLER.save() if pers:
self.assertFalse(_TASK_HANDLER.tasks) self.assertFalse(t.get_id() in _TASK_HANDLER.to_save)
self.assertFalse(_TASK_HANDLER.to_save) self.assertFalse(t.get_id() in _TASK_HANDLER.tasks)
# create a persistent task. _TASK_HANDLER.clear()
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True)
_TASK_HANDLER.save() def test_server_restart(self):
_TASK_HANDLER.clear(False) # remove all tasks, do not save this change. # emulate a server restart
timedelay = self.timedelay
utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True)
_TASK_HANDLER.clear(False) # remove all tasks from task handler, do not save this change.
_TASK_HANDLER.clock.advance(timedelay) # advance twisted reactor time past callback time _TASK_HANDLER.clock.advance(timedelay) # advance twisted reactor time past callback time
self.assertEqual(self.char1.ndb.dummy_var, False) # task has not run self.assertEqual(self.char1.ndb.dummy_var, False) # task has not run
_TASK_HANDLER.load() _TASK_HANDLER.load() # load persistent tasks from database.
_TASK_HANDLER.create_delays() _TASK_HANDLER.create_delays() # create new deffered instances from persistent tasks
_TASK_HANDLER.clock.advance(timedelay) # Clock must advance to trigger, even if past timedelay _TASK_HANDLER.clock.advance(timedelay) # Clock must advance to trigger, even if past timedelay
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran') self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
_TASK_HANDLER.clear()
self.char1.ndb.dummy_var = False