tests: add interrogate tests for MAKE_PROPERTY et al

This commit is contained in:
rdb 2017-11-06 13:02:23 +01:00
parent 96d237377b
commit b73c627c61

View File

@ -0,0 +1,466 @@
import sys
import pytest
from panda3d import core
from contextlib import contextmanager
@contextmanager
def constant_refcount(var):
""" with block that checks that the Python refcount remains the same. """
rc = sys.getrefcount(var)
yield
assert sys.getrefcount(var) == rc
def test_property():
# This is a property defined by MAKE_PROPERTY.
np = core.PandaNode("")
# Getter
transform = np.get_transform()
assert transform == np.transform
# Setter
new_transform = transform.set_pos((1, 0, 0))
np.transform = new_transform
assert np.transform == new_transform
# Invalid assignments
with pytest.raises(TypeError):
np.transform = None
with pytest.raises(TypeError):
np.transform = "nonsense"
with pytest.raises(TypeError):
del np.transform
def test_property2():
# This is a property defined by MAKE_PROPERTY2, that can be None.
mat = core.Material()
mat.ambient = (1, 0, 0, 1)
assert mat.ambient == (1, 0, 0, 1)
mat.ambient = None
assert mat.ambient is None
with pytest.raises(TypeError):
mat.ambient = "nonsense"
with pytest.raises(TypeError):
del mat.ambient
# The next tests are for MAKE_SEQ_PROPERTY.
@pytest.fixture
def seq_property(*items):
""" Returns a sequence property initialized with the given items. """
# It doesn't matter which property we use; I just happened to pick
# CollisionNode.solids.
cn = core.CollisionNode("")
append = cn.add_solid
for item in items:
append(item)
assert len(cn.solids) == len(items)
return cn.solids
# Arbitrary items we can use as items for the above seq property.
item_a = core.CollisionSphere((0, 0, 0), 1)
item_b = core.CollisionSphere((0, 0, 0), 2)
item_c = core.CollisionSphere((0, 0, 0), 3)
def test_seq_property_empty():
prop = seq_property()
assert not prop
assert len(prop) == 0
with pytest.raises(IndexError):
prop[0]
with pytest.raises(IndexError):
prop[-1]
def test_seq_property_iter():
prop = seq_property(item_a, item_b, item_b)
assert prop
assert len(prop) == 3
assert tuple(prop) == (item_a, item_b, item_b)
assert item_a in prop
assert item_c not in prop
assert None not in prop
def test_seq_property_getitem():
prop = seq_property(item_a, item_b, item_b)
assert prop[0] == item_a
assert prop[1] == item_b
assert prop[2] == item_b
# Reverse index
assert prop[-1] == item_b
assert prop[-2] == item_b
assert prop[-3] == item_a
# Long index
assert prop[1L] == item_b
assert prop[-1L] == item_b
# Out of bounds access
with pytest.raises(IndexError):
prop[-4]
with pytest.raises(IndexError):
prop[3]
with pytest.raises(IndexError):
prop[2**63]
# Invalid index
with pytest.raises(TypeError):
prop[None]
with pytest.raises(TypeError):
prop[item_a]
with pytest.raises(TypeError):
prop["nonsense"]
# Reference count check
i = 1
with constant_refcount(i):
prop[i]
# Make sure it preserves refcount of invalid indices
i = "nonsense"
with constant_refcount(i):
try:
prop[i]
except TypeError:
pass
def test_seq_property_setitem():
prop = seq_property(item_c, item_c, item_c)
prop[0] = item_a
prop[1] = item_b
assert tuple(prop) == (item_a, item_b, item_c)
# Refcount of key and value stays the same?
i = 0
with constant_refcount(i):
with constant_refcount(item_a):
prop[0] = item_a
# Reverse index
prop[-1] = item_a
prop[-2] = item_b
prop[-3] = item_c
assert tuple(prop) == (item_c, item_b, item_a)
# Long index
prop[1L] = item_b
assert prop[1] == item_b
prop[-1L] = item_b
assert prop[-1] == item_b
# Out of bounds access
with pytest.raises(IndexError):
prop[-4] = item_c
with pytest.raises(IndexError):
prop[3] = item_c
with pytest.raises(IndexError):
prop[2**63] = item_c
# Invalid index
with pytest.raises(TypeError):
prop[None] = item_c
with pytest.raises(TypeError):
prop[item_a] = item_c
with pytest.raises(TypeError):
prop["nonsense"] = item_c
# Invalid type
with pytest.raises(TypeError):
prop[0] = None
with pytest.raises(TypeError):
prop[0] = "nonsense"
def test_seq_property_delitem():
prop = seq_property(item_a, item_b, item_c)
# Out of bounds
with pytest.raises(IndexError):
prop[3]
with pytest.raises(IndexError):
prop[-4]
# Positive index
del prop[0]
assert tuple(prop) == (item_b, item_c)
# Negative index
del prop[-2]
assert tuple(prop) == (item_c,)
# Invalid index
with pytest.raises(TypeError):
del prop[None]
def test_seq_property_index():
prop = seq_property(item_a, item_b, item_b)
assert prop.index(item_a) == 0
assert prop.index(item_b) == 1
with pytest.raises(ValueError):
prop.index(item_c)
with pytest.raises(ValueError):
prop.index(None)
with pytest.raises(ValueError):
prop.index("nonsense")
# Refcount is properly decreased
with constant_refcount(item_b):
prop.index(item_b)
with constant_refcount(item_c):
try:
prop.index(item_c)
except ValueError:
pass
nonsense = "nonsense"
with constant_refcount(nonsense):
try:
prop.index(nonsense)
except ValueError:
pass
def test_seq_property_count():
prop = seq_property(item_a, item_b, item_b)
prop.count(item_a) == 1
prop.count(item_b) == 2
prop.count(item_c) == 0
prop.count(None) == 0
prop.count(("nonsense", -3.5)) == 0
# Refcount does not leak
with constant_refcount(item_b):
prop.count(item_b)
nonsense = "nonsense"
with constant_refcount(nonsense):
prop.count(nonsense)
def test_seq_property_clear():
prop = seq_property(item_a, item_b, item_b)
prop.clear()
assert not prop
assert len(prop) == 0
assert tuple(prop) == ()
def test_seq_property_pop():
prop = seq_property(item_a, item_b, item_c, item_b)
# Test out of bounds pop
with pytest.raises(IndexError):
prop.pop(4)
with pytest.raises(IndexError):
prop.pop(-5)
assert prop.pop(1) == item_b
assert prop.pop(-1) == item_b
assert prop.pop() == item_c
assert prop.pop(0) == item_a
# Wrong args
with pytest.raises(TypeError):
prop.pop(0, 0)
# Pop on empty sequence
with pytest.raises(IndexError):
prop.pop()
with pytest.raises(IndexError):
prop.pop(0)
with pytest.raises(IndexError):
prop.pop(-1)
def test_seq_property_remove():
prop = seq_property(item_a, item_b, item_c, item_b)
with constant_refcount(item_b):
prop.remove(item_b)
assert tuple(prop) == (item_a, item_c, item_b)
prop.remove(item_b)
assert tuple(prop) == (item_a, item_c)
with pytest.raises(ValueError):
prop.remove(item_b)
with pytest.raises(ValueError):
prop.remove(None)
with pytest.raises(ValueError):
prop.remove("nonsense")
# The next tests are for MAKE_MAP_PROPERTY.
@pytest.fixture
def map_property(**items):
""" Returns a mapping property initialized with the given values. """
# It doesn't matter which property we use; I just happened to pick
# NodePath.tags.
np = core.NodePath("")
for k, v in items.items():
np.set_tag(k, v)
return np.tags
def test_map_property_getitem():
key = 'key'
value = 'value'
prop = map_property(**{key: value})
with constant_refcount(key):
with constant_refcount(value):
assert prop[key] == value
with pytest.raises(KeyError):
prop['nonsense']
with pytest.raises(TypeError):
prop[None]
def test_map_property_setitem():
key = 'key'
value = 'value'
prop = map_property()
# Setting new key
with constant_refcount(key):
with constant_refcount(value):
prop[key] = value
assert prop[key] == value
# Setting existing key
with constant_refcount(key):
with constant_refcount(value):
prop[key] = value
assert prop[key] == value
# Unknown key
with pytest.raises(TypeError):
prop[None] = value
# Unknown value
with pytest.raises(TypeError):
prop[key] = None
def test_map_property_delitem():
key = 'key'
value = 'value'
prop = map_property(**{key: value})
with constant_refcount(key):
with constant_refcount(value):
del prop[key]
with pytest.raises(KeyError):
assert prop[key]
# Nonexistent key
with pytest.raises(KeyError):
del prop['nonsense']
# Invalid type key
with pytest.raises(TypeError):
del prop[None]
def test_map_property_contains():
prop = map_property(key='value')
assert 'key' in prop
assert None not in prop
assert 'value' not in prop
def test_map_property_get():
key = 'key'
value = 'value'
prop = map_property(**{key: value})
default = 'default'
with constant_refcount(key):
with constant_refcount(default):
assert prop.get(key) == value
with constant_refcount(key):
with constant_refcount(default):
assert prop.get(key, default) == value
assert prop.get('unknown') is None
with constant_refcount(default):
assert prop.get('unknown', default) == default
def test_map_property_pop():
key = 'key'
value = 'value'
prop = map_property(**{key: value})
assert prop.pop('nonsense', None) is None
assert prop.pop('nonsense', 'default') == 'default'
assert prop.pop('key', 'default') == 'value'
assert 'key' not in prop
def test_map_property_setdefault():
prop = map_property(key='value')
# Don't change value of key that already exists
prop.setdefault('key', 'value2')
assert prop['key'] == 'value'
# Change values of nonexistent key
prop.setdefault('key2', 'value2')
assert prop['key2'] == 'value2'
# These should error because you can't set None on this property
with pytest.raises(TypeError):
prop.setdefault('key3', None)
with pytest.raises(TypeError):
prop.setdefault('key3')
def test_map_property_update():
prop = map_property()
# Empty update
prop.update()
# Passing in dictionary
prop.update({'key': 'value'})
# Passing in keywords
prop.update(key2='value2')
# Don't pass in both!
with pytest.raises(TypeError):
prop.update({}, k='v')
assert prop['key'] == 'value'
assert prop['key2'] == 'value2'