Add unittests for contrib/chargen and custom_gametime. Removed unused time units settings from custom_gametime.

This commit is contained in:
Griatch 2017-02-19 13:10:17 +01:00
parent 7e395ea7ec
commit 46a5a62d2b
4 changed files with 137 additions and 96 deletions

View file

@ -2,32 +2,25 @@
Contribution - Griatch 2011 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 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 This is a simple character creation commandset for the Player level.
test this together with menu_login, which doesn't create a Character It shows some more info and gives the Player the option to create a
on its own. This shows some more info and gives the Player the option character without any more customizations than their name (further
to create a character without any more customizations than their name options are unique for each game anyway).
(further options are unique for each game anyway).
Since this extends the OOC cmdset, logging in from the menu will In MULTISESSION_MODEs 0 and 1, you will automatically log into an
automatically drop the Player into this cmdset unless they logged off existing Character. When using `@ooc` you will then end up in this
while puppeting a Character already before. cmdset.
Installation: Installation:
Read the instructions in contrib/examples/cmdset.py in order to create Import this module to `mygame/commands/default_cmdsets.py` and
a new default cmdset module for Evennia to use (copy the template up add `chargen.OOCCMdSetCharGen` to the `PlayerCmdSet` class
one level, and change the settings file's relevant variables to point (it says where to add it). Reload.
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)
""" """
@ -179,7 +172,7 @@ class CmdOOCCharacterCreate(Command):
else: else:
avail_chars = [new_character.id] avail_chars = [new_character.id]
self.caller.db._character_dbrefs = avail_chars 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): class OOCCmdSetCharGen(default_cmds.PlayerCmdSet):

View file

@ -12,14 +12,23 @@ Usage:
Use as the normal gametime module, that is by importing and using the 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 helper functions in this module in your own code. The calendar can be
specified in your settings file by adding and setting custom values customized by adding the `TIME_UNITS` dictionary to your settings
for one or more of the variables `TIME_SECS_PER_MIN`, file. This maps unit names to their length, expressed in the smallest
`TIME_MINS_PER_HOUR`, `TIME_DAYS_PER_WEEK`, `TIME_WEEKS_PER_MONTH` and unit. Here's the default as an example:
`TIME_MONTHS_PER_YEAR`. These are all given in seconds and whereas
they are called "week", "month" etc these names could represent TIME_UNITS = {
whatever fits your game. You can also set `TIME_UNITS` to a dict "sec": 1,
mapping the name of a unit to its length in seconds (like `{"min": "min": 60,
60, ...}. If not given, sane defaults will be used. "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 django.conf import settings
from evennia import DefaultScript from evennia import DefaultScript
from evennia.utils.create import create_script 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 # The game time speedup / slowdown relative real time
TIMEFACTOR = settings.TIME_FACTOR TIMEFACTOR = settings.TIME_FACTOR
# Game-time units, in game time seconds. These are supplied as a # These are the unit names understood by the scheduler.
# convenient measure for determining the current in-game time, e.g. # Each unit must be consistent and expressed in seconds.
# 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.
UNITS = getattr(settings, "TIME_UNITS", { UNITS = getattr(settings, "TIME_UNITS", {
"sec": SEC, # default custom calendar
"min": MIN, "sec": 1,
"hr": HOUR, "min": 60,
"hour": HOUR, "hr": 60 * 60,
"day": DAY, "hour": 60 * 60,
"week": WEEK, "day": 60 * 60 * 24,
"month": MONTH, "week": 60 * 60 * 24 * 7,
"year": YEAR, "month": 60 * 60 * 24 * 7 * 4,
"yr": YEAR, "yr": 60 * 60 * 24 * 7 * 4 * 12,
}) "year": 60 * 60 * 24 * 7 * 4 * 12, })
def time_to_tuple(seconds, *divisors): def time_to_tuple(seconds, *divisors):
@ -92,7 +91,8 @@ def gametime_to_realtime(format=False, **kwargs):
Kwargs: Kwargs:
format (bool): Formatting the output. 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: Returns:
time (float or tuple): The realtime difference or the same time (float or tuple): The realtime difference or the same
@ -163,7 +163,7 @@ def custom_gametime(absolute=False):
week, day, hour, minute, second). week, day, hour, minute, second).
""" """
current = gametime(absolute=absolute) current = gametime.gametime(absolute=absolute)
units = sorted(set(UNITS.values()), reverse=True) units = sorted(set(UNITS.values()), reverse=True)
del units[-1] del units[-1]
return time_to_tuple(current, *units) 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. 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) units = sorted(set(UNITS.values()), reverse=True)
# Remove seconds from the tuple # Remove seconds from the tuple
del units[-1] del units[-1]
@ -232,25 +232,26 @@ def schedule(callback, repeat=False, **kwargs):
""" """
Call the callback when the game time is up. 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: Args:
callback (function): the callback function that will be called [1]. callback (function): The callback function that will be called. This
repeat (bool, optional): should the callback be called regularly? must be a top-level function since the script will be persistent.
times (str: int): the time to call the callback. repeat (bool, optional): Should the callback be called regularly?
day, month, etc (str: int): The time units to call the callback; should
[1] The callback must be a top-level function, since the script will match the keys of TIME_UNITS.
be persistent.
Returns: 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) seconds = real_seconds_until(**kwargs)

View file

@ -68,6 +68,7 @@ Installation/testing:
""" """
from __future__ import division from __future__ import division
import datetime
import re import re
from django.conf import settings from django.conf import settings
from evennia import DefaultRoom from evennia import DefaultRoom
@ -129,12 +130,12 @@ class ExtendedRoom(DefaultRoom):
""" """
Calculate the current time and season ids. Calculate the current time and season ids.
""" """
# get the current time as parts of year and parts of day # get the current time as parts of year and parts of day.
# returns a tuple (years,months,weeks,days,hours,minutes,sec) # we assume a standard calendar here and use 24h format.
time = gametime.gametime(format=True) timestamp = gametime.gametime(absolute=True)
month, hour = time[1], time[4] datestamp = datetime.datetime.fromtimestamp(timestamp)
season = float(month) / MONTHS_PER_YEAR season = float(datestamp.month) / MONTHS_PER_YEAR
timeslot = float(hour) / HOURS_PER_DAY timeslot = float(datestamp.hour) / HOURS_PER_DAY
# figure out which slots these represent # figure out which slots these represent
if SEASONAL_BOUNDARIES[0] <= season < SEASONAL_BOUNDARIES[1]: if SEASONAL_BOUNDARIES[0] <= season < SEASONAL_BOUNDARIES[1]:

View file

@ -3,7 +3,6 @@ Testing suite for contrib folder
""" """
from django.conf import settings
from evennia.commands.default.tests import CommandTest from evennia.commands.default.tests import CommandTest
from evennia.utils.test_resources import EvenniaTest from evennia.utils.test_resources import EvenniaTest
from mock import Mock from mock import Mock
@ -172,40 +171,36 @@ from evennia.contrib import extended_room
from evennia import gametime from evennia import gametime
from evennia.objects.objects import DefaultRoom 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): class TestExtendedRoom(CommandTest):
room_typeclass = extended_room.ExtendedRoom room_typeclass = extended_room.ExtendedRoom
DETAIL_DESC = "A test detail." DETAIL_DESC = "A test detail."
SUMMER_DESC = "A summer description." SPRING_DESC = "A spring description."
OLD_DESC = "Old description." OLD_DESC = "Old description."
def setUp(self): def setUp(self):
super(TestExtendedRoom, self).setUp() super(TestExtendedRoom, self).setUp()
self.room1.ndb.last_timeslot = "night" self.room1.ndb.last_timeslot = "afternoon"
self.room1.ndb.last_season = "winter" self.room1.ndb.last_season = "winter"
self.room1.db.details = {'testdetail': self.DETAIL_DESC} 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 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): def test_return_appearance(self):
# get the appearance of a non-extended room for contrast purposes # get the appearance of a non-extended room for contrast purposes
old_desc = DefaultRoom.return_appearance(self.room1, self.char1) old_desc = DefaultRoom.return_appearance(self.room1, self.char1)
# the new appearance should be the old one, but with the desc switched # 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(old_desc.replace(self.OLD_DESC, self.SPRING_DESC),
self.assertEqual("summer", self.room1.ndb.last_season) self.room1.return_appearance(self.char1))
self.assertEqual("morning", self.room1.ndb.last_timeslot) self.assertEqual("spring", self.room1.ndb.last_season)
self.assertEqual("evening", self.room1.ndb.last_timeslot)
def test_return_detail(self): def test_return_detail(self):
self.assertEqual(self.DETAIL_DESC, self.room1.return_detail("testdetail")) self.assertEqual(self.DETAIL_DESC, self.room1.return_detail("testdetail"))
def test_cmdextendedlook(self): 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(), "testdetail", self.DETAIL_DESC)
self.call(extended_room.CmdExtendedLook(), "nonexistent", "Could not find 'nonexistent'.") 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:") self.call(extended_room.CmdExtendedDesc(), "", "Descriptions on Room:")
def test_cmdgametime(self): 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 # Test the contrib barter system
from evennia import create_object
from evennia.contrib import barter from evennia.contrib import barter
class TestBarter(CommandTest): class TestBarter(CommandTest):
def setUp(self): def setUp(self):
@ -309,11 +302,11 @@ class TestBarter(CommandTest):
self.call(barter.CmdTradeHelp(), "", "Trading commands\n", caller=self.char1) 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.]") 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.contrib import wilderness
from evennia import DefaultCharacter from evennia import DefaultCharacter
class TestWilderness(EvenniaTest): class TestWilderness(EvenniaTest):
def setUp(self): def setUp(self):
@ -429,3 +422,56 @@ class TestWilderness(EvenniaTest):
for direction, correct_loc in directions.iteritems(): # Not compatible with Python 3 for direction, correct_loc in directions.iteritems(): # Not compatible with Python 3
new_loc = wilderness.get_new_coordinates(loc, direction) new_loc = wilderness.get_new_coordinates(loc, direction)
self.assertEquals(new_loc, correct_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)