Source code for evennia.contrib.rpg.traits.tests

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Unit test module for Trait classes.

"""

from copy import copy

from anything import Something
from evennia.objects.objects import DefaultCharacter
from evennia.utils.test_resources import BaseEvenniaTestCase, EvenniaTest
from mock import MagicMock, patch

from . import traits


class _MockObj:
    def __init__(self):
        self.attributes = MagicMock()
        self.attributes.get = self.get
        self.attributes.add = self.add
        self.dbstore = {}
        self.category = "traits"

    def get(self, key, category=None):
        assert category == self.category
        return self.dbstore.get(key)

    def add(self, key, value, category=None):
        assert category == self.category
        self.dbstore[key] = value


# we want to test the base traits too
_TEST_TRAIT_CLASS_PATHS = [
    "evennia.contrib.rpg.traits.Trait",
    "evennia.contrib.rpg.traits.StaticTrait",
    "evennia.contrib.rpg.traits.CounterTrait",
    "evennia.contrib.rpg.traits.GaugeTrait",
]


class _TraitHandlerBase(BaseEvenniaTestCase):
    "Base for trait tests"

    @patch("evennia.contrib.rpg.traits.traits._TRAIT_CLASS_PATHS", new=_TEST_TRAIT_CLASS_PATHS)
    def setUp(self):
        self.obj = _MockObj()
        self.traithandler = traits.TraitHandler(self.obj)
        self.obj.traits = self.traithandler

    def _get_dbstore(self, key):
        return self.obj.dbstore["traits"][key]


[docs]class TraitHandlerTest(_TraitHandlerBase): """Testing for TraitHandler"""
[docs] def setUp(self): super().setUp() self.traithandler.add("test1", name="Test1", trait_type="trait") self.traithandler.add( "test2", name="Test2", trait_type="trait", value=["foo", {"1": [1, 2, 3]}, 4], )
[docs] def test_add_trait(self): self.assertEqual( self._get_dbstore("test1"), { "name": "Test1", "trait_type": "trait", "value": None, }, ) self.assertEqual( self._get_dbstore("test2"), { "name": "Test2", "trait_type": "trait", "value": ["foo", {"1": [1, 2, 3]}, 4], }, ) self.assertEqual(len(self.traithandler), 2)
[docs] def test_cache(self): """ Cache should not be set until first get """ self.assertEqual(len(self.traithandler._cache), 0) self.traithandler.all() # does not affect cache self.assertEqual(len(self.traithandler._cache), 0) self.traithandler.test1 self.assertEqual(len(self.traithandler._cache), 1) self.traithandler.test2 self.assertEqual(len(self.traithandler._cache), 2)
[docs] def test_setting(self): "Don't allow setting stuff on traithandler" with self.assertRaises(traits.TraitException): self.traithandler.foo = "bar" with self.assertRaises(traits.TraitException): self.traithandler["foo"] = "bar" with self.assertRaises(traits.TraitException): self.traithandler.test1 = "foo"
[docs] def test_getting(self): "Test we are getting data from the dbstore" self.assertEqual( self.traithandler.test1._data, {"name": "Test1", "trait_type": "trait", "value": None} ) self.assertEqual(self.traithandler._cache, Something) self.assertEqual( self.traithandler.test2._data, {"name": "Test2", "trait_type": "trait", "value": ["foo", {"1": [1, 2, 3]}, 4]}, ) self.assertEqual(self.traithandler._cache, Something) self.assertFalse(self.traithandler.get("foo")) self.assertFalse(self.traithandler.bar)
[docs] def test_all(self): "Test all method" self.assertEqual(self.traithandler.all(), ["test1", "test2"])
[docs] def test_remove(self): "Test remove method" self.traithandler.remove("test2") self.assertEqual(len(self.traithandler), 1) self.assertTrue(bool(self.traithandler.get("test1"))) # this populates cache self.assertEqual(len(self.traithandler._cache), 1) with self.assertRaises(traits.TraitException): self.traithandler.remove("foo")
[docs] def test_clear(self): "Test clear method" self.traithandler.clear() self.assertEqual(len(self.traithandler), 0)
[docs] def test_trait_db_connection(self): "Test that updating a trait property actually updates value in db" trait = self.traithandler.test1 self.assertEqual(trait.value, None) trait.value = 10 self.assertEqual(trait.value, 10) self.assertEqual(self.obj.attributes.get("traits", category="traits")["test1"]["value"], 10) trait.value = 20 self.assertEqual(trait.value, 20) self.assertEqual(self.obj.attributes.get("traits", category="traits")["test1"]["value"], 20) del trait.value self.assertEqual( self.obj.attributes.get("traits", category="traits")["test1"]["value"], None )
[docs]class TestTrait(_TraitHandlerBase): """ Test the base Trait class """
[docs] def setUp(self): super().setUp() self.traithandler.add( "test1", name="Test1", trait_type="trait", value="value", extra_val1="xvalue1", extra_val2="xvalue2", ) self.trait = self.traithandler.get("test1")
[docs] def test_init(self): self.assertEqual( self.trait._data, { "name": "Test1", "trait_type": "trait", "value": "value", "extra_val1": "xvalue1", "extra_val2": "xvalue2", }, )
[docs] def test_validate_input__valid(self): """Test valid validation input""" # all data supplied, and extras dat = {"name": "Test", "trait_type": "trait", "value": 10, "extra_val": 1000} expected = copy(dat) # we must break link or return === dat always self.assertEqual(expected, traits.Trait.validate_input(traits.Trait, dat)) # don't supply value, should get default dat = { "name": "Test", "trait_type": "trait", # missing value "extra_val": 1000, } expected = copy(dat) expected["value"] = traits.Trait.default_keys["value"] self.assertEqual(expected, traits.Trait.validate_input(traits.Trait, dat)) # make sure extra values are cleaned if trait accepts no extras dat = { "name": "Test", "trait_type": "trait", "value": 10, "extra_val1": 1000, "extra_val2": "xvalue", } expected = copy(dat) expected.pop("extra_val1") expected.pop("extra_val2") with patch.object(traits.Trait, "allow_extra_properties", False): self.assertEqual(expected, traits.Trait.validate_input(traits.Trait, dat))
[docs] def test_validate_input__fail(self): """Test failing validation""" dat = { # missing name "trait_type": "trait", "value": 10, "extra_val": 1000, } with self.assertRaises(traits.TraitException): traits.Trait.validate_input(traits.Trait, dat) # make value a required key mock_default_keys = {"value": traits.MandatoryTraitKey} with patch.object(traits.Trait, "default_keys", mock_default_keys): dat = { "name": "Trait", "trait_type": "trait", # missing value, now mandatory "extra_val": 1000, } with self.assertRaises(traits.TraitException): traits.Trait.validate_input(traits.Trait, dat)
[docs] def test_trait_getset(self): """Get-set-del operations on trait""" self.assertEqual(self.trait.name, "Test1") self.assertEqual(self.trait["name"], "Test1") self.assertEqual(self.trait.value, "value") self.assertEqual(self.trait["value"], "value") self.assertEqual(self.trait.extra_val1, "xvalue1") self.assertEqual(self.trait["extra_val2"], "xvalue2") self.trait.value = 20 self.assertEqual(self.trait["value"], 20) self.trait["value"] = 20 self.assertEqual(self.trait.value, 20) self.trait.extra_val1 = 100 self.assertEqual(self.trait.extra_val1, 100) # additional properties self.trait.foo = "bar" self.assertEqual(self.trait.foo, "bar") del self.trait.foo with self.assertRaises(KeyError): self.trait["foo"] with self.assertRaises(AttributeError): self.trait.foo del self.trait.extra_val1 with self.assertRaises(AttributeError): self.trait.extra_val1 del self.trait.value # fall back to default self.assertTrue(self.trait.value == traits.Trait.default_keys["value"])
[docs] def test_repr(self): self.assertEqual(repr(self.trait), Something) self.assertEqual(str(self.trait), Something)
[docs]class TestTraitStatic(_TraitHandlerBase): """ Test for static Traits """
[docs] def setUp(self): super().setUp() self.traithandler.add( "test1", name="Test1", trait_type="static", base=1, mod=2, mult=1.0, extra_val1="xvalue1", extra_val2="xvalue2", ) self.trait = self.traithandler.get("test1")
def _get_values(self): return self.trait.base, self.trait.mod, self.trait.mult, self.trait.value
[docs] def test_init(self): self.assertEqual( self._get_dbstore("test1"), { "name": "Test1", "trait_type": "static", "base": 1, "mod": 2, "mult": 1.0, "extra_val1": "xvalue1", "extra_val2": "xvalue2", }, )
[docs] def test_value(self): """value is (base + mod) * mult""" self.assertEqual(self._get_values(), (1, 2, 1.0, 3)) self.trait.base += 4 self.assertEqual(self._get_values(), (5, 2, 1.0, 7)) self.trait.mod -= 1 self.assertEqual(self._get_values(), (5, 1, 1.0, 6)) self.trait.mult += 1.0 self.assertEqual(self._get_values(), (5, 1, 2.0, 12)) self.trait.mult = 0.75 self.assertEqual(self._get_values(), (5, 1, 0.75, 4.5))
[docs] def test_delete(self): """Deleting resets to default.""" self.trait.mult = 2.0 del self.trait.base self.assertEqual(self._get_values(), (0, 2, 2.0, 4)) del self.trait.mult self.assertEqual(self._get_values(), (0, 2, 1.0, 2)) del self.trait.mod self.assertEqual(self._get_values(), (0, 0, 1.0, 0))
[docs]class TestTraitCounter(_TraitHandlerBase): """ Test for counter- Traits """
[docs] def setUp(self): super().setUp() self.traithandler.add( "test1", name="Test1", trait_type="counter", base=1, mod=2, mult=1.0, min=0, max=10, extra_val1="xvalue1", extra_val2="xvalue2", descs={ 0: "range0", 2: "range1", 5: "range2", 7: "range3", }, ) self.trait = self.traithandler.get("test1")
def _get_values(self): """Get (base, mod, mult, value, min, max).""" return ( self.trait.base, self.trait.mod, self.trait.mult, self.trait.value, self.trait.min, self.trait.max, )
[docs] def test_init(self): self.assertEqual( self._get_dbstore("test1"), { "name": "Test1", "trait_type": "counter", "base": 1, "mod": 2, "mult": 1.0, "min": 0, "max": 10, "extra_val1": "xvalue1", "extra_val2": "xvalue2", "descs": { 0: "range0", 2: "range1", 5: "range2", 7: "range3", }, "rate": 0, "ratetarget": None, "last_update": None, }, )
[docs] def test_value(self): """value is (current + mod) * mult, where current defaults to base""" self.assertEqual(self._get_values(), (1, 2, 1.0, 3, 0, 10)) self.trait.base += 4 self.assertEqual(self._get_values(), (5, 2, 1.0, 7, 0, 10)) self.trait.mod -= 1 self.assertEqual(self._get_values(), (5, 1, 1.0, 6, 0, 10)) self.trait.mult += 1.0 self.assertEqual(self._get_values(), (5, 1, 2.0, 10, 0, 10))
[docs] def test_boundaries__minmax(self): """Test range""" # should not exceed min/max values self.trait.base += 20 self.assertEqual(self._get_values(), (8, 2, 1.0, 10, 0, 10)) self.trait.base = 100 self.assertEqual(self._get_values(), (8, 2, 1.0, 10, 0, 10)) self.trait.base -= 40 self.assertEqual(self._get_values(), (-2, 2, 1.0, 0, 0, 10)) self.trait.base = -100 self.assertEqual(self._get_values(), (-2, 2, 1.0, 0, 0, 10))
[docs] def test_boundaries__bigmod(self): """add a big mod""" self.trait.base = 5 self.trait.mod = 100 self.assertEqual(self._get_values(), (5, 5, 1.0, 10, 0, 10)) self.trait.mod = -100 self.assertEqual(self._get_values(), (5, -5, 1.0, 0, 0, 10))
[docs] def test_boundaries__change_boundaries(self): """Change boundaries after base/mod change""" self.trait.base = 5 self.trait.mod = -100 self.trait.min = -20 self.assertEqual(self._get_values(), (5, -5, 1.0, 0, -20, 10)) self.trait.mod -= 100 self.assertEqual(self._get_values(), (5, -25, 1.0, -20, -20, 10)) self.trait.mod = 100 self.trait.max = 20 self.assertEqual(self._get_values(), (5, 5, 1.0, 10, -20, 20)) self.trait.mod = 100 self.assertEqual(self._get_values(), (5, 15, 1.0, 20, -20, 20))
[docs] def test_boundaries__disable(self): """Disable and re-enable boundaries""" self.trait.base = 5 self.trait.mod = 100 self.assertEqual(self._get_values(), (5, 5, 1.0, 10, 0, 10)) del self.trait.max self.assertEqual(self.trait.max, None) del self.trait.min self.assertEqual(self.trait.min, None) self.trait.base = 100 self.assertEqual(self._get_values(), (100, 5, 1.0, 105, None, None)) self.trait.base = -200 self.assertEqual(self._get_values(), (-200, 5, 1.0, -195, None, None)) # re-activate boundaries self.trait.max = 15 self.trait.min = 10 # his is blocked since base+mod is lower self.assertEqual(self._get_values(), (-200, 5, 1.0, -195, -195, 15))
[docs] def test_boundaries__inverse(self): """Set inverse boundaries - limited by base""" self.trait.mod = 0 self.assertEqual(self._get_values(), (1, 0, 1.0, 1, 0, 10)) self.trait.min = 20 # will be set to base self.assertEqual(self._get_values(), (1, 0, 1.0, 1, 1, 10)) self.trait.max = -20 self.assertEqual(self._get_values(), (1, 0, 1.0, 1, 1, 1))
[docs] def test_current(self): """Modifying current value""" self.trait.current = 5 self.assertEqual(self._get_values(), (1, 2, 1.0, 7, 0, 10)) self.trait.current = 10 self.assertEqual(self._get_values(), (1, 2, 1.0, 10, 0, 10)) self.trait.current = 12 self.assertEqual(self._get_values(), (1, 2, 1.0, 10, 0, 10)) self.trait.current = -1 self.assertEqual(self._get_values(), (1, 2, 1.0, 2, 0, 10)) self.trait.current -= 10 self.assertEqual(self._get_values(), (1, 2, 1.0, 2, 0, 10))
[docs] def test_delete(self): """Deleting resets to default.""" del self.trait.base self.assertEqual(self._get_values(), (0, 2, 1.0, 2, 0, 10)) del self.trait.mod self.assertEqual(self._get_values(), (0, 0, 1.0, 0, 0, 10)) del self.trait.min del self.trait.max self.assertEqual(self._get_values(), (0, 0, 1.0, 0, None, None))
[docs] def test_percentage(self): """Test percentage calculation""" self.trait.base = 8 self.trait.mod = 2 self.trait.mult = 1.0 self.trait.min = 0 self.trait.max = 10 self.assertEqual(self.trait.percent(), "100.0%") self.trait.current = 3 self.assertEqual(self.trait.percent(), "50.0%") self.trait.current = 1 self.assertEqual(self.trait.percent(), "30.0%") # have to lower this since max cannot be lowered below base+mod self.trait.mod = 1 self.trait.current = 2 self.trait.max -= 1 self.assertEqual(self.trait.percent(), "33.3%") # open boundary del self.trait.min self.assertEqual(self.trait.percent(), "100.0%")
[docs] def test_descs(self): """Test descriptions""" self.trait.min = -5 self.trait.mod = 0 self.assertEqual(self._get_values(), (1, 0, 1.0, 1, -5, 10)) self.trait.current = -2 self.assertEqual(self.trait.desc(), "range0") self.trait.current = 0 self.assertEqual(self.trait.desc(), "range0") self.trait.current = 1 self.assertEqual(self.trait.desc(), "range1") self.trait.current = 3 self.assertEqual(self.trait.desc(), "range2") self.trait.current = 5 self.assertEqual(self.trait.desc(), "range2") self.trait.current = 9 self.assertEqual(self.trait.desc(), "range3") self.trait.current = 100 self.assertEqual(self.trait.desc(), "range3")
[docs]class TestTraitCounterTimed(_TraitHandlerBase): """ Test for trait with timer component """
[docs] @patch("evennia.contrib.rpg.traits.traits.time", new=MagicMock(return_value=1000)) def setUp(self): super().setUp() self.traithandler.add( "test1", name="Test1", trait_type="counter", base=1, mod=2, mult=1.0, min=0, max=100, extra_val1="xvalue1", extra_val2="xvalue2", descs={ 0: "range0", 2: "range1", 5: "range2", 7: "range3", }, rate=1, ratetarget=None, ) self.trait = self.traithandler.get("test1")
def _get_timer_data(self): return ( self.trait.value, self.trait.current, self.trait.rate, self.trait._data["last_update"], self.trait.ratetarget, )
[docs] @patch("evennia.contrib.rpg.traits.traits.time") def test_timer_rate(self, mock_time): """Test time stepping""" mock_time.return_value = 1000 self.assertEqual(self._get_timer_data(), (3, 1, 1, 1000, None)) mock_time.return_value = 1001 self.assertEqual(self._get_timer_data(), (4, 2, 1, 1001, None)) mock_time.return_value = 1096 self.assertEqual(self._get_timer_data(), (99, 97, 1, 1096, None)) # hit maximum boundary mock_time.return_value = 1120 self.assertEqual(self._get_timer_data(), (100, 98, 1, None, None)) mock_time.return_value = 1200 self.assertEqual(self._get_timer_data(), (100, 98, 1, None, None)) # drop current self.trait.current = 50 self.assertEqual(self._get_timer_data(), (52, 50, 1, 1200, None)) # set a new rate self.trait.rate = 2 mock_time.return_value = 1210 self.assertEqual(self._get_timer_data(), (72, 70, 2, 1210, None)) self.trait.rate = -10 mock_time.return_value = 1214 self.assertEqual(self._get_timer_data(), (32, 30, -10, 1214, None)) mock_time.return_value = 1218 self.assertEqual(self._get_timer_data(), (0, -2, -10, None, None))
[docs] @patch("evennia.contrib.rpg.traits.traits.time") def test_timer_ratetarget(self, mock_time): """test ratetarget""" mock_time.return_value = 1000 self.trait.ratetarget = 60 self.assertEqual(self._get_timer_data(), (3, 1, 1, 1000, 60)) mock_time.return_value = 1056 self.assertEqual(self._get_timer_data(), (59, 57, 1, 1056, 60)) mock_time.return_value = 1057 self.assertEqual(self._get_timer_data(), (60, 58, 1, None, 60)) mock_time.return_value = 1060 self.assertEqual(self._get_timer_data(), (60, 58, 1, None, 60)) self.trait.ratetarget = 70 mock_time.return_value = 1066 self.assertEqual(self._get_timer_data(), (66, 64, 1, 1066, 70)) mock_time.return_value = 1070 self.assertEqual(self._get_timer_data(), (70, 68, 1, None, 70))
[docs]class TestTraitGauge(_TraitHandlerBase):
[docs] def setUp(self): super().setUp() self.traithandler.add( "test1", name="Test1", trait_type="gauge", base=8, # max = (base + mod) * mult mod=2, mult=1.0, extra_val1="xvalue1", extra_val2="xvalue2", descs={ 0: "range0", 2: "range1", 5: "range2", 7: "range3", }, ) self.trait = self.traithandler.get("test1")
def _get_values(self): """Get (base, mod, mult, value, min, max).""" return ( self.trait.base, self.trait.mod, self.trait.mult, self.trait.value, self.trait.min, self.trait.max, )
[docs] def test_init(self): self.assertEqual( self._get_dbstore("test1"), { "name": "Test1", "trait_type": "gauge", "base": 8, "mod": 2, "mult": 1.0, "min": 0, "extra_val1": "xvalue1", "extra_val2": "xvalue2", "descs": { 0: "range0", 2: "range1", 5: "range2", 7: "range3", }, "rate": 0, "ratetarget": None, "last_update": None, }, )
[docs] def test_value(self): """value is current, where current defaults to base + mod""" # current unset - follows base + mod self.assertEqual(self._get_values(), (8, 2, 1.0, 10, 0, 10)) self.trait.base += 4 self.assertEqual(self._get_values(), (12, 2, 1.0, 14, 0, 14)) self.trait.mod -= 1 self.assertEqual(self._get_values(), (12, 1, 1.0, 13, 0, 13)) self.trait.mult += 1.0 self.assertEqual(self._get_values(), (12, 1, 2.0, 26, 0, 26)) # set current, decouple from base + mod self.trait.current = 5 self.assertEqual(self._get_values(), (12, 1, 2.0, 5, 0, 26)) self.trait.mod += 1 self.trait.base -= 4 self.trait.mult -= 1.0 self.assertEqual(self._get_values(), (8, 2, 1.0, 5, 0, 10)) self.trait.min = -100 self.trait.base = -20 self.assertEqual(self._get_values(), (-20, 2, 1.0, -18, -100, -18))
[docs] def test_boundaries__minmax(self): """Test range""" # current unset - tied to base + mod self.trait.base += 20 self.assertEqual(self._get_values(), (28, 2, 1.0, 30, 0, 30)) # set current - decouple from base + mod self.trait.current = 19 self.assertEqual(self._get_values(), (28, 2, 1.0, 19, 0, 30)) # test upper bound self.trait.current = 100 self.assertEqual(self._get_values(), (28, 2, 1.0, 30, 0, 30)) # with multiplier self.trait.mult = 2.0 self.assertEqual(self._get_values(), (28, 2, 2.0, 30, 0, 60)) self.trait.current = 100 self.assertEqual(self._get_values(), (28, 2, 2.0, 60, 0, 60)) # min defaults to 0 self.trait.mult = 1.0 self.trait.current = -10 self.assertEqual(self._get_values(), (28, 2, 1.0, 0, 0, 30)) self.trait.min = -20 self.assertEqual(self._get_values(), (28, 2, 1.0, 0, -20, 30)) self.trait.current = -10 self.assertEqual(self._get_values(), (28, 2, 1.0, -10, -20, 30))
[docs] def test_boundaries__bigmod(self): """add a big mod""" self.trait.base = 5 self.trait.mod = 100 self.assertEqual(self._get_values(), (5, 100, 1.0, 105, 0, 105)) # restricted by min self.trait.mod = -100 self.assertEqual(self._get_values(), (5, -5, 1.0, 0, 0, 0)) self.trait.min = -200 self.assertEqual(self._get_values(), (5, -5, 1.0, 0, -200, 0))
[docs] def test_boundaries__change_boundaries(self): """Change boundaries after current change""" self.trait.current = 20 self.assertEqual(self._get_values(), (8, 2, 1.0, 10, 0, 10)) self.trait.mod = 102 self.assertEqual(self._get_values(), (8, 102, 1.0, 10, 0, 110)) # raising min past current value will force it upwards self.trait.min = 20 self.assertEqual(self._get_values(), (8, 102, 1.0, 20, 20, 110))
[docs] def test_boundaries__disable(self): """Disable and re-enable boundary""" self.trait.base = 5 self.trait.min = 1 self.assertEqual(self._get_values(), (5, 2, 1.0, 7, 1, 7)) del self.trait.min self.assertEqual(self._get_values(), (5, 2, 1.0, 7, 0, 7)) del self.trait.base del self.trait.mod self.assertEqual(self._get_values(), (0, 0, 1.0, 0, 0, 0)) with self.assertRaises(traits.TraitException): del self.trait.max
[docs] def test_boundaries__inverse(self): """Try to set reversed boundaries""" self.trait.mod = 0 self.trait.base = -10 # limited by min self.assertEqual(self._get_values(), (0, 0, 1.0, 0, 0, 0)) self.trait.min = -10 self.assertEqual(self._get_values(), (0, 0, 1.0, 0, -10, 0)) self.trait.base = -10 self.assertEqual(self._get_values(), (-10, 0, 1.0, -10, -10, -10)) self.min = 0 # limited by base + mod self.assertEqual(self._get_values(), (-10, 0, 1.0, -10, -10, -10))
[docs] def test_current(self): """Modifying current value""" self.trait.base = 10 self.trait.current = 5 self.assertEqual(self._get_values(), (10, 2, 1.0, 5, 0, 12)) self.trait.current = 10 self.assertEqual(self._get_values(), (10, 2, 1.0, 10, 0, 12)) self.trait.current = 12 self.assertEqual(self._get_values(), (10, 2, 1.0, 12, 0, 12)) self.trait.current = 0 self.assertEqual(self._get_values(), (10, 2, 1.0, 0, 0, 12)) self.trait.current = -1 self.assertEqual(self._get_values(), (10, 2, 1.0, 0, 0, 12))
[docs] def test_delete(self): """Deleting resets to default.""" del self.trait.mod self.assertEqual(self._get_values(), (8, 0, 1.0, 8, 0, 8)) self.trait.mod = 2 del self.trait.base self.assertEqual(self._get_values(), (0, 2, 1.0, 2, 0, 2)) del self.trait.min self.assertEqual(self._get_values(), (0, 2, 1.0, 2, 0, 2)) self.trait.min = -10 self.assertEqual(self._get_values(), (0, 2, 1.0, 2, -10, 2)) del self.trait.min self.assertEqual(self._get_values(), (0, 2, 1.0, 2, 0, 2))
[docs] def test_percentage(self): """Test percentage calculation""" self.assertEqual(self.trait.percent(), "100.0%") self.trait.current = 5 self.assertEqual(self.trait.percent(), "50.0%") self.trait.current = 3 self.assertEqual(self.trait.percent(), "30.0%") self.trait.mod -= 1 self.assertEqual(self.trait.percent(), "33.3%")
[docs] def test_descs(self): """Test descriptions""" self.trait.min = -5 self.assertEqual(self._get_values(), (8, 2, 1.0, 10, -5, 10)) self.trait.current = -2 self.assertEqual(self.trait.desc(), "range0") self.trait.current = 0 self.assertEqual(self.trait.desc(), "range0") self.trait.current = 1 self.assertEqual(self.trait.desc(), "range1") self.trait.current = 3 self.assertEqual(self.trait.desc(), "range2") self.trait.current = 5 self.assertEqual(self.trait.desc(), "range2") self.trait.current = 9 self.assertEqual(self.trait.desc(), "range3") self.trait.current = 100 self.assertEqual(self.trait.desc(), "range3")
[docs]class TestTraitGaugeTimed(_TraitHandlerBase): """ Test for trait with timer component """
[docs] @patch("evennia.contrib.rpg.traits.traits.time", new=MagicMock(return_value=1000)) def setUp(self): super().setUp() self.traithandler.add( "test1", name="Test1", trait_type="gauge", base=98, mod=2, min=0, extra_val1="xvalue1", extra_val2="xvalue2", descs={ 0: "range0", 2: "range1", 5: "range2", 7: "range3", }, rate=1, ratetarget=None, ) self.trait = self.traithandler.get("test1")
def _get_timer_data(self): return ( self.trait.value, self.trait.current, self.trait.rate, self.trait._data["last_update"], self.trait.ratetarget, )
[docs] @patch("evennia.contrib.rpg.traits.traits.time") def test_timer_rate(self, mock_time): """Test time stepping""" mock_time.return_value = 1000 self.trait.current = 1 self.assertEqual(self._get_timer_data(), (1, 1, 1, 1000, None)) mock_time.return_value = 1001 self.assertEqual(self._get_timer_data(), (2, 2, 1, 1001, None)) mock_time.return_value = 1096 self.assertEqual(self._get_timer_data(), (97, 97, 1, 1096, None)) # hit maximum boundary mock_time.return_value = 1120 self.assertEqual(self._get_timer_data(), (100, 100, 1, None, None)) mock_time.return_value = 1200 self.assertEqual(self._get_timer_data(), (100, 100, 1, None, None)) # drop current self.trait.current = 50 self.assertEqual(self._get_timer_data(), (50, 50, 1, 1200, None)) # set a new rate self.trait.rate = 2 mock_time.return_value = 1210 self.assertEqual(self._get_timer_data(), (70, 70, 2, 1210, None)) self.trait.rate = -10 mock_time.return_value = 1214 self.assertEqual(self._get_timer_data(), (30, 30, -10, 1214, None)) mock_time.return_value = 1218 self.assertEqual(self._get_timer_data(), (0, 0, -10, None, None))
[docs] @patch("evennia.contrib.rpg.traits.traits.time") def test_timer_ratetarget(self, mock_time): """test ratetarget""" mock_time.return_value = 1000 self.trait.current = 1 self.trait.ratetarget = 60 self.assertEqual(self._get_timer_data(), (1, 1, 1, 1000, 60)) mock_time.return_value = 1056 self.assertEqual(self._get_timer_data(), (57, 57, 1, 1056, 60)) mock_time.return_value = 1059 self.assertEqual(self._get_timer_data(), (60, 60, 1, None, 60)) mock_time.return_value = 1060 self.assertEqual(self._get_timer_data(), (60, 60, 1, None, 60)) self.trait.ratetarget = 70 mock_time.return_value = 1066 self.assertEqual(self._get_timer_data(), (66, 66, 1, 1066, 70)) mock_time.return_value = 1070 self.assertEqual(self._get_timer_data(), (70, 70, 1, None, 70))
[docs]class TestNumericTraitOperators(BaseEvenniaTestCase): """Test case for numeric magic method implementations."""
[docs] def setUp(self): # direct instantiation for testing only; use TraitHandler in production self.st = traits.Trait( { "name": "Strength", "trait_type": "trait", "value": 8, } ) self.at = traits.Trait( { "name": "Attack", "trait_type": "trait", "value": 4, } )
[docs] def tearDown(self): self.st, self.at = None, None
[docs] def test_pos_shortcut(self): """overridden unary + operator returns `value` property""" self.assertIn(type(+self.st), (float, int)) self.assertEqual(+self.st, self.st.value) self.assertEqual(+self.st, 8)
[docs] def test_add_traits(self): """test addition of `Trait` objects""" # two Trait objects self.assertEqual(self.st + self.at, 12) # Trait and numeric self.assertEqual(self.st + 1, 9) self.assertEqual(1 + self.st, 9)
[docs] def test_sub_traits(self): """test subtraction of `Trait` objects""" # two Trait objects self.assertEqual(self.st - self.at, 4) # Trait and numeric self.assertEqual(self.st - 1, 7) self.assertEqual(10 - self.st, 2)
[docs] def test_mul_traits(self): """test multiplication of `Trait` objects""" # between two Traits self.assertEqual(self.st * self.at, 32) # between Trait and numeric self.assertEqual(self.at * 4, 16) self.assertEqual(4 * self.at, 16)
[docs] def test_floordiv(self): """test floor division of `Trait` objects""" # between two Traits self.assertEqual(self.st // self.at, 2) # between Trait and numeric self.assertEqual(self.st // 2, 4) self.assertEqual(18 // self.st, 2)
[docs] def test_comparisons_traits(self): """test equality comparison between `Trait` objects""" self.assertNotEqual(self.st, self.at) self.assertLess(self.at, self.st) self.assertLessEqual(self.at, self.st) self.assertGreater(self.st, self.at) self.assertGreaterEqual(self.st, self.at)
[docs] def test_comparisons_numeric(self): """equality comparisons between `Trait` and numeric""" self.assertEqual(self.st, 8) self.assertEqual(8, self.st) self.assertNotEqual(self.st, 0) self.assertNotEqual(0, self.st) self.assertLess(self.st, 10) self.assertLess(0, self.st) self.assertLessEqual(self.st, 8) self.assertLessEqual(8, self.st) self.assertLessEqual(self.st, 10) self.assertLessEqual(0, self.st) self.assertGreater(self.st, 0) self.assertGreater(10, self.st) self.assertGreaterEqual(self.st, 8) self.assertGreaterEqual(8, self.st) self.assertGreaterEqual(self.st, 0) self.assertGreaterEqual(10, self.st)
[docs]class DummyCharacter(_MockObj): strength = traits.TraitProperty("Strength", trait_type="static", base=10, mod=2) hunting = traits.TraitProperty("Hunting skill", trait_type="counter", base=10, mod=1, max=100) health = traits.TraitProperty("Health value", trait_type="gauge", base=100)
[docs]class TestTraitFields(BaseEvenniaTestCase): """ Test the TraitField class. """
[docs] @patch("evennia.contrib.rpg.traits.traits._TRAIT_CLASS_PATHS", new=_TEST_TRAIT_CLASS_PATHS) def test_traitfields(self): obj = DummyCharacter() obj2 = DummyCharacter() self.assertEqual(12, obj.strength.value) self.assertEqual(11, obj.hunting.value) self.assertEqual(100, obj.health.value) obj.strength.base += 5 self.assertEqual(17, obj.strength.value) obj.strength.berserk = True self.assertEqual(obj.strength.berserk, True) self.assertEqual(100, obj.traits.health) self.assertEqual(None, obj.traits.hp) # the traithandler still works obj.traits.health.current -= 1 self.assertEqual(99, obj.health.value) # making sure Descriptors are separate self.assertEqual(12, obj2.strength.value) self.assertEqual(17, obj.strength.value) obj2.strength.base += 1 obj.strength.base += 3 self.assertEqual(13, obj2.strength.value) self.assertEqual(20, obj.strength.value)
[docs]class TraitContribTestingChar(DefaultCharacter): HP = traits.TraitProperty("health", trait_type="trait", value=5)
[docs]class TraitPropertyTestCase(EvenniaTest): """ Test atomic updating. """ character_typeclass = TraitContribTestingChar
[docs] def test_round1(self): self.char1.HP.value = 1
[docs] def test_round2(self): self.char1.HP.value = 2