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,61 +323,84 @@ 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()
_TASK_HANDLER.clear()
def test_call_early(self):
# call a task early with call
for pers in (True, False):
t = utils.delay(self.timedelay, dummy_func, self.char1.dbref, persistent=pers)
result = t.call() result = t.call()
self.assertTrue(result) self.assertTrue(result)
del result
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran') self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.assertTrue(_TASK_HANDLER.active(t.get_id())) # test Task.get_id self.assertTrue(t.exists())
self.assertTrue(t.active()) self.assertTrue(t.active())
self.char1.ndb.dummy_var = False # Set variable to continue completion after delay time test.
_TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertTrue(t.called) # test Task.called property
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.char1.ndb.dummy_var = False self.char1.ndb.dummy_var = False
# test a persistent deferral, that completes on a manual call
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True) def test_do_task(self):
self.assertTrue(t.active()) # call the task early with do_task
for pers in (True, False):
t = utils.delay(self.timedelay, dummy_func, self.char1.dbref, persistent=pers)
# call the task early to test Task.call and TaskHandler.call_task
result = t.do_task() result = t.do_task()
self.assertTrue(result) 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.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.assertFalse(t.exists())
self.char1.ndb.dummy_var = False self.char1.ndb.dummy_var = False
# test a non persisten deferral, that completes after delay time.
t = utils.delay(timedelay, dummy_func, self.char1.dbref) 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()) 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.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
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()) 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 self.char1.ndb.dummy_var = False
# test a non persisten deferral, with a short timedelay
t = utils.delay(.1, dummy_func, self.char1.dbref) 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()) self.assertTrue(t.active())
_TASK_HANDLER.clock.advance(.1) # make time pass _TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran') self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.assertFalse(t.exists())
self.char1.ndb.dummy_var = False self.char1.ndb.dummy_var = False
# test canceling a deferral.
# after this the task_id 1 remains used by this canceled but unused task 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())
_TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertFalse(_TASK_HANDLER.active(t.get_id()))
self.assertFalse(t.active())
def test_called(self):
timedelay = self.timedelay
t = utils.delay(timedelay, dummy_func, self.char1.dbref)
self.assertFalse(t.called)
_TASK_HANDLER.clock.advance(timedelay) # make time pass
self.assertTrue(t.called)
def test_cancel(self):
timedelay = self.timedelay
for pers in (False, True):
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.assertTrue(t.active()) self.assertTrue(t.active())
success = t.cancel() success = t.cancel()
self.assertFalse(t.active()) self.assertFalse(t.active())
@ -385,29 +408,40 @@ class TestDelay(EvenniaTest):
self.assertTrue(t.exists()) self.assertTrue(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)
self.char1.ndb.dummy_var = False
# test removing an active task def test_remove(self):
t = utils.delay(timedelay, dummy_func, self.char1.dbref) timedelay = self.timedelay
for pers in (False, True):
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.assertTrue(t.active()) self.assertTrue(t.active())
success = t.remove() success = t.remove()
self.assertTrue(success)
self.assertFalse(t.active()) self.assertFalse(t.active())
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)
self.assertFalse(t.exists())
self.char1.ndb.dummy_var = False def test_remove_canceled(self):
# test removing a canceled task # remove a canceled task
t = utils.delay(timedelay, dummy_func, self.char1.dbref) timedelay = self.timedelay
for pers in (False, True):
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.assertTrue(t.active()) self.assertTrue(t.active())
deferal_inst = t.get_deferred() success = t.cancel()
deferal_inst.cancel() self.assertTrue(success)
self.assertTrue(t.exists())
self.assertFalse(t.active()) self.assertFalse(t.active())
success = t.remove() success = t.remove()
self.assertTrue(success)
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)
self.assertFalse(t.exists())
self.char1.ndb.dummy_var = False def test_pause_unpause(self):
# test pause, paused and unpause # remove a canceled task
t = utils.delay(timedelay, dummy_func, self.char1.dbref) timedelay = self.timedelay
for pers in (False, True):
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
self.assertTrue(t.active()) self.assertTrue(t.active())
t.pause() t.pause()
self.assertTrue(t.paused) self.assertTrue(t.paused)
@ -420,66 +454,82 @@ class TestDelay(EvenniaTest):
t.unpause() t.unpause()
self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran') self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
self.char1.ndb.dummy_var = False self.char1.ndb.dummy_var = False
# test automated removal of stale tasks.
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True) 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() t.cancel()
self.assertFalse(t.active()) self.assertFalse(t.active())
_TASK_HANDLER.clock.advance(timedelay) # make time pass _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.to_save)
self.assertTrue(t.get_id() in _TASK_HANDLER.tasks) 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)
self.assertTrue(t2.get_id() in _TASK_HANDLER.tasks)
# advance past stale timeout
_TASK_HANDLER._now = datetime.now() + timedelta(seconds=_TASK_HANDLER.stale_timeout + 6) # task handler time to 6 seconds after stale timeout
_TASK_HANDLER.clean_stale_tasks() # cleanup of stale tasks in in the save method
self.assertFalse(t2.get_id() in _TASK_HANDLER.to_save)
self.assertFalse(t2.get_id() in _TASK_HANDLER.tasks)
self.char1.ndb.dummy_var = False
_TASK_HANDLER._now = False
# if _TASK_HANDLER.stale_timeout is 0 or less, automatic cleanup should not run
_TASK_HANDLER.stale_timeout = 0
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True)
t.cancel() t.cancel()
self.assertFalse(t.active()) self.assertFalse(t.active())
_TASK_HANDLER.clock.advance(timedelay) # advance twisted's reactor past callback time _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.to_save)
self.assertTrue(t.get_id() in _TASK_HANDLER.tasks) self.assertTrue(t.get_id() in _TASK_HANDLER.tasks)
# add a task to test automatic removal # Make task handler's now time, after the stale timeout
_TASK_HANDLER._now = datetime.now() + timedelta(seconds=_TASK_HANDLER.stale_timeout + 6) # task handler time to 6 seconds after stale timeout _TASK_HANDLER._now = datetime.now() + timedelta(seconds=_TASK_HANDLER.stale_timeout + timedelay + 1)
t2 = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True) _TASK_HANDLER.clean_stale_tasks() # cleanup of stale tasks in in the save method
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
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)
t2 = utils.delay(timedelay, dummy_func, self.char1.dbref)
if pers:
self.assertTrue(t.get_id() in _TASK_HANDLER.to_save) self.assertTrue(t.get_id() in _TASK_HANDLER.to_save)
self.assertTrue(t.get_id() in _TASK_HANDLER.tasks) self.assertTrue(t.get_id() in _TASK_HANDLER.tasks)
self.assertEqual(self.char1.ndb.dummy_var, False) self.assertEqual(self.char1.ndb.dummy_var, False)
# manual removal should still work
_TASK_HANDLER.clean_stale_tasks() # cleanup of stale tasks in in the save method
if pers:
self.assertFalse(t.get_id() in _TASK_HANDLER.to_save)
self.assertFalse(t.get_id() in _TASK_HANDLER.tasks)
_TASK_HANDLER.clear() _TASK_HANDLER.clear()
self.char1.ndb.dummy_var = False
_TASK_HANDLER._now = False def test_server_restart(self):
# replicate a restart # emulate a server restart
_TASK_HANDLER.clear() timedelay = self.timedelay
_TASK_HANDLER.save() utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True)
self.assertFalse(_TASK_HANDLER.tasks) _TASK_HANDLER.clear(False) # remove all tasks from task handler, do not save this change.
self.assertFalse(_TASK_HANDLER.to_save)
# create a persistent task.
t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True)
_TASK_HANDLER.save()
_TASK_HANDLER.clear(False) # remove all tasks, 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