""" Unit tests for the utilities of the evennia.utils.utils module. TODO: Not nearly all utilities are covered yet. """ import os.path import random from datetime import datetime, timedelta import mock from django.test import TestCase from evennia.utils import utils from evennia.utils.ansi import ANSIString from evennia.utils.test_resources import BaseEvenniaTest from parameterized import parameterized from twisted.internet import task class TestIsIter(TestCase): def test_is_iter(self): self.assertEqual(True, utils.is_iter([1, 2, 3, 4])) self.assertEqual(False, utils.is_iter("This is not an iterable")) class TestCrop(TestCase): def test_crop(self): # No text, return no text self.assertEqual("", utils.crop("", width=10, suffix="[...]")) # Input length equal to max width, no crop self.assertEqual("0123456789", utils.crop("0123456789", width=10, suffix="[...]")) # Input length greater than max width, crop (suffix included in width) self.assertEqual("0123[...]", utils.crop("0123456789", width=9, suffix="[...]")) # Input length less than desired width, no crop self.assertEqual("0123", utils.crop("0123", width=9, suffix="[...]")) # Width too small or equal to width of suffix self.assertEqual("012", utils.crop("0123", width=3, suffix="[...]")) self.assertEqual("01234", utils.crop("0123456", width=5, suffix="[...]")) class TestDedent(TestCase): def test_dedent(self): # Empty string, return empty string self.assertEqual("", utils.dedent("")) # No leading whitespace self.assertEqual("TestDedent", utils.dedent("TestDedent")) # Leading whitespace, single line self.assertEqual("TestDedent", utils.dedent(" TestDedent")) # Leading whitespace, multi line input_string = " hello\n world" expected_string = "hello\nworld" self.assertEqual(expected_string, utils.dedent(input_string)) class TestCompressWhitespace(TestCase): def test_compress_whitespace(self): # No text, return no text self.assertEqual("", utils.compress_whitespace("")) # If no whitespace is exceeded, should return the same self.assertEqual("One line\nTwo spaces", utils.compress_whitespace("One line\nTwo spaces")) # Extra newlines are removed self.assertEqual("First line\nSecond line", utils.compress_whitespace("First line\n\nSecond line")) # Extra spaces are removed self.assertEqual("Too many spaces", utils.compress_whitespace("Too many spaces")) # "Invisible" extra lines with whitespace are removed self.assertEqual("First line\nSecond line", utils.compress_whitespace("First line\n \n \nSecond line")) # Max kwargs are respected self.assertEqual("First line\n\nSecond line", utils.compress_whitespace("First line\n\nSecond line", max_spacing=1, max_linebreaks=2)) def test_preserve_indents(self): """Ensure that indentation spacing is preserved.""" indented = """\ Hanging Indents they're great let's keep them\ """ # since there is no doubled-up spacing besides indents, input should equal output self.assertEqual(indented, utils.compress_whitespace(indented)) class TestListToString(TestCase): """ Default function header from time.py: list_to_string(inlist, sep=",", endsep=", and", addquote=False) Examples: with defaults: [1,2,3] -> '1, 2, and 3' with endsep==',': [1,2,3] -> '1, 2, 3' with sep==';' and endsep==';': [1,2,3] -> '1; 2; 3' with sep=='or': [1,2,3] -> '1 or 2, and 3' with endsep=='and': [1,2,3] -> '1, 2 and 3' with endsep=='; and': [1,2,3] -> '1, 2; and 3' with endsep=='': [1,2,3] -> '1, 2 3' with addquote and endsep="and" [1,2,3] -> '"1", "2" and "3"' """ def test_list_to_string(self): self.assertEqual("1, 2, and 3", utils.list_to_string([1, 2, 3])) self.assertEqual("1, 2, 3", utils.list_to_string([1, 2, 3], endsep=",")) self.assertEqual("1, 2 and 3", utils.list_to_string([1, 2, 3], endsep="and")) self.assertEqual("1, 2 3", utils.list_to_string([1, 2, 3], endsep="")) self.assertEqual("1; 2; 3", utils.list_to_string([1, 2, 3], sep=";", endsep=";")) self.assertEqual("1 or 2, and 3", utils.list_to_string([1, 2, 3], sep="or")) self.assertEqual("1, 2; and 3", utils.list_to_string([1, 2, 3], endsep="; and")) self.assertEqual( '"1", "2", "3"', utils.list_to_string([1, 2, 3], endsep=",", addquote=True) ) self.assertEqual( '"1", "2" and "3"', utils.list_to_string([1, 2, 3], endsep="and", addquote=True) ) self.assertEqual("1 and 2", utils.list_to_string([1, 2])) self.assertEqual("1, 2", utils.list_to_string([1, 2], endsep=",")) class TestMLen(TestCase): """ Verifies that m_len behaves like len in all situations except those where MXP may be involved. """ def test_non_mxp_string(self): self.assertEqual(utils.m_len("Test_string"), 11) def test_mxp_string(self): self.assertEqual(utils.m_len("|lclook|ltat|le"), 2) def test_mxp_ansi_string(self): self.assertEqual(utils.m_len(ANSIString("|lcl|gook|ltat|le|n")), 2) def test_non_mxp_ansi_string(self): self.assertEqual(utils.m_len(ANSIString("|gHello|n")), 5) def test_list(self): self.assertEqual(utils.m_len([None, None]), 2) def test_dict(self): self.assertEqual(utils.m_len({"hello": True, "Goodbye": False}), 2) class TestDisplayLen(TestCase): """ Verifies that display_len behaves like m_len in all situations except those where asian characters are involved. """ def test_non_mxp_string(self): self.assertEqual(utils.display_len("Test_string"), 11) def test_mxp_string(self): self.assertEqual(utils.display_len("|lclook|ltat|le"), 2) def test_mxp_ansi_string(self): self.assertEqual(utils.display_len(ANSIString("|lcl|gook|ltat|le|n")), 2) def test_non_mxp_ansi_string(self): self.assertEqual(utils.display_len(ANSIString("|gHello|n")), 5) def test_list(self): self.assertEqual(utils.display_len([None, None]), 2) def test_dict(self): self.assertEqual(utils.display_len({"hello": True, "Goodbye": False}), 2) def test_east_asian(self): self.assertEqual(utils.display_len("서서서"), 6) class TestANSIString(TestCase): """ Verifies that ANSIString's string-API works as intended. """ def setUp(self): self.example_raw = "|relectric |cboogaloo|n" self.example_ansi = ANSIString(self.example_raw) self.example_str = "electric boogaloo" self.example_output = "\x1b[1m\x1b[31melectric \x1b[1m\x1b[36mboogaloo\x1b[0m" def test_length(self): self.assertEqual(len(self.example_ansi), 17) def test_clean(self): self.assertEqual(self.example_ansi.clean(), self.example_str) def test_raw(self): self.assertEqual(self.example_ansi.raw(), self.example_output) def test_format(self): self.assertEqual(f"{self.example_ansi:0<20}", self.example_output + "000") class TestTimeformat(TestCase): """ Default function header from time.py: time_format(seconds, style=0) """ def test_style_0(self): """Test the style 0 of time_format.""" self.assertEqual(utils.time_format(0, 0), "00:00") self.assertEqual(utils.time_format(28, 0), "00:00") self.assertEqual(utils.time_format(92, 0), "00:01") self.assertEqual(utils.time_format(300, 0), "00:05") self.assertEqual(utils.time_format(660, 0), "00:11") self.assertEqual(utils.time_format(3600, 0), "01:00") self.assertEqual(utils.time_format(3725, 0), "01:02") self.assertEqual(utils.time_format(86350, 0), "23:59") self.assertEqual(utils.time_format(86800, 0), "1d 00:06") self.assertEqual(utils.time_format(130800, 0), "1d 12:20") self.assertEqual(utils.time_format(530800, 0), "6d 03:26") def test_style_1(self): """Test the style 1 of time_format.""" self.assertEqual(utils.time_format(0, 1), "0s") self.assertEqual(utils.time_format(28, 1), "28s") self.assertEqual(utils.time_format(92, 1), "1m") self.assertEqual(utils.time_format(300, 1), "5m") self.assertEqual(utils.time_format(660, 1), "11m") self.assertEqual(utils.time_format(3600, 1), "1h") self.assertEqual(utils.time_format(3725, 1), "1h") self.assertEqual(utils.time_format(86350, 1), "23h") self.assertEqual(utils.time_format(86800, 1), "1d") self.assertEqual(utils.time_format(130800, 1), "1d") self.assertEqual(utils.time_format(530800, 1), "6d") def test_style_2(self): """Test the style 2 of time_format.""" self.assertEqual(utils.time_format(0, 2), "0 minutes") self.assertEqual(utils.time_format(28, 2), "0 minutes") self.assertEqual(utils.time_format(92, 2), "1 minute") self.assertEqual(utils.time_format(300, 2), "5 minutes") self.assertEqual(utils.time_format(660, 2), "11 minutes") self.assertEqual(utils.time_format(3600, 2), "1 hour, 0 minutes") self.assertEqual(utils.time_format(3725, 2), "1 hour, 2 minutes") self.assertEqual(utils.time_format(86350, 2), "23 hours, 59 minutes") self.assertEqual(utils.time_format(86800, 2), "1 day, 0 hours, 6 minutes") self.assertEqual(utils.time_format(130800, 2), "1 day, 12 hours, 20 minutes") self.assertEqual(utils.time_format(530800, 2), "6 days, 3 hours, 26 minutes") def test_style_3(self): """Test the style 3 of time_format.""" self.assertEqual(utils.time_format(0, 3), "") self.assertEqual(utils.time_format(28, 3), "28 seconds") self.assertEqual(utils.time_format(92, 3), "1 minute 32 seconds") self.assertEqual(utils.time_format(300, 3), "5 minutes 0 seconds") self.assertEqual(utils.time_format(660, 3), "11 minutes 0 seconds") self.assertEqual(utils.time_format(3600, 3), "1 hour, 0 minutes") self.assertEqual(utils.time_format(3725, 3), "1 hour, 2 minutes 5 seconds") self.assertEqual(utils.time_format(86350, 3), "23 hours, 59 minutes 10 seconds") self.assertEqual(utils.time_format(86800, 3), "1 day, 0 hours, 6 minutes 40 seconds") self.assertEqual(utils.time_format(130800, 3), "1 day, 12 hours, 20 minutes 0 seconds") self.assertEqual(utils.time_format(530800, 3), "6 days, 3 hours, 26 minutes 40 seconds") def test_style_4(self): """Test the style 4 of time_format.""" self.assertEqual(utils.time_format(0, 4), "0 seconds") self.assertEqual(utils.time_format(28, 4), "28 seconds") self.assertEqual(utils.time_format(92, 4), "a minute") self.assertEqual(utils.time_format(300, 4), "5 minutes") self.assertEqual(utils.time_format(660, 4), "11 minutes") self.assertEqual(utils.time_format(3600, 4), "an hour") self.assertEqual(utils.time_format(3725, 4), "an hour") self.assertEqual(utils.time_format(86350, 4), "23 hours") self.assertEqual(utils.time_format(86800, 4), "a day") self.assertEqual(utils.time_format(130800, 4), "a day") self.assertEqual(utils.time_format(530800, 4), "6 days") self.assertEqual(utils.time_format(3030800, 4), "a month") self.assertEqual(utils.time_format(7030800, 4), "2 months") self.assertEqual(utils.time_format(40030800, 4), "a year") self.assertEqual(utils.time_format(90030800, 4), "2 years") def test_unknown_format(self): """Test that unknown formats raise exceptions.""" self.assertRaises(ValueError, utils.time_format, 0, 5) self.assertRaises(ValueError, utils.time_format, 0, "u") @mock.patch( "evennia.utils.utils.timezone.now", new=mock.MagicMock(return_value=datetime(2019, 8, 28, 21, 56)), ) class TestDateTimeFormat(TestCase): def test_datetimes(self): dtobj = datetime(2017, 7, 26, 22, 54) self.assertEqual(utils.datetime_format(dtobj), "Jul 26, 2017") dtobj = datetime(2019, 7, 26, 22, 54) self.assertEqual(utils.datetime_format(dtobj), "Jul 26") dtobj = datetime(2019, 8, 28, 19, 54) self.assertEqual(utils.datetime_format(dtobj), "19:54") dtobj = datetime(2019, 8, 28, 21, 32) self.assertEqual(utils.datetime_format(dtobj), "21:32:00") class TestImportFunctions(TestCase): def _path_to_file(self, filename): testdir = os.path.dirname(os.path.abspath(__file__)) return os.path.join(testdir, filename) def test_mod_import(self): loaded_mod = utils.mod_import("evennia.utils.ansi") self.assertIsNotNone(loaded_mod) def test_mod_import_invalid(self): loaded_mod = utils.mod_import("evennia.utils.invalid_module") self.assertIsNone(loaded_mod) def test_mod_import_from_path(self): test_path = self._path_to_file("test_eveditor.py") loaded_mod = utils.mod_import_from_path(test_path) self.assertIsNotNone(loaded_mod) def test_mod_import_from_path_invalid(self): test_path = self._path_to_file("invalid_filename.py") loaded_mod = utils.mod_import_from_path(test_path) self.assertIsNone(loaded_mod) class LatinifyTest(TestCase): def setUp(self): super().setUp() self.example_str = "It naïvely says, “plugh.”" self.expected_output = 'It naively says, "plugh."' def test_plain_string(self): result = utils.latinify(self.example_str) self.assertEqual(result, self.expected_output) def test_byte_string(self): byte_str = utils.to_bytes(self.example_str) result = utils.latinify(byte_str) self.assertEqual(result, self.expected_output) class TestFormatGrid(TestCase): maxDiff = None def setUp(self): # make the random only semi-random with a fixed seed random.seed(1) def tearDown(self): # restore normal randomness random.seed(None) def _generate_elements(self, basewidth, variation, amount): return [ "X" * max(1, basewidth + int(random.randint(-variation, variation))) for _ in range(amount) ] def test_even_grid(self): """Grid with small variations""" elements = self._generate_elements(3, 1, 30) rows = utils.format_grid(elements, width=78) self.assertEqual(len(rows), 4) self.assertTrue(all(len(row) == 78 for row in rows)) def test_disparate_grid(self): """Grid with big variations""" elements = self._generate_elements(3, 15, 30) rows = utils.format_grid(elements, width=82, sep=" ") self.assertEqual(len(rows), 8) self.assertTrue(all(len(row) == 82 for row in rows)) def test_huge_grid(self): """Grid with very long strings""" elements = self._generate_elements(70, 20, 30) rows = utils.format_grid(elements, width=78) self.assertEqual(len(rows), 30) self.assertTrue(all(len(row) == 78 for row in rows)) def test_overlap(self): """Grid with elements overlapping into the next slot""" elements = ( "alias", "batchcode", "batchcommands", "cmdsets", "copy", "cpattr", "desc", "destroy", "dig", "examine", "find", "force", "lock", ) rows = utils.format_grid(elements, width=78) self.assertEqual(len(rows), 3) for element in elements: self.assertTrue(element in "\n".join(rows), f"element {element} is missing.") def test_breakline(self): """Grid with line-long elements in middle""" elements = self._generate_elements(6, 4, 30) elements[10] = elements[20] = "-" * 78 rows = utils.format_grid(elements, width=78) self.assertEqual(len(rows), 8) for element in elements: self.assertTrue(element in "\n".join(rows), f"element {element} is missing.") class TestPercent(TestCase): """ Test the utils.percentage function. """ def test_ok_input(self): result = utils.percent(3, 0, 10) self.assertEqual(result, "30.0%") result = utils.percent(2.5, 0, 5, formatting=None) self.assertEqual(result, 50.0) def test_bad_input(self): """Gracefully handle weird input.""" self.assertEqual(utils.percent(3, 10, 1), "0.0%") self.assertEqual(utils.percent(3, None, 1), "100.0%") self.assertEqual(utils.percent(1, 1, 1), "100.0%") self.assertEqual(utils.percent(3, 1, 1), "0.0%") self.assertEqual(utils.percent(3, 0, 1), "100.0%") self.assertEqual(utils.percent(-3, 0, 1), "0.0%") class TestSafeConvert(TestCase): """ Test evennia.utils.utils.safe_convert_to_types """ @parameterized.expand( [ ( ("1", "2", 3, 4, "5"), {"a": 1, "b": "2", "c": 3}, ((int, float, str, int), {"a": int, "b": float}), # " (1, 2.0, "3", 4, "5"), {"a": 1, "b": 2.0, "c": 3}, ), ( ("1 + 2", "[1, 2, 3]", [3, 4, 5]), {"a": "3 + 4", "b": 5}, (("py", "py", "py"), {"a": "py", "b": "py"}), (3, [1, 2, 3], [3, 4, 5]), {"a": 7, "b": 5}, ), ] ) def test_conversion(self, args, kwargs, converters, expected_args, expected_kwargs): """ Test the converter with different inputs """ result_args, result_kwargs = utils.safe_convert_to_types( converters, *args, raise_errors=True, **kwargs ) self.assertEqual(expected_args, result_args) self.assertEqual(expected_kwargs, result_kwargs) def test_conversion__fail(self): """ Test failing conversion """ from evennia.utils.funcparser import ParsingError with self.assertRaises(ValueError): utils.safe_convert_to_types((int,), *("foo",), raise_errors=True) with self.assertRaises(ParsingError) as err: utils.safe_convert_to_types(("py", {}), *("foo",), raise_errors=True) _TASK_HANDLER = None def dummy_func(obj): """ Used in TestDelay. A function that: can be serialized uses no memory references uses evennia objects """ # get a reference of object from evennia.objects.models import ObjectDB obj = ObjectDB.objects.object_search(obj) obj = obj[0] # make changes to object obj.ndb.dummy_var = "dummy_func ran" return True class TestDelay(BaseEvenniaTest): """ Test utils.delay. """ def setUp(self): super().setUp() # get a reference of TASK_HANDLER self.timedelay = 5 global _TASK_HANDLER if _TASK_HANDLER is None: from evennia.scripts.taskhandler import TASK_HANDLER as _TASK_HANDLER _TASK_HANDLER.clock = task.Clock() self.char1.ndb.dummy_var = False def tearDown(self): 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() self.assertTrue(result) self.assertEqual(self.char1.ndb.dummy_var, "dummy_func ran") self.assertTrue(t.exists()) self.assertTrue(t.active()) self.char1.ndb.dummy_var = False def test_do_task(self): # 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() self.assertTrue(result) 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 = 0.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) 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()) success = t.cancel() self.assertFalse(t.active()) self.assertTrue(success) self.assertTrue(t.exists()) _TASK_HANDLER.clock.advance(timedelay) # make time pass self.assertEqual(self.char1.ndb.dummy_var, False) def test_remove(self): timedelay = self.timedelay for pers in (False, True): t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers) self.assertTrue(t.active()) success = t.remove() self.assertTrue(success) self.assertFalse(t.active()) self.assertFalse(t.exists()) _TASK_HANDLER.clock.advance(timedelay) # make time pass self.assertEqual(self.char1.ndb.dummy_var, False) def test_remove_canceled(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()) success = t.cancel() self.assertTrue(success) 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 t2 = utils.delay(timedelay, dummy_func, self.char1.dbref) 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_manual_stale_task_removal(self): # manual 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 ) _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.tasks) 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() def test_server_restart(self): # 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 self.assertEqual(self.char1.ndb.dummy_var, False) # task has not run _TASK_HANDLER.load() # load persistent tasks from database. _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 self.assertEqual(self.char1.ndb.dummy_var, "dummy_func ran") class TestIntConversions(TestCase): def test_int2str(self): self.assertEqual("three", utils.int2str(3)) # special adjective conversion self.assertEqual("3rd", utils.int2str(3, adjective=True)) # generic adjective conversion self.assertEqual("5th", utils.int2str(5, adjective=True)) # No mapping return int as str self.assertEqual("15", utils.int2str(15)) def test_str2int(self): # simple conversions self.assertEqual(5, utils.str2int("5")) # basic mapped numbers self.assertEqual(3, utils.str2int("three")) self.assertEqual(20, utils.str2int("twenty")) # multi-place numbers self.assertEqual(2345, utils.str2int("two thousand, three hundred and forty-five")) # ordinal numbers self.assertEqual(1, utils.str2int("1st")) self.assertEqual(1, utils.str2int("first")) self.assertEqual(4, utils.str2int("fourth")) # ordinal sound-change conversions self.assertEqual(5, utils.str2int("fifth")) self.assertEqual(20, utils.str2int("twentieth")) with self.assertRaises(ValueError): utils.str2int("not a number") class TestJustify(TestCase): def test_justify_whitespace(self): result = utils.justify(" ", 1, align="l") self.assertEqual(" ", result) result = utils.justify("", 1, align="l") self.assertEqual(" ", result) @parameterized.expand( [ (5, "Task \n ID "), (6, " Task \n ID "), (7, "Task ID"), (8, "Task ID "), (9, " Task ID "), (10, " Task ID "), (11, " Task ID "), ] ) def test_center_justify_small(self, width, expected): result = utils.justify("Task ID", width, align="c", indent=0, fillchar=" ") self.assertEqual(expected, result) def test_justify_ansi(self): """ Justify ansistring """ from evennia.utils.ansi import ANSI_RED line = ANSIString("This is a |rred|n word") result = utils.justify(line, align="c", width=30) self.assertIn(ANSI_RED, str(result)) class TestGroupObjectsByKeyAndDesc(TestCase): """ Test the utils.group_objects_by_key_and_desc function. """ class MockObject: def __init__(self, key, desc): self.key = key self.desc = desc def get_display_name(self, looker, **kwargs): return self.key + f" (looker: {looker.key})" def get_display_desc(self, looker, **kwargs): return self.desc + f" (looker: {looker.key})" def get_numbered_name(self, count, looker, **kwargs): return f"{count} {self.key} (looker: {looker.key})" def __repr__(self): return f"MockObject({self.key}, {self.desc})" def test_group_by_key_and_desc(self): ma1 = self.MockObject("itemA", "descA") ma2 = self.MockObject("itemA", "descA") ma3 = self.MockObject("itemA", "descA") ma4 = self.MockObject("itemA", "descA") mb1 = self.MockObject("itemB", "descB") mb2 = self.MockObject("itemB", "descB") mb3 = self.MockObject("itemB", "descB") me = self.MockObject("Looker", "DescLooker") result = utils.group_objects_by_key_and_desc([ma1, ma2, ma3, ma4, mb1, mb2, mb3], caller=me) self.assertEqual( list(result), [ ("4 itemA (looker: Looker)", "descA (looker: Looker)", [ma1, ma2, ma3, ma4]), ("3 itemB (looker: Looker)", "descB (looker: Looker)", [mb1, mb2, mb3]), ], ) # Create a list of objects class TestMatchIP(TestCase): """ test utils.match_ip """ def test_match_ip(self): self.assertFalse(utils.match_ip("192.168.0.1", "10.0.0.0/24")) self.assertTrue(utils.match_ip("192.168.0.1", "192.168.0.0/24")) self.assertTrue(utils.match_ip("192.168.0.1", "192.168.0.1")) self.assertFalse(utils.match_ip("192.168.0.1", "10.0.0.1"))