From 46a5a62d2b8a1499ea4ad29124337f9a08290ce4 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 19 Feb 2017 13:10:17 +0100 Subject: [PATCH] Add unittests for contrib/chargen and custom_gametime. Removed unused time units settings from custom_gametime. --- evennia/contrib/chargen.py | 35 ++++------ evennia/contrib/custom_gametime.py | 101 +++++++++++++++-------------- evennia/contrib/extended_room.py | 13 ++-- evennia/contrib/tests.py | 84 ++++++++++++++++++------ 4 files changed, 137 insertions(+), 96 deletions(-) diff --git a/evennia/contrib/chargen.py b/evennia/contrib/chargen.py index a80a338b0c..915c96cd3a 100644 --- a/evennia/contrib/chargen.py +++ b/evennia/contrib/chargen.py @@ -2,32 +2,25 @@ Contribution - Griatch 2011 -[Note - with the advent of MULTISESSION_MODE=2, this is not really as +> Note - with the advent of MULTISESSION_MODE=2, this is not really as necessary anymore - the ooclook and @charcreate commands in that mode -replaces this module with better functionality.] +replaces this module with better functionality. This remains here for +inspiration. -This is a simple character creation commandset. A suggestion is to -test this together with menu_login, which doesn't create a Character -on its own. This shows some more info and gives the Player the option -to create a character without any more customizations than their name -(further options are unique for each game anyway). +This is a simple character creation commandset for the Player level. +It shows some more info and gives the Player the option to create a +character without any more customizations than their name (further +options are unique for each game anyway). -Since this extends the OOC cmdset, logging in from the menu will -automatically drop the Player into this cmdset unless they logged off -while puppeting a Character already before. +In MULTISESSION_MODEs 0 and 1, you will automatically log into an +existing Character. When using `@ooc` you will then end up in this +cmdset. Installation: -Read the instructions in contrib/examples/cmdset.py in order to create -a new default cmdset module for Evennia to use (copy the template up -one level, and change the settings file's relevant variables to point -to the cmdsets inside). If you already have such a module you should -of course use that. - -Next import this module in your custom cmdset module and add the -following line to the end of OOCCmdSet's at_cmdset_creation(): - - self.add(chargen.OOCCmdSetCharGen) +Import this module to `mygame/commands/default_cmdsets.py` and +add `chargen.OOCCMdSetCharGen` to the `PlayerCmdSet` class +(it says where to add it). Reload. """ @@ -179,7 +172,7 @@ class CmdOOCCharacterCreate(Command): else: avail_chars = [new_character.id] self.caller.db._character_dbrefs = avail_chars - self.caller.msg("|gThe Character |c%s|g was successfully created!" % charname) + self.caller.msg("|gThe character |c%s|g was successfully created!" % charname) class OOCCmdSetCharGen(default_cmds.PlayerCmdSet): diff --git a/evennia/contrib/custom_gametime.py b/evennia/contrib/custom_gametime.py index 15ab9696ad..199e6b6f87 100644 --- a/evennia/contrib/custom_gametime.py +++ b/evennia/contrib/custom_gametime.py @@ -12,14 +12,23 @@ Usage: Use as the normal gametime module, that is by importing and using the helper functions in this module in your own code. The calendar can be -specified in your settings file by adding and setting custom values -for one or more of the variables `TIME_SECS_PER_MIN`, -`TIME_MINS_PER_HOUR`, `TIME_DAYS_PER_WEEK`, `TIME_WEEKS_PER_MONTH` and -`TIME_MONTHS_PER_YEAR`. These are all given in seconds and whereas -they are called "week", "month" etc these names could represent -whatever fits your game. You can also set `TIME_UNITS` to a dict -mapping the name of a unit to its length in seconds (like `{"min": -60, ...}. If not given, sane defaults will be used. +customized by adding the `TIME_UNITS` dictionary to your settings +file. This maps unit names to their length, expressed in the smallest +unit. Here's the default as an example: + + TIME_UNITS = { + "sec": 1, + "min": 60, + "hr": 60 * 60, + "hour": 60 * 60, + "day": 60 * 60 * 24, + "week": 60 * 60 * 24 * 7, + "month": 60 * 60 * 24 * 7 * 4, + "yr": 60 * 60 * 24 * 7 * 4 * 12, + "year": 60 * 60 * 24 * 7 * 4 * 12, } + +When using a custom calendar, these time unit names are used as kwargs to +the converter functions in this module. """ @@ -28,33 +37,23 @@ mapping the name of a unit to its length in seconds (like `{"min": from django.conf import settings from evennia import DefaultScript from evennia.utils.create import create_script -from evennia.utils.gametime import gametime +from evennia.utils import gametime # The game time speedup / slowdown relative real time TIMEFACTOR = settings.TIME_FACTOR -# Game-time units, in game time seconds. These are supplied as a -# convenient measure for determining the current in-game time, e.g. -# when defining in-game events. The words month, week and year can be -# used to mean whatever units of time are used in your game. -SEC = 1 -MIN = getattr(settings, "TIME_SECS_PER_MIN", 60) -HOUR = getattr(settings, "TIME_MINS_PER_HOUR", 60) * MIN -DAY = getattr(settings, "TIME_HOURS_PER_DAY", 24) * HOUR -WEEK = getattr(settings, "TIME_DAYS_PER_WEEK", 7) * DAY -MONTH = getattr(settings, "TIME_WEEKS_PER_MONTH", 4) * WEEK -YEAR = getattr(settings, "TIME_MONTHS_PER_YEAR", 12) * MONTH -# these are the unit names understood by the scheduler. +# These are the unit names understood by the scheduler. +# Each unit must be consistent and expressed in seconds. UNITS = getattr(settings, "TIME_UNITS", { - "sec": SEC, - "min": MIN, - "hr": HOUR, - "hour": HOUR, - "day": DAY, - "week": WEEK, - "month": MONTH, - "year": YEAR, - "yr": YEAR, -}) + # default custom calendar + "sec": 1, + "min": 60, + "hr": 60 * 60, + "hour": 60 * 60, + "day": 60 * 60 * 24, + "week": 60 * 60 * 24 * 7, + "month": 60 * 60 * 24 * 7 * 4, + "yr": 60 * 60 * 24 * 7 * 4 * 12, + "year": 60 * 60 * 24 * 7 * 4 * 12, }) def time_to_tuple(seconds, *divisors): @@ -92,7 +91,8 @@ def gametime_to_realtime(format=False, **kwargs): Kwargs: format (bool): Formatting the output. - times (int): The various components of the time (must match UNITS). + days, month etc (int): These are the names of time units that must + match the `settings.TIME_UNITS` dict keys. Returns: time (float or tuple): The realtime difference or the same @@ -163,7 +163,7 @@ def custom_gametime(absolute=False): week, day, hour, minute, second). """ - current = gametime(absolute=absolute) + current = gametime.gametime(absolute=absolute) units = sorted(set(UNITS.values()), reverse=True) del units[-1] return time_to_tuple(current, *units) @@ -186,7 +186,7 @@ def real_seconds_until(**kwargs): The number of real seconds before the given game time is up. """ - current = gametime(absolute=True) + current = gametime.gametime(absolute=True) units = sorted(set(UNITS.values()), reverse=True) # Remove seconds from the tuple del units[-1] @@ -232,25 +232,26 @@ def schedule(callback, repeat=False, **kwargs): """ Call the callback when the game time is up. - This function will setup a script that will be called when the - time corresponds to the game time. If the game is stopped for - more than a few seconds, the callback may be called with a slight - delay. If `repeat` is set to True, the callback will be called - again next time the game time matches the given time. The time - is given in units as keyword arguments. For instance: - >>> schedule(func, min=5, sec=0) # Will call next hour at :05. - >>> schedule(func, hour=2, min=30, sec=0) # Will call the next day at 02:30. - Args: - callback (function): the callback function that will be called [1]. - repeat (bool, optional): should the callback be called regularly? - times (str: int): the time to call the callback. - - [1] The callback must be a top-level function, since the script will - be persistent. + callback (function): The callback function that will be called. This + must be a top-level function since the script will be persistent. + repeat (bool, optional): Should the callback be called regularly? + day, month, etc (str: int): The time units to call the callback; should + match the keys of TIME_UNITS. Returns: - The created script (Script). + script (Script): The created script. + + Examples: + schedule(func, min=5, sec=0) # Will call next hour at :05. + schedule(func, hour=2, min=30, sec=0) # Will call the next day at 02:30. + Notes: + This function will setup a script that will be called when the + time corresponds to the game time. If the game is stopped for + more than a few seconds, the callback may be called with a + slight delay. If `repeat` is set to True, the callback will be + called again next time the game time matches the given time. + The time is given in units as keyword arguments. """ seconds = real_seconds_until(**kwargs) diff --git a/evennia/contrib/extended_room.py b/evennia/contrib/extended_room.py index 6cacfeb5b2..e5a8fdd934 100644 --- a/evennia/contrib/extended_room.py +++ b/evennia/contrib/extended_room.py @@ -68,6 +68,7 @@ Installation/testing: """ from __future__ import division +import datetime import re from django.conf import settings from evennia import DefaultRoom @@ -129,12 +130,12 @@ class ExtendedRoom(DefaultRoom): """ Calculate the current time and season ids. """ - # get the current time as parts of year and parts of day - # returns a tuple (years,months,weeks,days,hours,minutes,sec) - time = gametime.gametime(format=True) - month, hour = time[1], time[4] - season = float(month) / MONTHS_PER_YEAR - timeslot = float(hour) / HOURS_PER_DAY + # get the current time as parts of year and parts of day. + # we assume a standard calendar here and use 24h format. + timestamp = gametime.gametime(absolute=True) + datestamp = datetime.datetime.fromtimestamp(timestamp) + season = float(datestamp.month) / MONTHS_PER_YEAR + timeslot = float(datestamp.hour) / HOURS_PER_DAY # figure out which slots these represent if SEASONAL_BOUNDARIES[0] <= season < SEASONAL_BOUNDARIES[1]: diff --git a/evennia/contrib/tests.py b/evennia/contrib/tests.py index 96e8a4e385..455bffc54d 100644 --- a/evennia/contrib/tests.py +++ b/evennia/contrib/tests.py @@ -3,7 +3,6 @@ Testing suite for contrib folder """ -from django.conf import settings from evennia.commands.default.tests import CommandTest from evennia.utils.test_resources import EvenniaTest from mock import Mock @@ -172,40 +171,36 @@ from evennia.contrib import extended_room from evennia import gametime from evennia.objects.objects import DefaultRoom -# mock gametime to return 7th month, 10 in morning -gametime.gametime = Mock(return_value=(None, 7, None, None, 10)) -# mock settings so we're not affected by a given server's hours of day/months in year -settings.TIME_MONTH_PER_YEAR = 12 -settings.TIME_HOUR_PER_DAY = 24 - - class TestExtendedRoom(CommandTest): room_typeclass = extended_room.ExtendedRoom DETAIL_DESC = "A test detail." - SUMMER_DESC = "A summer description." + SPRING_DESC = "A spring description." OLD_DESC = "Old description." def setUp(self): super(TestExtendedRoom, self).setUp() - self.room1.ndb.last_timeslot = "night" + self.room1.ndb.last_timeslot = "afternoon" self.room1.ndb.last_season = "winter" self.room1.db.details = {'testdetail': self.DETAIL_DESC} - self.room1.db.summer_desc = self.SUMMER_DESC + self.room1.db.spring_desc = self.SPRING_DESC self.room1.db.desc = self.OLD_DESC + # mock gametime to return 7th month, 10 in morning + gametime.gametime = Mock(return_value=2975000766) # spring evening def test_return_appearance(self): # get the appearance of a non-extended room for contrast purposes old_desc = DefaultRoom.return_appearance(self.room1, self.char1) # the new appearance should be the old one, but with the desc switched - self.assertEqual(old_desc.replace(self.OLD_DESC, self.SUMMER_DESC), self.room1.return_appearance(self.char1)) - self.assertEqual("summer", self.room1.ndb.last_season) - self.assertEqual("morning", self.room1.ndb.last_timeslot) + self.assertEqual(old_desc.replace(self.OLD_DESC, self.SPRING_DESC), + self.room1.return_appearance(self.char1)) + self.assertEqual("spring", self.room1.ndb.last_season) + self.assertEqual("evening", self.room1.ndb.last_timeslot) def test_return_detail(self): self.assertEqual(self.DETAIL_DESC, self.room1.return_detail("testdetail")) def test_cmdextendedlook(self): - self.call(extended_room.CmdExtendedLook(), "here", "Room(#1)\n%s" % self.SUMMER_DESC) + self.call(extended_room.CmdExtendedLook(), "here", "Room(#1)\n%s" % self.SPRING_DESC) self.call(extended_room.CmdExtendedLook(), "testdetail", self.DETAIL_DESC) self.call(extended_room.CmdExtendedLook(), "nonexistent", "Could not find 'nonexistent'.") @@ -220,15 +215,13 @@ class TestExtendedRoom(CommandTest): self.call(extended_room.CmdExtendedDesc(), "", "Descriptions on Room:") def test_cmdgametime(self): - self.call(extended_room.CmdGameTime(), "", "It's a summer day, in the morning.") + self.call(extended_room.CmdGameTime(), "", "It's a spring day, in the evening.") # Test the contrib barter system -from evennia import create_object from evennia.contrib import barter - class TestBarter(CommandTest): def setUp(self): @@ -309,11 +302,11 @@ class TestBarter(CommandTest): self.call(barter.CmdTradeHelp(), "", "Trading commands\n", caller=self.char1) self.call(barter.CmdFinish(), ": Ending.", "You say, \"Ending.\"\n [You aborted trade. No deal was made.]") +# Test wilderness from evennia.contrib import wilderness from evennia import DefaultCharacter - class TestWilderness(EvenniaTest): def setUp(self): @@ -429,3 +422,56 @@ class TestWilderness(EvenniaTest): for direction, correct_loc in directions.iteritems(): # Not compatible with Python 3 new_loc = wilderness.get_new_coordinates(loc, direction) self.assertEquals(new_loc, correct_loc, direction) + +# Testing chargen contrib +from evennia.contrib import chargen + +class TestChargen(CommandTest): + + def test_ooclook(self): + self.call(chargen.CmdOOCLook(), "foo", "You have no characters to look at", caller=self.player) + self.call(chargen.CmdOOCLook(), "", "You, TestPlayer, are an OOC ghost without form.", caller=self.player) + + def test_charcreate(self): + self.call(chargen.CmdOOCCharacterCreate(), "testchar", "The character testchar was successfully created!", caller=self.player) + self.call(chargen.CmdOOCCharacterCreate(), "testchar", "Character testchar already exists.", caller=self.player) + self.assertTrue(self.player.db._character_dbrefs) + self.call(chargen.CmdOOCLook(), "", "You, TestPlayer, are an OOC ghost without form.",caller=self.player) + self.call(chargen.CmdOOCLook(), "testchar", "testchar(", caller=self.player) + +# Testing custom_gametime +from evennia.contrib import custom_gametime + +def _testcallback(): + pass + +class TestCustomGameTime(EvenniaTest): + def setUp(self): + super(TestCustomGameTime, self).setUp() + gametime.gametime = Mock(return_value=2975000898.46) # does not seem to work + def tearDown(self): + if hasattr(self, "timescript"): + self.timescript.stop() + def test_time_to_tuple(self): + self.assertEqual(custom_gametime.time_to_tuple(10000, 34,2,4,6,1), (294, 2, 0, 0, 0, 0)) + self.assertEqual(custom_gametime.time_to_tuple(10000, 3,3,4), (3333, 0, 0, 1)) + self.assertEqual(custom_gametime.time_to_tuple(100000, 239,24,3), (418, 4, 0, 2)) + def test_gametime_to_realtime(self): + self.assertEqual(custom_gametime.gametime_to_realtime(days=2, mins=4), 86520.0) + self.assertEqual(custom_gametime.gametime_to_realtime(format=True, days=2), (0,0,0,1,0,0,0)) + def test_realtime_to_gametime(self): + self.assertEqual(custom_gametime.realtime_to_gametime(days=2, mins=34), 349680.0) + self.assertEqual(custom_gametime.realtime_to_gametime(days=2, mins=34, format=True), (0, 0, 0, 4, 1, 8, 0)) + self.assertEqual(custom_gametime.realtime_to_gametime(format=True, days=2, mins=4), (0, 0, 0, 4, 0, 8, 0)) + def test_custom_gametime(self): + self.assertEqual(custom_gametime.custom_gametime(), (102, 5, 2, 6, 21, 8, 18)) + self.assertEqual(custom_gametime.custom_gametime(absolute=True), (102, 5, 2, 6, 21, 8, 18)) + def test_real_seconds_until(self): + self.assertEqual(custom_gametime.real_seconds_until(year=2300, month=11, day=6), 31911667199.77) + def test_schedule(self): + self.timescript = custom_gametime.schedule(_testcallback, repeat=True, min=5, sec=0) + self.assertEqual(self.timescript.interval, 1700.7699999809265) + + + +