Browse Source

tests: add interrogate tests for MAKE_PROPERTY et al

rdb 8 years ago
parent
commit
b73c627c61
1 changed files with 466 additions and 0 deletions
  1. 466 0
      tests/interrogate/test_property.py

+ 466 - 0
tests/interrogate/test_property.py

@@ -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.
[email protected]
+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.
[email protected]
+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'