mirror of
https://github.com/Relintai/gdnative_python.git
synced 2024-11-14 10:27:22 +01:00
414 lines
10 KiB
Python
414 lines
10 KiB
Python
|
import sys
|
||
|
import pytest
|
||
|
from random import Random
|
||
|
from inspect import isfunction
|
||
|
from functools import partial
|
||
|
|
||
|
from godot import (
|
||
|
Array,
|
||
|
Vector2,
|
||
|
Vector3,
|
||
|
Color,
|
||
|
GDString,
|
||
|
PoolIntArray,
|
||
|
PoolRealArray,
|
||
|
PoolByteArray,
|
||
|
PoolVector2Array,
|
||
|
PoolVector3Array,
|
||
|
PoolColorArray,
|
||
|
PoolStringArray,
|
||
|
Node,
|
||
|
)
|
||
|
|
||
|
from conftest import generate_global_obj
|
||
|
|
||
|
|
||
|
is_windows_32 = (sys.platform == "win32") and (sys.maxsize <= 2 ** 32)
|
||
|
|
||
|
|
||
|
NODE = generate_global_obj(Node)
|
||
|
|
||
|
|
||
|
class BasePoolArrayBench:
|
||
|
cls = None
|
||
|
|
||
|
def __init__(self):
|
||
|
# Fixed seed for reproducibility
|
||
|
self.random = Random(0)
|
||
|
|
||
|
def generate_value(self):
|
||
|
raise NotImplemented
|
||
|
|
||
|
def generate_values(self, count):
|
||
|
return [self.generate_value() for _ in range(count)]
|
||
|
|
||
|
def expand_arg(self, arg):
|
||
|
if isfunction(arg):
|
||
|
return arg(self)
|
||
|
else:
|
||
|
return arg
|
||
|
|
||
|
|
||
|
class PoolIntArrayBench(BasePoolArrayBench):
|
||
|
cls = PoolIntArray
|
||
|
|
||
|
def generate_value(self):
|
||
|
return self.random.randint(-(2 ** 31), 2 ** 31 - 1)
|
||
|
|
||
|
|
||
|
class PoolRealArrayBench(BasePoolArrayBench):
|
||
|
cls = PoolRealArray
|
||
|
|
||
|
def generate_value(self):
|
||
|
# Use integer instead of float to avoid floating point imprecision in comparisons
|
||
|
return float(self.random.randint(0, 100))
|
||
|
|
||
|
|
||
|
class PoolByteArrayBench(BasePoolArrayBench):
|
||
|
cls = PoolByteArray
|
||
|
|
||
|
def generate_value(self):
|
||
|
return self.random.randint(0, 255)
|
||
|
|
||
|
|
||
|
class PoolColorArrayBench(BasePoolArrayBench):
|
||
|
cls = PoolColorArray
|
||
|
|
||
|
def generate_value(self):
|
||
|
# Use integer instead of float to avoid floating point imprecision in comparisons
|
||
|
return Color(self.random.randint(0, 100))
|
||
|
|
||
|
|
||
|
class PoolStringArrayBench(BasePoolArrayBench):
|
||
|
cls = PoolStringArray
|
||
|
|
||
|
def generate_value(self):
|
||
|
return GDString(str(self.random.random()))
|
||
|
|
||
|
|
||
|
class PoolVector2ArrayBench(BasePoolArrayBench):
|
||
|
cls = PoolVector2Array
|
||
|
|
||
|
def generate_value(self):
|
||
|
# Use integer instead of float to avoid floating point imprecision in comparisons
|
||
|
return Vector2(self.random.randint(0, 100))
|
||
|
|
||
|
|
||
|
class PoolVector3ArrayBench(BasePoolArrayBench):
|
||
|
cls = PoolVector3Array
|
||
|
|
||
|
def generate_value(self):
|
||
|
# Use integer instead of float to avoid floating point imprecision in comparisons
|
||
|
return Vector3(self.random.randint(0, 100))
|
||
|
|
||
|
|
||
|
@pytest.fixture(
|
||
|
scope="module",
|
||
|
ids=lambda x: x.cls.__name__,
|
||
|
params=[
|
||
|
PoolIntArrayBench,
|
||
|
PoolRealArrayBench,
|
||
|
PoolByteArrayBench,
|
||
|
PoolColorArrayBench,
|
||
|
PoolStringArrayBench,
|
||
|
PoolVector2ArrayBench,
|
||
|
PoolVector3ArrayBench,
|
||
|
],
|
||
|
)
|
||
|
def pool_x_array(request):
|
||
|
return request.param()
|
||
|
|
||
|
|
||
|
def test_empty_init(pool_x_array):
|
||
|
v1 = pool_x_array.cls()
|
||
|
v2 = pool_x_array.cls()
|
||
|
assert type(v1) == pool_x_array.cls
|
||
|
assert v1 == v2
|
||
|
assert len(v1) == 0
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"bad_val",
|
||
|
[
|
||
|
lambda x: x.generate_value(),
|
||
|
lambda x: (object() for _ in range(1)), # Must be generated each time
|
||
|
42,
|
||
|
"dummy",
|
||
|
NODE,
|
||
|
Vector2(),
|
||
|
[object()],
|
||
|
lambda x: [x.generate_value(), object(), x.generate_value()],
|
||
|
],
|
||
|
)
|
||
|
def test_bad_init(pool_x_array, bad_val):
|
||
|
bad_val = pool_x_array.expand_arg(bad_val)
|
||
|
with pytest.raises(TypeError):
|
||
|
pool_x_array.cls(bad_val)
|
||
|
|
||
|
|
||
|
def test_initialized_init(pool_x_array):
|
||
|
if is_windows_32:
|
||
|
pytest.skip("Cause segfault on windows-32, see issue #185")
|
||
|
|
||
|
vals = pool_x_array.generate_values(4)
|
||
|
v1 = pool_x_array.cls(vals)
|
||
|
v2 = pool_x_array.cls(Array(vals))
|
||
|
v3 = pool_x_array.cls(v2)
|
||
|
assert type(v1) == pool_x_array.cls
|
||
|
assert type(v2) == pool_x_array.cls
|
||
|
assert type(v3) == pool_x_array.cls
|
||
|
assert v1 == v2
|
||
|
assert v2 == v3
|
||
|
assert len(v1) == 4
|
||
|
|
||
|
|
||
|
def test_equal(pool_x_array):
|
||
|
vals = pool_x_array.generate_values(4)
|
||
|
|
||
|
v1 = pool_x_array.cls(vals)
|
||
|
v2 = pool_x_array.cls()
|
||
|
for item in vals:
|
||
|
v2.append(item)
|
||
|
v3 = pool_x_array.cls()
|
||
|
v3 += v2
|
||
|
|
||
|
# Test __eq__ operator
|
||
|
assert v1 == v2
|
||
|
assert v2 == v3
|
||
|
|
||
|
# Test __ne__ operator
|
||
|
assert not v1 != v2
|
||
|
assert not v2 != v3
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize("other_type", [list, tuple, Array])
|
||
|
def test_bad_equal_on_different_types(pool_x_array, other_type):
|
||
|
if is_windows_32 and other_type is Array:
|
||
|
pytest.skip("Cause segfault on windows-32, see issue #185")
|
||
|
|
||
|
vals = pool_x_array.generate_values(4)
|
||
|
|
||
|
pool = pool_x_array.cls(vals)
|
||
|
other = other_type(vals)
|
||
|
|
||
|
# Test __eq__ operator
|
||
|
assert not pool == other
|
||
|
|
||
|
# Test __ne__ operator
|
||
|
assert pool != other
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"arg",
|
||
|
[
|
||
|
None,
|
||
|
0,
|
||
|
Array(),
|
||
|
[],
|
||
|
(),
|
||
|
"",
|
||
|
Vector2(),
|
||
|
NODE,
|
||
|
lambda s: s.generate_value(),
|
||
|
lambda s: s.cls(s.generate_values(2)),
|
||
|
],
|
||
|
)
|
||
|
def test_bad_equal(pool_x_array, arg):
|
||
|
pool = pool_x_array.cls()
|
||
|
other = pool_x_array.expand_arg(arg)
|
||
|
|
||
|
# Test __ne__ operator
|
||
|
assert not pool == other
|
||
|
|
||
|
# Test __eq__ operator
|
||
|
assert pool != other
|
||
|
|
||
|
|
||
|
def test_add(pool_x_array):
|
||
|
v0 = pool_x_array.generate_values(2)
|
||
|
arr = pool_x_array.cls(v0)
|
||
|
v1 = pool_x_array.generate_values(2)
|
||
|
arr += pool_x_array.cls(v1) # __iadd__
|
||
|
assert arr == pool_x_array.cls(v0 + v1)
|
||
|
v2 = pool_x_array.generate_values(2)
|
||
|
arr2 = arr + pool_x_array.cls(v2) # __add__
|
||
|
assert arr2 == pool_x_array.cls(v0 + v1 + v2)
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize("arg", [None, [], (), Array(), 0, "foo", Vector2(), NODE])
|
||
|
def test_bad_add(pool_x_array, arg):
|
||
|
with pytest.raises(TypeError):
|
||
|
pool_x_array.cls() + arg
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize("arg", [None, [], (), Array(), 0, "foo", Vector2(), NODE])
|
||
|
def test_bad_iadd(pool_x_array, arg):
|
||
|
arr = pool_x_array.cls()
|
||
|
with pytest.raises(TypeError):
|
||
|
arr += arg
|
||
|
|
||
|
|
||
|
def test_repr(pool_x_array):
|
||
|
name = pool_x_array.cls.__name__
|
||
|
v = pool_x_array.cls()
|
||
|
assert repr(v) == f"<{name}([])>"
|
||
|
items = pool_x_array.generate_values(3)
|
||
|
v = pool_x_array.cls(items)
|
||
|
assert repr(v) == f"<{name}({items!r})>"
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"field,ret_type,params",
|
||
|
[
|
||
|
["append", type(None), lambda x: (x.generate_value(),)],
|
||
|
["push_back", type(None), lambda x: (x.generate_value(),)],
|
||
|
["resize", type(None), (2,)],
|
||
|
],
|
||
|
ids=lambda x: x[0],
|
||
|
)
|
||
|
def test_methods(pool_x_array, field, ret_type, params):
|
||
|
# Don't test methods' validity but bindings one
|
||
|
v = pool_x_array.cls(pool_x_array.generate_values(1))
|
||
|
params = pool_x_array.expand_arg(params)
|
||
|
assert hasattr(v, field)
|
||
|
method = getattr(v, field)
|
||
|
assert callable(method)
|
||
|
ret = method(*params)
|
||
|
assert type(ret) == ret_type
|
||
|
|
||
|
|
||
|
def test_len(pool_x_array):
|
||
|
arr = pool_x_array.cls()
|
||
|
assert len(arr) == 0
|
||
|
arr.append(pool_x_array.generate_value())
|
||
|
assert len(arr) == 1
|
||
|
|
||
|
|
||
|
def test_getitem(pool_x_array):
|
||
|
vals = pool_x_array.generate_values(3)
|
||
|
arr = pool_x_array.cls(vals)
|
||
|
assert arr[0] == vals[0]
|
||
|
assert arr[1] == vals[1]
|
||
|
assert arr[-1] == vals[-1]
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"slice_",
|
||
|
[
|
||
|
slice(1, 3),
|
||
|
slice(1, 3, -1),
|
||
|
slice(None, None, -1),
|
||
|
slice(None, None, 2),
|
||
|
slice(None, None, 10),
|
||
|
slice(-10, 10, 1),
|
||
|
slice(-10, None, 1),
|
||
|
slice(-1, None, 1),
|
||
|
slice(-1, 1, -1),
|
||
|
],
|
||
|
)
|
||
|
def test_getitem_slice(pool_x_array, slice_):
|
||
|
vals = pool_x_array.generate_values(4)
|
||
|
arr = pool_x_array.cls(vals)
|
||
|
expected = vals[slice_]
|
||
|
sub_arr = arr[slice_]
|
||
|
assert isinstance(sub_arr, pool_x_array.cls)
|
||
|
assert sub_arr == pool_x_array.cls(expected)
|
||
|
|
||
|
|
||
|
def test_getitem_slice_zero_step(pool_x_array):
|
||
|
arr = pool_x_array.cls(pool_x_array.generate_values(2))
|
||
|
with pytest.raises(ValueError):
|
||
|
arr[::0]
|
||
|
|
||
|
|
||
|
def test_outofrange_getitem(pool_x_array):
|
||
|
arr = pool_x_array.cls(pool_x_array.generate_values(2))
|
||
|
with pytest.raises(IndexError):
|
||
|
arr[2]
|
||
|
with pytest.raises(IndexError):
|
||
|
arr[-3]
|
||
|
|
||
|
|
||
|
def test_setitem(pool_x_array):
|
||
|
arr = pool_x_array.cls(pool_x_array.generate_values(3))
|
||
|
v = pool_x_array.generate_value()
|
||
|
arr[0] = v
|
||
|
assert len(arr) == 3
|
||
|
assert arr[0] == v
|
||
|
arr[-1] = v
|
||
|
assert len(arr) == 3
|
||
|
assert arr[-1] == v
|
||
|
|
||
|
|
||
|
def test_outofrange_setitem(pool_x_array):
|
||
|
arr = pool_x_array.cls(pool_x_array.generate_values(2))
|
||
|
v = pool_x_array.generate_value()
|
||
|
with pytest.raises(IndexError):
|
||
|
arr[2] = v
|
||
|
with pytest.raises(IndexError):
|
||
|
arr[-3] = v
|
||
|
|
||
|
|
||
|
def test_delitem(pool_x_array):
|
||
|
items = pool_x_array.generate_values(3)
|
||
|
arr = pool_x_array.cls(items)
|
||
|
del arr[0]
|
||
|
assert len(arr) == 2
|
||
|
assert arr[0] == items[1]
|
||
|
assert arr[1] == items[2]
|
||
|
del arr[-1]
|
||
|
assert len(arr) == 1
|
||
|
assert arr[-1] == items[1]
|
||
|
|
||
|
|
||
|
def test_outofrange_delitem(pool_x_array):
|
||
|
arr = pool_x_array.cls(pool_x_array.generate_values(2))
|
||
|
with pytest.raises(IndexError):
|
||
|
del arr[2]
|
||
|
with pytest.raises(IndexError):
|
||
|
del arr[-3]
|
||
|
|
||
|
|
||
|
def test_iter(pool_x_array):
|
||
|
items = pool_x_array.generate_values(3)
|
||
|
arr = pool_x_array.cls(items)
|
||
|
items_from_v = [x for x in arr]
|
||
|
assert items_from_v == items
|
||
|
|
||
|
|
||
|
def test_append(pool_x_array):
|
||
|
items = pool_x_array.generate_values(3)
|
||
|
arr = pool_x_array.cls()
|
||
|
for item in items:
|
||
|
arr.append(item)
|
||
|
assert len(arr) == 3
|
||
|
assert arr == pool_x_array.cls(items)
|
||
|
|
||
|
|
||
|
def test_raw_access(pool_x_array):
|
||
|
arr = pool_x_array.cls()
|
||
|
arr.resize(100)
|
||
|
values = pool_x_array.generate_values(10)
|
||
|
|
||
|
with arr.raw_access() as ptr:
|
||
|
assert isinstance(ptr.get_address(), int)
|
||
|
|
||
|
for i in range(100):
|
||
|
ptr[i] = values[i % len(values)]
|
||
|
|
||
|
with arr.raw_access() as ptr:
|
||
|
for i in range(100):
|
||
|
assert ptr[i] == values[i % len(values)]
|
||
|
|
||
|
|
||
|
def test_pool_byte_array_overflow():
|
||
|
with pytest.raises(OverflowError):
|
||
|
PoolByteArray([256])
|
||
|
with pytest.raises(OverflowError):
|
||
|
PoolByteArray([1, 2, 256, 4])
|
||
|
arr = PoolByteArray([1])
|
||
|
with pytest.raises(OverflowError):
|
||
|
arr.append(256)
|
||
|
with pytest.raises(OverflowError):
|
||
|
arr.push_back(256)
|