Fix unit tests. Make TagCategoryProperty return tags set by other means.
This commit is contained in:
parent
615b98c171
commit
f78d4abcdd
8 changed files with 399 additions and 147 deletions
|
|
@ -35,9 +35,14 @@ class TestExtendedRoom(EvenniaTestCase):
|
|||
base_room_desc = "Base room description."
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.room = create_object(extended_room.ExtendedRoom, key="Test Room")
|
||||
self.room.desc = self.base_room_desc
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
self.room.delete()
|
||||
|
||||
def test_room_description(self):
|
||||
"""
|
||||
Test that the vanilla room description is returned as expected.
|
||||
|
|
@ -88,7 +93,8 @@ class TestExtendedRoom(EvenniaTestCase):
|
|||
"$state(night, Night room description.)"
|
||||
" What a great day!"
|
||||
)
|
||||
room_desc = self.room.get_display_desc(None)
|
||||
char = Mock()
|
||||
room_desc = self.room.get_display_desc(char)
|
||||
self.assertEqual(room_desc, f"{desc} What a great day!")
|
||||
|
||||
def test_room_states(self):
|
||||
|
|
@ -102,18 +108,19 @@ class TestExtendedRoom(EvenniaTestCase):
|
|||
)
|
||||
self.room.add_room_state("under_construction")
|
||||
self.assertEqual(self.room.room_states, ["under_construction"])
|
||||
self.assertEqual(self.room.get_display_desc(None), "This room is under construction. ")
|
||||
char = Mock()
|
||||
self.assertEqual(self.room.get_display_desc(char), "This room is under construction. ")
|
||||
|
||||
self.room.add_room_state("under_repair")
|
||||
self.assertEqual(self.room.room_states, ["under_construction", "under_repair"])
|
||||
self.assertEqual(
|
||||
self.room.get_display_desc(None),
|
||||
self.room.get_display_desc(char),
|
||||
"This room is under construction. This room is under repair.",
|
||||
)
|
||||
|
||||
self.room.remove_room_state("under_construction")
|
||||
self.assertEqual(
|
||||
self.room.get_display_desc(None),
|
||||
self.room.get_display_desc(char),
|
||||
" This room is under repair.",
|
||||
)
|
||||
|
||||
|
|
@ -122,6 +129,10 @@ class TestExtendedRoom(EvenniaTestCase):
|
|||
Test rooms with alternate descriptions.
|
||||
|
||||
"""
|
||||
from evennia import ObjectDB
|
||||
|
||||
ObjectDB.objects.all() # TODO - fixes an issue with home FK missing
|
||||
|
||||
self.room.add_desc("The room is burning!", room_state="burning")
|
||||
self.room.add_desc("The room is flooding!", room_state="flooding")
|
||||
self.assertEqual(self.room.get_display_desc(None), self.base_room_desc)
|
||||
|
|
|
|||
|
|
@ -354,6 +354,54 @@ class TestProperties(EvenniaTestCase):
|
|||
self.assertTrue(obj.tags.has("category_tag2", category="tagcategory2"))
|
||||
self.assertTrue(obj.tags.has("category_tag3", category="tagcategory2"))
|
||||
|
||||
self.assertEqual(obj.tagcategory1, ["category_tag1"])
|
||||
self.assertEqual(
|
||||
set(obj.tagcategory2), set(["category_tag1", "category_tag2", "category_tag3"])
|
||||
)
|
||||
|
||||
def test_tag_category_properties_external_modification(self):
|
||||
obj = self.obj
|
||||
|
||||
self.assertEqual(obj.tagcategory1, ["category_tag1"])
|
||||
self.assertEqual(
|
||||
set(obj.tagcategory2), set(["category_tag1", "category_tag2", "category_tag3"])
|
||||
)
|
||||
|
||||
# add extra tag to category
|
||||
obj.tags.add("category_tag2", category="tagcategory1")
|
||||
self.assertEqual(
|
||||
obj.tags.get(category="tagcategory1"),
|
||||
["category_tag1", "category_tag2"],
|
||||
)
|
||||
self.assertEqual(set(obj.tagcategory1), set(["category_tag1", "category_tag2"]))
|
||||
|
||||
# add/remove extra tags to category
|
||||
obj.tags.add("category_tag4", category="tagcategory2")
|
||||
obj.tags.remove("category_tag3", category="tagcategory2")
|
||||
self.assertEqual(
|
||||
set(obj.tags.get(category="tagcategory2", return_list=True)),
|
||||
set(["category_tag1", "category_tag2", "category_tag4"]),
|
||||
)
|
||||
# note that when we access the property again, it will be updated to contain the same tags
|
||||
self.assertEqual(
|
||||
set(obj.tagcategory2),
|
||||
set(["category_tag1", "category_tag2", "category_tag3", "category_tag4"]),
|
||||
)
|
||||
|
||||
del obj.tagcategory1
|
||||
# should be deleted from database
|
||||
self.assertEqual(obj.tags.get(category="tagcategory1", return_list=True), [])
|
||||
# accessing the property should return the default value
|
||||
self.assertEqual(obj.tagcategory1, ["category_tag1"])
|
||||
|
||||
del obj.tagcategory2
|
||||
# should be deleted from database
|
||||
self.assertEqual(obj.tags.get(category="tagcategory2", return_list=True), [])
|
||||
# accessing the property should return the default value
|
||||
self.assertEqual(
|
||||
set(obj.tagcategory2), set(["category_tag1", "category_tag2", "category_tag3"])
|
||||
)
|
||||
|
||||
def test_object_awareness(self):
|
||||
"""Test the "object-awareness" of customized AttributeProperty getter/setters"""
|
||||
obj = self.obj
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ class TagCategoryProperty:
|
|||
|
||||
taghandler_name = "tags"
|
||||
|
||||
def __init__(self, *args):
|
||||
def __init__(self, *default_tags):
|
||||
"""
|
||||
Assign a property for a Tag Category, with any number of Tag keys.
|
||||
This is often more useful than the `TagProperty` since it's common to want to check which
|
||||
|
|
@ -185,8 +185,12 @@ class TagCategoryProperty:
|
|||
|
||||
Args:
|
||||
*args (str or callable): Tag keys to assign to this property, using the category given
|
||||
by the name of the property. If a callable, it will be called without arguments
|
||||
to return the tag key. It is not possible to set tag `data` this way (use the
|
||||
by the name of the property. Note that, if these tags are always set on the object,
|
||||
if they are removed by some other means, they will be re-added when this property
|
||||
is accessed. Furthermore, changing this list after the object was created, will
|
||||
not remove any old tags (there is no way for the property to know if the
|
||||
new list is new or not). If a callable, it will be called without arguments to
|
||||
return the tag key. It is not possible to set tag `data` this way (use the
|
||||
Taghandler directly for that). Tag keys are not case sensitive.
|
||||
|
||||
Raises:
|
||||
|
|
@ -204,7 +208,7 @@ class TagCategoryProperty:
|
|||
|
||||
"""
|
||||
self._category = ""
|
||||
self._tags = self._parse_tag_input(*args)
|
||||
self._default_tags = self._parse_tag_input(*default_tags)
|
||||
|
||||
def _parse_tag_input(self, *args):
|
||||
"""
|
||||
|
|
@ -240,56 +244,43 @@ class TagCategoryProperty:
|
|||
"""
|
||||
taghandler = getattr(instance, self.taghandler_name)
|
||||
|
||||
tags = []
|
||||
add_new = []
|
||||
for tagkey in self._tags:
|
||||
try:
|
||||
tag = taghandler.get(
|
||||
key=tagkey, category=self._category, return_list=False, raise_exception=True
|
||||
)
|
||||
except AttributeError:
|
||||
add_new.append(tagkey)
|
||||
else:
|
||||
tags.append(tag)
|
||||
if add_new:
|
||||
for new_tag in add_new:
|
||||
# we must remove this from the internal store or system will think it already
|
||||
# existed when determining the sets in __set__
|
||||
self._tags.remove(new_tag)
|
||||
self.__set__(instance, *add_new)
|
||||
default_tags = self._default_tags
|
||||
tags = taghandler.get(category=self._category, return_list=True)
|
||||
|
||||
missing_default_tags = set(default_tags) - set(tags)
|
||||
|
||||
if missing_default_tags:
|
||||
getattr(instance, self.taghandler_name).batch_add(
|
||||
*[(tag, self._category) for tag in missing_default_tags]
|
||||
)
|
||||
|
||||
tags += missing_default_tags # to avoid a second db call
|
||||
|
||||
return tags
|
||||
|
||||
def __set__(self, instance, *args):
|
||||
"""
|
||||
Assign a new set of tags to the category. This replaces the previous set of tags.
|
||||
Assign a new set of tags to the category. Note that we can't know if previous
|
||||
tags were assigned from this property or from TagHandler, so we don't
|
||||
remove old tags. To refresh to only have the tags in this constructor, first
|
||||
use `del` on this property and re-access the property with the changed default list.
|
||||
|
||||
"""
|
||||
taghandler = getattr(instance, self.taghandler_name)
|
||||
|
||||
old_tags = set(self._tags)
|
||||
new_tags = set(self._parse_tag_input(*args))
|
||||
|
||||
# new_tags could be a sub/superset of old tags
|
||||
removed_tags = old_tags - new_tags
|
||||
added_tags = new_tags - old_tags
|
||||
|
||||
# remove tags
|
||||
for tag in removed_tags:
|
||||
taghandler.remove(key=tag, category=self._category)
|
||||
|
||||
# add new tags (won't re-add if obj already had it)
|
||||
taghandler.batch_add(*[(tag, self._category) for tag in added_tags])
|
||||
getattr(instance, self.taghandler_name).batch_add(*[(tag, self._category) for tag in args])
|
||||
|
||||
def __delete__(self, instance):
|
||||
"""
|
||||
Called when running `del` on the property. Will remove all tags of this
|
||||
category from the object. Note that the tags will be readded on next fetch
|
||||
unless the TagCategoryProperty is also removed in code!
|
||||
category from the object. Note that next time this desriptor is accessed, the
|
||||
default ones will be re-added!
|
||||
|
||||
Note:
|
||||
This will remove _all_ tags of this category from the object. This is necessary
|
||||
in order to be able to be able to combine this with `__set__` to get a tag
|
||||
list where property and handler are in sync.
|
||||
|
||||
"""
|
||||
for tagkey in self.tags:
|
||||
getattr(instance, self.taghandler_name).remove(key=self.tagkey, category=self._category)
|
||||
getattr(instance, self.taghandler_name).remove(category=self._category)
|
||||
|
||||
|
||||
class TagHandler(object):
|
||||
|
|
@ -726,7 +717,7 @@ class TagHandler(object):
|
|||
for tup in args:
|
||||
tup = make_iter(tup)
|
||||
nlen = len(tup)
|
||||
if nlen == 1: # just a key
|
||||
if nlen == 1: # just a key, no category
|
||||
keys[None].append(tup[0])
|
||||
elif nlen == 2:
|
||||
keys[tup[1]].append(tup[0])
|
||||
|
|
@ -736,6 +727,27 @@ class TagHandler(object):
|
|||
for category, key in keys.items():
|
||||
self.add(key=key, category=category, data=data.get(category, None))
|
||||
|
||||
def batch_remove(self, *args):
|
||||
"""
|
||||
Batch-remove tags from a list of tuples.
|
||||
|
||||
Args:
|
||||
*args (tuple or str): Each argument should be a `tagstr` keys or tuple
|
||||
`(keystr, category)` or `(keystr, category, data)` (the `data` field is ignored,
|
||||
only `keystr`/`category` matters). It's possible to mix input types.
|
||||
|
||||
"""
|
||||
keys = defaultdict(list)
|
||||
for tup in args:
|
||||
tup = make_iter(tup)
|
||||
nlen = len(tup)
|
||||
if nlen == 1: # just a key, no category
|
||||
keys[None].append(tup[0])
|
||||
elif nlen > 1:
|
||||
keys[tup[1]].append(tup[0])
|
||||
for category, key in keys.items():
|
||||
self.remove(key=key, category=category, data=data.get(category, None))
|
||||
|
||||
def __str__(self):
|
||||
return ",".join(self.all())
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue