mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Implemented a unit testing framework for Evennia. Unfortunately it seems it is only usable in latest Django SVN, due to a Django bug; Run "manage.py test-evennia" - if you get errors about SUPPORTS_TRANSACTIONS, you are affected by the bug. Since this is only likely to affect evennia devs at this point I added a few base tests in src/objects/tests.py as a template for those willing to help add unit tests.
This commit is contained in:
parent
a7899e0119
commit
502ebff1a2
7 changed files with 227 additions and 15 deletions
|
|
@ -92,7 +92,7 @@ class CmdPassword(MuxCommand):
|
|||
oldpass = self.lhslist[0] # this is already stripped by parse()
|
||||
newpass = self.rhslist[0] # ''
|
||||
try:
|
||||
uaccount = caller.user
|
||||
uaccount = caller.player.user
|
||||
except AttributeError:
|
||||
caller.msg("This is only applicable for players.")
|
||||
return
|
||||
|
|
|
|||
|
|
@ -11,11 +11,18 @@ import os
|
|||
# Tack on the root evennia directory to the python path.
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Get Evennia version
|
||||
#------------------------------------------------------------
|
||||
try:
|
||||
VERSION = open("%s%s%s" % (os.pardir, os.sep, 'VERSION')).readline().strip()
|
||||
except IOError:
|
||||
VERSION = "Unknown version"
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Check so session file exists in the current dir- if not, create it.
|
||||
#------------------------------------------------------------
|
||||
|
||||
_CREATED_SETTINGS = False
|
||||
if not os.path.exists('settings.py'):
|
||||
# If settings.py doesn't already exist, create it and populate it with some
|
||||
|
|
@ -88,7 +95,8 @@ from src.settings_default import *
|
|||
|
||||
###################################################
|
||||
# Evennia components (django apps)
|
||||
###################################################"""
|
||||
###################################################
|
||||
"""
|
||||
|
||||
settings_file.write(string)
|
||||
settings_file.close()
|
||||
|
|
@ -97,6 +105,9 @@ from src.settings_default import *
|
|||
Welcome to Evennia (version %s)!
|
||||
We created a fresh settings.py file for you.""" % VERSION
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Test the import of the settings file
|
||||
#------------------------------------------------------------
|
||||
try:
|
||||
from game import settings
|
||||
except Exception:
|
||||
|
|
@ -114,10 +125,36 @@ except Exception:
|
|||
print string
|
||||
sys.exit(1)
|
||||
|
||||
# check required versions
|
||||
#------------------------------------------------------------
|
||||
# Test runner setup
|
||||
#------------------------------------------------------------
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'game.settings'
|
||||
from django.test.simple import DjangoTestSuiteRunner
|
||||
|
||||
class EvenniaTestSuiteRunner(DjangoTestSuiteRunner):
|
||||
"""
|
||||
This test runner only runs tests on the apps specified in src/ and game/ to
|
||||
avoid running the large number of tests defined by Django
|
||||
"""
|
||||
def build_suite(self, test_labels, extra_tests=None, **kwargs):
|
||||
"""
|
||||
Build a test suite for Evennia. test_labels is a list of apps to test.
|
||||
If not given, a subset of settings.INSTALLED_APPS will be used.
|
||||
"""
|
||||
if not test_labels:
|
||||
test_labels = [applabel.rsplit('.', 1)[1] for applabel in settings.INSTALLED_APPS
|
||||
if (applabel.startswith('src.') or applabel.startswith('game.'))]
|
||||
return super(EvenniaTestSuiteRunner, self).build_suite(test_labels, extra_tests=extra_tests, **kwargs)
|
||||
def run_suite(self, test_labels=None, extra_tests=None, **kwargs):
|
||||
"Run wrapper for the tests"
|
||||
return super(EvenniaTestSuiteRunner, self).run_suite(self.build_suite(test_labels, extra_tests), **kwargs)
|
||||
|
||||
#------------------------------------------------------------
|
||||
# This is run only if the module is called as a program
|
||||
#------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
from django.core.management import execute_manager
|
||||
|
||||
# checks if the settings file was created this run
|
||||
if _CREATED_SETTINGS:
|
||||
print """
|
||||
Edit your new settings.py file as needed, then run
|
||||
|
|
@ -125,7 +162,15 @@ if __name__ == "__main__":
|
|||
create the database and your superuser account.
|
||||
"""
|
||||
sys.exit()
|
||||
# run the django setups
|
||||
from src.utils.utils import check_evennia_dependencies
|
||||
|
||||
# running the unit tests
|
||||
if len(sys.argv) > 1 and sys.argv[1] == 'test-evennia':
|
||||
print "Running Evennia-specific test suites ..."
|
||||
EvenniaTestSuiteRunner(sys.argv[2:]).run_suite()
|
||||
sys.exit()
|
||||
|
||||
# run the standard django manager, if dependencies match
|
||||
from src.utils.utils import check_evennia_dependencies
|
||||
if check_evennia_dependencies():
|
||||
from django.core.management import execute_manager
|
||||
execute_manager(settings)
|
||||
|
|
|
|||
|
|
@ -264,9 +264,15 @@ def format_multimatches(caller, matches):
|
|||
|
||||
# Main command-handler function
|
||||
|
||||
def cmdhandler(caller, raw_string, unloggedin=False):
|
||||
def cmdhandler(caller, raw_string, unloggedin=False, testing=False):
|
||||
"""
|
||||
This is the main function to handle any string sent to the engine.
|
||||
|
||||
caller - calling object
|
||||
raw_string - the command string given on the command line
|
||||
unloggedin - if caller is an authenticated user or not
|
||||
testing - if we should actually execute the command or not.
|
||||
if True, the command instance will be returned instead.
|
||||
"""
|
||||
try: # catch bugs in cmdhandler itself
|
||||
try: # catch special-type commands
|
||||
|
|
@ -375,7 +381,11 @@ def cmdhandler(caller, raw_string, unloggedin=False):
|
|||
# we make sure to validate its scripts.
|
||||
cmd.obj.scripts.validate()
|
||||
|
||||
# Parse and execute
|
||||
if testing:
|
||||
# only return the command instance
|
||||
return cmd
|
||||
|
||||
# Parse and execute
|
||||
cmd.parse()
|
||||
cmd.func()
|
||||
# Done!
|
||||
|
|
@ -395,6 +405,10 @@ def cmdhandler(caller, raw_string, unloggedin=False):
|
|||
# cmd.obj is automatically made available.
|
||||
# we make sure to validate its scripts.
|
||||
cmd.obj.scripts.validate()
|
||||
|
||||
if testing:
|
||||
# only return the command instance
|
||||
return syscmd
|
||||
|
||||
# parse and run the command
|
||||
syscmd.parse()
|
||||
|
|
|
|||
153
src/objects/tests.py
Normal file
153
src/objects/tests.py
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Unit testing of the 'objects' Evennia component.
|
||||
|
||||
Runs as part of the Evennia's test suite with 'manage.py test-evennia'.
|
||||
|
||||
Please add new tests to this module as needed.
|
||||
|
||||
Guidelines:
|
||||
A 'test case' is testing a specific component and is defined as a class inheriting from unittest.TestCase.
|
||||
The test case class can have a method setUp() that creates and sets up the testing environment.
|
||||
All methods inside the test case class whose names start with 'test' are used as test methods by the runner.
|
||||
Inside the test methods, special member methods assert*() are used to test the behaviour.
|
||||
"""
|
||||
|
||||
import re, time
|
||||
try:
|
||||
# this is a special optimized Django version, only available in current Django devel
|
||||
from django.utils.unittest import TestCase
|
||||
except ImportError:
|
||||
# if our Django is older we use the normal version
|
||||
# TODO: Switch this to django.test.TestCase when the but has been plugged that gives
|
||||
# traceback when using that module over TransactionTestCase.
|
||||
from django.test import TestCase
|
||||
#from django.test import TransactionTestCase as TestCase
|
||||
from django.conf import settings
|
||||
from src.objects import models, objects
|
||||
from src.utils import create
|
||||
from src.server import session, sessionhandler
|
||||
|
||||
class TestObjAttrs(TestCase):
|
||||
"""
|
||||
Test aspects of ObjAttributes
|
||||
"""
|
||||
def setUp(self):
|
||||
"set up the test"
|
||||
self.attr = models.ObjAttribute()
|
||||
self.obj1 = create.create_object(objects.Object, key="testobj1", location=None)
|
||||
self.obj2 = create.create_object(objects.Object, key="testobj2", location=self.obj1)
|
||||
|
||||
# tests
|
||||
def test_store_str(self):
|
||||
hstring = "sdfv00=97sfjs842 ivfjlQKFos9GF^8dddsöäå-?%"
|
||||
self.obj1.db.testattr = hstring
|
||||
self.assertEqual(hstring, self.obj1.db.testattr)
|
||||
def test_store_obj(self):
|
||||
self.obj1.db.testattr = self.obj2
|
||||
self.assertEqual(self.obj2 ,self.obj1.db.testattr)
|
||||
self.assertEqual(self.obj2.location, self.obj1.db.testattr.location)
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Command testing
|
||||
#------------------------------------------------------------
|
||||
|
||||
# print all feedback from test commands (can become very verbose!)
|
||||
VERBOSE = False
|
||||
|
||||
class FakeSession(session.SessionProtocol):
|
||||
"""
|
||||
A fake session that implements dummy versions of the real thing; this is needed to mimic
|
||||
a logged-in player.
|
||||
"""
|
||||
def connectionMade(self):
|
||||
self.prep_session()
|
||||
sessionhandler.add_session(self)
|
||||
def prep_session(self):
|
||||
self.server, self.address = None, "0.0.0.0"
|
||||
self.name, self.uid = None, None
|
||||
self.logged_in = False
|
||||
self.encoding = "utf-8"
|
||||
self.cmd_last, self.cmd_last_visible, self.cmd_conn_time = time.time(), time.time(), time.time()
|
||||
self.cmd_total = 0
|
||||
def disconnectClient(self):
|
||||
pass
|
||||
def lineReceived(self, raw_string):
|
||||
pass
|
||||
def msg(self, message, markup=True):
|
||||
if VERBOSE:
|
||||
print message
|
||||
|
||||
class TestCommand(TestCase):
|
||||
"""
|
||||
Sets up the basics of testing the default commands and the generic things
|
||||
that should always be present in a command.
|
||||
|
||||
Inherit new tests from this.
|
||||
"""
|
||||
def setUp(self):
|
||||
"sets up the testing environment"
|
||||
self.room1 = create.create_object(settings.BASE_ROOM_TYPECLASS, key="room1")
|
||||
self.room2 = create.create_object(settings.BASE_ROOM_TYPECLASS, key="room2")
|
||||
|
||||
# create a faux player/character for testing.
|
||||
self.char1 = create.create_player("TestingPlayer", "testplayer@test.com", "testpassword", location=self.room1)
|
||||
self.char1.player.user.is_superuser = True
|
||||
sess = FakeSession()
|
||||
sess.connectionMade()
|
||||
sess.login(self.char1.player)
|
||||
|
||||
self.char2 = create.create_object(settings.BASE_CHARACTER_TYPECLASS, key="char2", location=self.room1)
|
||||
self.obj1 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj1", location=self.room1)
|
||||
self.obj2 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj2", location=self.room1)
|
||||
self.exit1 = create.create_object(settings.BASE_EXIT_TYPECLASS, key="exit1", location=self.room1)
|
||||
self.exit2 = create.create_object(settings.BASE_EXIT_TYPECLASS, key="exit2", location=self.room2)
|
||||
|
||||
def get_cmd(self, cmd_class, argument_string=""):
|
||||
"""
|
||||
Obtain a cmd instance from a class and an input string
|
||||
Note: This does not make use of the cmdhandler functionality.
|
||||
"""
|
||||
cmd = cmd_class()
|
||||
cmd.caller = self.char1
|
||||
cmd.cmdstring = cmd_class.key
|
||||
cmd.args = argument_string
|
||||
cmd.cmdset = None
|
||||
cmd.obj = self.char1
|
||||
return cmd
|
||||
|
||||
def execute_cmd(self, raw_string):
|
||||
"""
|
||||
Creates the command through faking a normal command call;
|
||||
This also mangles the input in various ways to test if the command
|
||||
will be fooled.
|
||||
"""
|
||||
test1 = re.sub(r'\s', '', raw_string) # remove all whitespace inside it
|
||||
test2 = "%s/åäö öäö;-:$£@*~^' 'test" % raw_string # inserting weird characters in call
|
||||
test3 = "%s %s" % (raw_string, raw_string) # multiple calls
|
||||
self.char1.execute_cmd(test1)
|
||||
self.char1.execute_cmd(test2)
|
||||
self.char1.execute_cmd(test3)
|
||||
self.char1.execute_cmd(raw_string)
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Default set Command testing
|
||||
#------------------------------------------------------------
|
||||
|
||||
class TestHome(TestCommand):
|
||||
def test_call(self):
|
||||
self.char1.home = self.room2
|
||||
self.execute_cmd("home")
|
||||
self.assertEqual(self.char1.location, self.room2)
|
||||
class TestLook(TestCommand):
|
||||
def test_call(self):
|
||||
self.execute_cmd("look here")
|
||||
class TestPassword(TestCommand):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@password testpassword = newpassword")
|
||||
class TestNick(TestCommand):
|
||||
def test_call(self):
|
||||
self.execute_cmd("nickname testalias = testaliasedstring")
|
||||
self.assertEquals("testaliasedstring", self.char1.nicks.get("testalias", None))
|
||||
|
|
@ -538,7 +538,7 @@ def has_perm(accessing_obj, accessed_obj, lock_type, default_deny=False):
|
|||
if typelist and lock_type in typelist]
|
||||
|
||||
if not locklist or not any(locklist):
|
||||
# No locks; use default security policy
|
||||
# No viable locks; use default security policy
|
||||
return not default_deny
|
||||
|
||||
# we have locks of the right type. Set default flag OR on all that
|
||||
|
|
@ -562,8 +562,7 @@ def has_perm(accessing_obj, accessed_obj, lock_type, default_deny=False):
|
|||
|
||||
# try to add permissions from connected player
|
||||
if hasattr(accessing_obj, 'has_player') and accessing_obj.has_player:
|
||||
# accessing_obj has a valid, connected player. We start with
|
||||
# those permissions.
|
||||
# accessing_obj has a valid, connected player. We start with those permissions.
|
||||
player = accessing_obj.player
|
||||
if player.is_superuser:
|
||||
# superuser always has access
|
||||
|
|
|
|||
|
|
@ -417,7 +417,7 @@ def create_player(name, email, password,
|
|||
new_user = User.objects.create_user(name, email, password)
|
||||
|
||||
# create the associated Player for this User, and tie them together
|
||||
new_player = PlayerDB(db_key=name, user=new_user)
|
||||
new_player = PlayerDB(db_key=name, user=new_user, db_typeclass_path=typeclass)
|
||||
new_player.save()
|
||||
|
||||
# assign mud permissions
|
||||
|
|
|
|||
|
|
@ -133,7 +133,8 @@ def cemit_info(message):
|
|||
infochan = Channel.objects.get_channel(infochan[0])
|
||||
except Exception:
|
||||
return
|
||||
cname = infochan.key
|
||||
cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n')])
|
||||
infochan.msg(cmessage)
|
||||
if infochan:
|
||||
cname = infochan.key
|
||||
cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n')])
|
||||
infochan.msg(cmessage)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue