177 lines
4.6 KiB
Python
177 lines
4.6 KiB
Python
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
|