mirror of
https://github.com/Relintai/gdnative_python.git
synced 2025-01-08 15:29:39 +01:00
288 lines
8.4 KiB
Python
288 lines
8.4 KiB
Python
import pytest
|
|
from math import inf
|
|
from struct import unpack
|
|
|
|
import pandemonium
|
|
from pandemonium import (
|
|
Vector3,
|
|
GDString,
|
|
NodePath,
|
|
Object,
|
|
Node,
|
|
CanvasItem,
|
|
Node2D,
|
|
PluginScript,
|
|
OpenSimplexNoise,
|
|
OS,
|
|
Error,
|
|
OK,
|
|
exposed,
|
|
)
|
|
|
|
|
|
@exposed
|
|
class virtualtestbedcls(Node):
|
|
def _to_string(self):
|
|
# Implemented for test_bindings::test_virtual_to_string_customize
|
|
return GDString("<Main Node>")
|
|
|
|
def _notification(self, what):
|
|
on_notification = getattr(self, "on_notification", None)
|
|
if on_notification:
|
|
on_notification(what)
|
|
|
|
|
|
def test_free_node():
|
|
v = Node.new()
|
|
v.free()
|
|
# `check_memory_leak` auto fixture will do the bookkeeping
|
|
|
|
|
|
def test_expose_contains_constant():
|
|
assert "OK" in dir(pandemonium)
|
|
assert OK is not None
|
|
|
|
|
|
def test_expose_contains_class():
|
|
assert "Node" in dir(pandemonium)
|
|
assert Node is not None
|
|
|
|
|
|
def test_expose_contains_builtins():
|
|
assert "Vector3" in dir(pandemonium)
|
|
assert Vector3 is not None
|
|
|
|
|
|
def test_call_one_arg_short(current_node):
|
|
with pytest.raises(TypeError) as exc:
|
|
current_node.get_child()
|
|
assert str(exc.value) == "get_child() takes exactly one argument (0 given)"
|
|
|
|
|
|
def test_call_too_few_args(current_node):
|
|
with pytest.raises(TypeError) as exc:
|
|
current_node.move_child()
|
|
assert str(exc.value) == "move_child() takes exactly 2 positional arguments (0 given)"
|
|
|
|
|
|
def test_call_with_defaults_and_too_few_args(current_node):
|
|
with pytest.raises(TypeError) as exc:
|
|
current_node.add_child()
|
|
assert str(exc.value) == "add_child() takes at least 1 positional argument (0 given)"
|
|
|
|
|
|
def test_call_none_in_base_type_args(current_node):
|
|
with pytest.raises(TypeError) as exc:
|
|
# signature: def get_child(self, pandemonium_int idx)
|
|
current_node.get_child(None)
|
|
assert str(exc.value) == "an integer is required"
|
|
|
|
|
|
def test_call_none_in_builtin_args(current_node):
|
|
with pytest.raises(TypeError) as exc:
|
|
# signature: def get_node(self, NodePath path not None)
|
|
current_node.get_node(None)
|
|
assert str(exc.value) == "Invalid value None, must be str or NodePath"
|
|
|
|
|
|
def test_call_none_in_bindings_args(current_node):
|
|
with pytest.raises(TypeError) as exc:
|
|
# signature: def get_path_to(self, Node node not None)
|
|
current_node.get_path_to(None)
|
|
assert (
|
|
str(exc.value)
|
|
== "Argument 'node' has incorrect type (expected pandemonium.bindings.Node, got NoneType)"
|
|
)
|
|
|
|
|
|
def test_call_too_many_args(current_node):
|
|
with pytest.raises(TypeError) as exc:
|
|
current_node.get_child(1, 2)
|
|
assert str(exc.value) == "get_child() takes exactly one argument (2 given)"
|
|
|
|
|
|
def test_call_with_default_and_too_many_args(current_node):
|
|
with pytest.raises(TypeError) as exc:
|
|
current_node.add_child(1, 2, 3)
|
|
assert str(exc.value) == "add_child() takes at most 2 positional arguments (3 given)"
|
|
|
|
|
|
def test_call_with_defaults(generate_obj):
|
|
node = generate_obj(Node)
|
|
child = generate_obj(Node)
|
|
# signature: void add_child(Node node, bool legible_unique_name=false)
|
|
node.add_child(child)
|
|
|
|
# legible_unique_name is False by default, check name is not human-redable
|
|
children_names = [str(x.name) for x in node.get_children()]
|
|
assert children_names == ["@@2"]
|
|
|
|
|
|
def test_call_returns_enum(generate_obj):
|
|
node = generate_obj(Node)
|
|
ret = node.connect("foo", node, "bar")
|
|
assert isinstance(ret, Error)
|
|
|
|
|
|
def test_call_with_kwargs(generate_obj):
|
|
node = generate_obj(Node)
|
|
child = generate_obj(Node)
|
|
new_child = generate_obj(Node)
|
|
|
|
node.add_child(child, legible_unique_name=True)
|
|
# Check name is readable
|
|
children_names = [str(x.name) for x in node.get_children()]
|
|
assert children_names == ["Node"]
|
|
|
|
# Kwargs are passed out of order
|
|
node.add_child_below_node(legible_unique_name=True, child_node=new_child, node=node)
|
|
# Check names are still readable
|
|
children_names = [str(x.name) for x in node.get_children()]
|
|
assert children_names == ["Node", "Node2"]
|
|
|
|
|
|
def test_inheritance(generate_obj):
|
|
node = generate_obj(Node)
|
|
|
|
# CanvasItem is a direct subclass of Node
|
|
canvas_item = generate_obj(CanvasItem)
|
|
assert isinstance(node, Object)
|
|
assert isinstance(canvas_item, Object)
|
|
assert isinstance(canvas_item, Node)
|
|
|
|
# TODO: headless server end up with a static memory leak
|
|
# when instanciating a Node2D...
|
|
if not OS.has_feature("Server"):
|
|
# Node2D is a grand child subclass of Node
|
|
node2d = generate_obj(Node2D)
|
|
assert isinstance(node, Object)
|
|
assert isinstance(node2d, Object)
|
|
assert isinstance(node2d, Node)
|
|
|
|
|
|
def test_call_with_refcounted_return_value(current_node):
|
|
script = current_node.get_script()
|
|
assert isinstance(script, PluginScript)
|
|
|
|
|
|
def test_call_with_refcounted_param_value(generate_obj):
|
|
node = generate_obj(Node)
|
|
script = PluginScript()
|
|
node.set_script(script)
|
|
|
|
|
|
def test_access_property(generate_obj):
|
|
node = generate_obj(Node)
|
|
path = NodePath("/foo/bar")
|
|
node._import_path = path
|
|
assert node._import_path == path
|
|
|
|
|
|
@pytest.mark.xfail(reason="Create Python class from Python not implemented yet")
|
|
def test_new_on_overloaded_class(generate_obj):
|
|
node = generate_obj(virtualtestbedcls)
|
|
# Make sure doing MyClass.new() doesn't return an instance of the
|
|
# Pandemonium class we inherit from
|
|
assert isinstance(node, virtualtestbedcls)
|
|
|
|
|
|
@pytest.mark.xfail(reason="Create Python class from Python not implemented yet")
|
|
def test_virtual_call_overloaded_notification(generate_obj):
|
|
node = generate_obj(virtualtestbedcls)
|
|
|
|
notifications = []
|
|
|
|
def _on_notification(what):
|
|
notifications.append(what)
|
|
|
|
node.on_notification = _on_notification
|
|
try:
|
|
node.notification(1)
|
|
node.notification(2)
|
|
node.notification(3)
|
|
|
|
finally:
|
|
node.on_notification = None
|
|
|
|
assert notifications == [1, 2, 3]
|
|
|
|
|
|
@pytest.mark.xfail(reason="Pluginscript doesn't support _to_string overloading")
|
|
def test_virtual_to_string_customize(generate_obj):
|
|
node = generate_obj(virtualtestbedcls)
|
|
# Object.to_string() can be customized when defining _to_string()
|
|
expected = GDString("<Main Node>")
|
|
assert node._to_string() == expected
|
|
assert node.to_string() == expected
|
|
|
|
# Try to access undefined _to_string
|
|
node = generate_obj(Node)
|
|
with pytest.raises(AttributeError):
|
|
node._to_string()
|
|
|
|
|
|
@pytest.fixture(params=["pandemonium_class", "python_subclass"])
|
|
def node_for_access(request, current_node, generate_obj):
|
|
if request.param == "pandemonium_class":
|
|
return generate_obj(Node)
|
|
else:
|
|
return current_node
|
|
|
|
|
|
@pytest.mark.xfail(reason="Current implement uses Object.callv which doesn't inform of the failure")
|
|
def test_virtual_call__to_string_not_customized(node_for_access):
|
|
with pytest.raises(AttributeError):
|
|
node_for_access._to_string()
|
|
|
|
|
|
@pytest.mark.xfail(reason="Current implement uses Object.callv which doesn't inform of the failure")
|
|
def test_virtual_call__notification_not_customized(node_for_access):
|
|
with pytest.raises(AttributeError):
|
|
node_for_access._notification(42)
|
|
|
|
|
|
def test_access_unknown_attribute(node_for_access):
|
|
with pytest.raises(AttributeError):
|
|
node_for_access.dummy
|
|
|
|
|
|
def test_call_unknown_method(node_for_access):
|
|
with pytest.raises(AttributeError):
|
|
node_for_access.dummy(42)
|
|
|
|
|
|
def test_create_refcounted_value():
|
|
script1_ref1 = PluginScript()
|
|
script2_ref1 = PluginScript()
|
|
script1_ref2 = script1_ref1
|
|
script2_ref2 = script2_ref1
|
|
del script1_ref1
|
|
|
|
|
|
def test_late_initialized_bindings_and_float_param_ret():
|
|
# OpenSimplexNoise is refcounted, so no need to create it with `generate_obj`
|
|
obj = OpenSimplexNoise()
|
|
|
|
# Float are tricky given they must be converted back and forth to double
|
|
ret = obj.get_noise_1d(inf)
|
|
assert ret == 0
|
|
|
|
# Build a double number that cannot be reprented on a float
|
|
double_only_number, = unpack("!d", b"\x11" * 8)
|
|
ret = obj.get_noise_1d(double_only_number)
|
|
assert ret == pytest.approx(-0.02726514)
|
|
|
|
# Now try with better parameter to have a correct return value
|
|
ret = obj.get_noise_3d(100, 200, 300)
|
|
assert ret == pytest.approx(-0.10482934)
|
|
|
|
|
|
def test_bad_meth_to_create_non_refcounted_object():
|
|
with pytest.raises(RuntimeError):
|
|
Node()
|
|
|
|
|
|
def test_bad_meth_to_create_refcounted_object():
|
|
with pytest.raises(RuntimeError):
|
|
OpenSimplexNoise.new()
|