| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- from panda3d.core import PandaNode, TransformState
- from contextlib import contextmanager
- import pytest
- import gc
- @contextmanager
- def gc_disabled():
- gc.disable()
- gc.collect()
- gc.freeze()
- gc.set_debug(gc.DEBUG_SAVEALL)
- gc.garbage.clear()
- try:
- yield
- finally:
- gc.set_debug(0)
- gc.garbage.clear()
- gc.unfreeze()
- gc.collect()
- gc.enable()
- def test_node_prev_transform():
- identity = TransformState.make_identity()
- t1 = TransformState.make_pos((1, 0, 0))
- t2 = TransformState.make_pos((2, 0, 0))
- t3 = TransformState.make_pos((3, 0, 0))
- node = PandaNode("node")
- assert node.transform == identity
- assert node.prev_transform == identity
- assert not node.has_dirty_prev_transform()
- node.transform = t1
- assert node.transform == t1
- assert node.prev_transform == identity
- assert node.has_dirty_prev_transform()
- node.transform = t2
- assert node.transform == t2
- assert node.prev_transform == identity
- assert node.has_dirty_prev_transform()
- node.reset_prev_transform()
- assert node.transform == t2
- assert node.prev_transform == t2
- assert not node.has_dirty_prev_transform()
- node.transform = t3
- assert node.prev_transform == t2
- assert node.has_dirty_prev_transform()
- PandaNode.reset_all_prev_transform()
- assert node.transform == t3
- assert node.prev_transform == t3
- assert not node.has_dirty_prev_transform()
- def test_node_tag_cycle():
- with gc_disabled():
- node = PandaNode('test_node_tag_cycle')
- node.set_python_tag('self', node)
- node.set_python_tag('self2', node)
- assert gc.is_tracked(node)
- node = None
- gc.collect()
- assert len(gc.garbage) > 0
- for g in gc.garbage:
- if isinstance(g, PandaNode) and g.name == 'test_node_tag_cycle':
- break
- else:
- assert False
- @pytest.mark.xfail
- def test_node_tag_cycle_multiple_wrappers():
- # Doesn't yet work since the traverse checks that the refcount is 1.
- with gc_disabled():
- node = PandaNode('test_node_tag_cycle_multiple_wrappers')
- node.set_python_tag('self', node)
- # Find another reference to the same node, we do this by temporarily
- # attaching a child.
- child = PandaNode('child')
- node.add_child(child)
- node2 = child.get_parent(0)
- node.remove_child(0)
- child = None
- assert node2 == node
- assert node2 is not node
- assert node2.this == node.this
- node.set_python_tag('self2', node2)
- node = None
- node2 = None
- gc.collect()
- assert len(gc.garbage) > 0
- for g in gc.garbage:
- if isinstance(g, PandaNode) and g.name == 'test_node_tag_cycle_multiple_wrappers':
- break
- else:
- pytest.fail('not found in garbage')
- def test_node_subclass_persistent():
- class NodeSubclass(PandaNode):
- pass
- node = NodeSubclass('test_node_subclass')
- assert isinstance(node, PandaNode)
- # Always GC-tracked
- assert gc.is_tracked(node)
- # Registered type handle
- type_handle = node.get_type()
- assert type_handle.name == 'NodeSubclass'
- assert type_handle != PandaNode.get_class_type()
- assert tuple(type_handle.parent_classes) == (PandaNode.get_class_type(), )
- # Persistent wrapper
- parent = PandaNode('parent')
- parent.add_child(node)
- child = parent.get_child(0)
- assert child.this == node.this
- assert child is node
- assert type(child) is NodeSubclass
- parent = None
- child = None
- def test_node_subclass_gc():
- class NodeSubclass(PandaNode):
- pass
- # Python wrapper destructs last
- with gc_disabled():
- node = NodeSubclass('test_node_subclass_gc1')
- assert node in gc.get_objects()
- node = None
- gc.collect()
- assert len(gc.garbage) == 1
- assert gc.garbage[0].name == 'test_node_subclass_gc1'
- # C++ reference destructs last
- with gc_disabled():
- node = NodeSubclass('test_node_subclass_gc2')
- parent = PandaNode('parent')
- parent.add_child(node)
- assert node in gc.get_objects()
- node = None
- # Hasn't been collected yet, since parent still holds a reference
- gc.collect()
- assert len(gc.garbage) == 0
- assert 'test_node_subclass_gc2' in [obj.name for obj in gc.get_objects() if isinstance(obj, PandaNode)]
- parent = None
- # Destructed without needing the GC
- assert 'test_node_subclass_gc2' not in [obj.name for obj in gc.get_objects() if isinstance(obj, PandaNode)]
- gc.collect()
- assert len(gc.garbage) == 0
|