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)