From e6e4d90c47bf474e425e60d8c1f1a67556f390cf Mon Sep 17 00:00:00 2001 From: Nicolas Braud-Santoni Date: Mon, 4 Feb 2019 14:55:06 +0100 Subject: [PATCH] tests: Fix warts found by flake8 This includes: - unused variables and imports; - ambiguous variable names which should have been fixed by #85; - a confusing chain of equalities that likely didn't test what it was meant to. --- tests/benchmark.py | 8 +-- tests/conftest.py | 3 +- tests/test_convert.py | 17 +++-- tests/test_typing.py | 19 ++++-- tests/test_vector2_addition.py | 6 +- tests/test_vector2_angle.py | 36 +++++----- tests/test_vector2_ctor.py | 7 +- tests/test_vector2_dot.py | 21 ++++-- tests/test_vector2_equality.py | 22 ++++--- tests/test_vector2_isclose.py | 47 ++++++++----- tests/test_vector2_length.py | 11 ++-- tests/test_vector2_member_access.py | 4 +- tests/test_vector2_negation.py | 9 ++- tests/test_vector2_normalize.py | 2 - tests/test_vector2_reflect.py | 10 +-- tests/test_vector2_rotate.py | 49 ++++++-------- tests/test_vector2_scalar_multiplication.py | 14 ++-- tests/test_vector2_scale.py | 1 - tests/test_vector2_substraction.py | 6 +- tests/test_vector2_truncate.py | 9 ++- tests/utils.py | 73 ++++++++++----------- 21 files changed, 197 insertions(+), 177 deletions(-) diff --git a/tests/benchmark.py b/tests/benchmark.py index 14318790..1af91375 100755 --- a/tests/benchmark.py +++ b/tests/benchmark.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -import perf # type: ignore +import perf # type: ignore from ppb_vector import Vector2 from utils import * @@ -8,11 +8,11 @@ y = Vector2(0, 1) scalar = 123 -for f in BINARY_OPS + BINARY_SCALAR_OPS + BOOL_OPS: # type: ignore +for f in BINARY_OPS + BINARY_SCALAR_OPS + BOOL_OPS: # type: ignore r.bench_func(f.__name__, f, x, y) -for f in UNARY_OPS + UNARY_SCALAR_OPS: # type: ignore +for f in UNARY_OPS + UNARY_SCALAR_OPS: # type: ignore r.bench_func(f.__name__, f, x) -for f in SCALAR_OPS: # type: ignore +for f in SCALAR_OPS: # type: ignore r.bench_func(f.__name__, f, x, scalar) diff --git a/tests/conftest.py b/tests/conftest.py index ff756fec..7f38bc25 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,6 @@ -import os - def setup_hypothesis(): from hypothesis import settings, Verbosity + settings.register_profile("ci", max_examples=1000) settings.register_profile("dev", max_examples=10) settings.register_profile("debug", max_examples=10, verbosity=Verbosity.verbose) diff --git a/tests/test_convert.py b/tests/test_convert.py index 3108ab1e..a38cf477 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -4,10 +4,15 @@ from ppb_vector import Vector2 from utils import vector_likes, vectors -class V(Vector2): pass -@pytest.mark.parametrize('vector_like', vector_likes(), ids=lambda x: type(x).__name__) # type: ignore -@pytest.mark.parametrize('cls', [Vector2, V]) # type: ignore +class V(Vector2): + pass + + +@pytest.mark.parametrize( + "vector_like", vector_likes(), ids=lambda x: type(x).__name__ +) # type: ignore +@pytest.mark.parametrize("cls", [Vector2, V]) # type: ignore def test_convert_class(cls, vector_like): vector = cls.convert(vector_like) assert isinstance(vector, cls) @@ -18,22 +23,24 @@ def test_convert_class(cls, vector_like): def test_convert_tuple(vector: Vector2): assert vector == tuple(vector) == (vector.x, vector.y) + @given(vector=vectors()) def test_convert_list(vector: Vector2): assert vector == list(vector) == [vector.x, vector.y] + @given(vector=vectors()) def test_convert_dict(vector: Vector2): assert vector == vector.asdict() -@pytest.mark.parametrize('coerce', [tuple, list, Vector2.asdict]) +@pytest.mark.parametrize("coerce", [tuple, list, Vector2.asdict]) @given(x=vectors()) def test_convert_roundtrip(coerce, x: Vector2): assert x == Vector2(coerce(x)) -@pytest.mark.parametrize('coerce', [tuple, list]) +@pytest.mark.parametrize("coerce", [tuple, list]) @given(x=vectors()) def test_convert_roundtrip_positional(coerce, x: Vector2): assert x == Vector2(*coerce(x)) diff --git a/tests/test_typing.py b/tests/test_typing.py index c9f612b2..a9d655ff 100644 --- a/tests/test_typing.py +++ b/tests/test_typing.py @@ -7,38 +7,43 @@ class V1(Vector2): """Arbitrary subclass of Vector2.""" + pass + class V11(V1): """Subclass of V1.""" + pass + class V2(Vector2): """Arbitrary subclass of Vector2, distinct from V1.""" + pass -@pytest.mark.parametrize('op', BINARY_OPS) +@pytest.mark.parametrize("op", BINARY_OPS) @given(x=st.builds(V1, vectors()), y=st.builds(V1, units())) def test_binop_same(op, x: V1, y: V2): assert isinstance(op(x, y), V1) -@pytest.mark.parametrize('op', BINARY_OPS) +@pytest.mark.parametrize("op", BINARY_OPS) @given(x=vectors(), y=units()) def test_binop_different(op, x: Vector2, y: Vector2): assert isinstance(op(V1(x), V2(y)), (V1, V2)) assert isinstance(op(V2(x), V1(y)), (V1, V2)) -@pytest.mark.parametrize('op', BINARY_OPS) +@pytest.mark.parametrize("op", BINARY_OPS) @given(x=st.builds(V1, vectors()), y=st.builds(V1, units())) def test_binop_subclass(op, x: V1, y: V1): assert isinstance(op(V11(x), y), V11) assert isinstance(op(x, V11(y)), V11) -@pytest.mark.parametrize('op', SCALAR_OPS) +@pytest.mark.parametrize("op", SCALAR_OPS) @given(x=st.builds(V1, vectors()), scalar=floats()) def test_vnumop(op, x: V1, scalar: float): try: @@ -48,7 +53,7 @@ def test_vnumop(op, x: V1, scalar: float): reject() -@pytest.mark.parametrize('op', UNARY_OPS) +@pytest.mark.parametrize("op", UNARY_OPS) @given(x=st.builds(V1, vectors())) def test_monop(op, x): try: @@ -58,7 +63,9 @@ def test_monop(op, x): reject() -@pytest.mark.parametrize('op', BINARY_OPS + BINARY_SCALAR_OPS + BOOL_OPS) # type: ignore +@pytest.mark.parametrize( + "op", BINARY_OPS + BINARY_SCALAR_OPS + BOOL_OPS # type: ignore +) @given(x=vectors(), y=units()) def test_binop_vectorlike(op, x: Vector2, y: Vector2): """Test that `op` accepts a vector-like second parameter.""" diff --git a/tests/test_vector2_addition.py b/tests/test_vector2_addition.py index 9cb6a827..432f482f 100644 --- a/tests/test_vector2_addition.py +++ b/tests/test_vector2_addition.py @@ -25,7 +25,7 @@ def test_addition_vector_list(): def test_addition_vector_dict(): test_vector = Vector2(1, 1) - test_dict = {'x': 3, 'y': 5} + test_dict = {"x": 3, "y": 5} result = test_vector + test_dict assert result == Vector2(4, 6) @@ -33,7 +33,7 @@ def test_addition_vector_dict(): data = [ ([Vector2(1, 1), (2, 2)], Vector2(3, 3)), ([Vector2(1, 2), [2, 2]], Vector2(3, 4)), - ([Vector2(1, 2), {'x': 2, 'y': 2}], Vector2(3, 4)), + ([Vector2(1, 2), {"x": 2, "y": 2}], Vector2(3, 4)), ([Vector2(10, 16), Vector2(2, 2)], Vector2(12, 18)), ([Vector2(25, 22), (12, 92)], Vector2(37, 114)), ([Vector2(25, 22), Vector2(22, 61)], Vector2(47, 83)), @@ -43,6 +43,6 @@ def test_addition_vector_dict(): ] -@pytest.mark.parametrize('test_input, expected', data) +@pytest.mark.parametrize("test_input, expected", data) def test_multiples_values(test_input, expected): assert (test_input[0] + test_input[1]) == expected diff --git a/tests/test_vector2_angle.py b/tests/test_vector2_angle.py index 80544cab..736c0715 100644 --- a/tests/test_vector2_angle.py +++ b/tests/test_vector2_angle.py @@ -1,19 +1,22 @@ from ppb_vector import Vector2 from math import isclose import pytest # type: ignore -from hypothesis import assume, given, note +from hypothesis import assume, given from utils import angle_isclose, floats, vectors -@pytest.mark.parametrize("left, right, expected", [ - (Vector2(1, 1), Vector2(0, -1), -135), - (Vector2(1, 1), Vector2(-1, 0), 135), - (Vector2(0, 1), Vector2(0, -1), 180), - (Vector2(-1, -1), Vector2(1, 0), 135), - (Vector2(-1, -1), Vector2(-1, 0), -45), - (Vector2(1, 0), Vector2(0, 1), 90), - (Vector2(1, 0), Vector2(1, 0), 0), -]) +@pytest.mark.parametrize( + "left, right, expected", + [ + (Vector2(1, 1), Vector2(0, -1), -135), + (Vector2(1, 1), Vector2(-1, 0), 135), + (Vector2(0, 1), Vector2(0, -1), 180), + (Vector2(-1, -1), Vector2(1, 0), 135), + (Vector2(-1, -1), Vector2(-1, 0), -45), + (Vector2(1, 0), Vector2(0, 1), 90), + (Vector2(1, 0), Vector2(1, 0), 0), + ], +) def test_angle(left, right, expected): lr = left.angle(right) rl = right.angle(left) @@ -23,10 +26,7 @@ def test_angle(left, right, expected): assert isclose(rl, 180 if expected == 180 else -expected) -@given( - left=vectors(), - right=vectors(), -) +@given(left=vectors(), right=vectors()) def test_angle_range(left, right): """Vector2.angle produces values in [-180; 180] and is antisymmetric. @@ -38,11 +38,8 @@ def test_angle_range(left, right): assert -180 < rl <= 180 assert angle_isclose(lr, -rl) -@given( - left=vectors(), - middle=vectors(), - right=vectors(), -) + +@given(left=vectors(), middle=vectors(), right=vectors()) def test_angle_additive(left, middle, right): """left.angle(middle) + middle.angle(right) == left.angle(right)""" lm = left.angle(middle) @@ -50,6 +47,7 @@ def test_angle_additive(left, middle, right): lr = left.angle(right) assert angle_isclose(lm + mr, lr) + @given(x=vectors(), scalar=floats()) def test_angle_aligned(x: Vector2, scalar: float): """x.angle(scalar * x) is 0 or 180, depending on whether scalar > 0""" diff --git a/tests/test_vector2_ctor.py b/tests/test_vector2_ctor.py index 0a7968c1..eca94d49 100644 --- a/tests/test_vector2_ctor.py +++ b/tests/test_vector2_ctor.py @@ -5,10 +5,11 @@ from ppb_vector import Vector2 -class V(Vector2): pass +class V(Vector2): + pass -@pytest.mark.parametrize('cls', [Vector2, V]) +@pytest.mark.parametrize("cls", [Vector2, V]) @given(x=vectors()) def test_ctor_vector_like(cls, x: Vector2): for x_like in vector_likes(x): @@ -17,7 +18,7 @@ def test_ctor_vector_like(cls, x: Vector2): assert isinstance(vector, cls) -@pytest.mark.parametrize('cls', [Vector2, V]) +@pytest.mark.parametrize("cls", [Vector2, V]) @given(x=floats(), y=floats()) def test_ctor_coordinates(cls, x: float, y: float): assert cls(x, y) == cls((x, y)) diff --git a/tests/test_vector2_dot.py b/tests/test_vector2_dot.py index 206a7ce3..7b1601ee 100644 --- a/tests/test_vector2_dot.py +++ b/tests/test_vector2_dot.py @@ -1,7 +1,6 @@ from ppb_vector import Vector2 from math import sqrt -import pytest # type: ignore from hypothesis import assume, given, note from utils import angles, floats, isclose, vectors @@ -11,19 +10,23 @@ def test_dot_axis(vector: Vector2): assert vector * (1, 0) == vector.x assert vector * (0, 1) == vector.y + @given(x=vectors(), y=vectors()) def test_dot_commutes(x: Vector2, y: Vector2): assert x * y == y * x + @given(x=vectors()) def test_dot_length(x: Vector2): assert isclose(x * x, x.length * x.length) + @given(x=vectors(), y=vectors()) def test_cauchy_schwarz(x: Vector2, y: Vector2): """Test the Cauchy-Schwarz inequality: |x·y| ⩽ |x| |y|""" assert abs(x * y) <= (1 + 1e-12) * x.length * y.length + @given(x=vectors(), y=vectors(), angle=angles()) def test_dot_rotational_invariance(x: Vector2, y: Vector2, angle: float): """Test that rotating vectors doesn't change their dot product.""" @@ -34,14 +37,18 @@ def test_dot_rotational_invariance(x: Vector2, y: Vector2, angle: float): # Exclude near-orthogonal test inputs assume(abs(cos_t) > 1e-6) - assert isclose(x * y, x.rotate(angle) * y.rotate(angle), - rel_to=(x, y), rel_exp=2) + assert isclose(x * y, x.rotate(angle) * y.rotate(angle), rel_to=(x, y), rel_exp=2) + + +MAGNITUDE = 1e10 -MAGNITUDE=1e10 -@given(x=vectors(max_magnitude=MAGNITUDE), z=vectors(max_magnitude=MAGNITUDE), - y=vectors(max_magnitude=sqrt(MAGNITUDE)), - scalar=floats(max_magnitude=sqrt(MAGNITUDE))) +@given( + x=vectors(max_magnitude=MAGNITUDE), + z=vectors(max_magnitude=MAGNITUDE), + y=vectors(max_magnitude=sqrt(MAGNITUDE)), + scalar=floats(max_magnitude=sqrt(MAGNITUDE)), +) def test_dot_linear(x: Vector2, y: Vector2, z: Vector2, scalar: float): """Test that x · (λ y + z) = λ x·y + x·z""" inner, outer = x * (scalar * y + z), scalar * x * y + x * z diff --git a/tests/test_vector2_equality.py b/tests/test_vector2_equality.py index e397ca7d..5d4fe8ed 100644 --- a/tests/test_vector2_equality.py +++ b/tests/test_vector2_equality.py @@ -5,26 +5,30 @@ @given(x=vectors()) def test_equal_self(x: Vector2): - assert x == x + assert x == x + @given(x=vectors()) def test_equal_non_vector(x: Vector2): - assert (x == "foo") == ("foo" == x) == False + assert x != "foo" + assert (x == "foo") == ("foo" == x) + @given(x=vectors(), y=vectors()) def test_equal_symmetric(x: Vector2, y): - assert (x == y) == (y == x) + assert (x == y) == (y == x) - for y_like in vector_likes(y): - assert (x == y_like) == (y_like == x) + for y_like in vector_likes(y): + assert (x == y_like) == (y_like == x) @given(x=vectors()) def test_non_zero_equal(x: Vector2): - assume(x != (0, 0)) - assert x != 1.1 * x - assert x != -x + assume(x != (0, 0)) + assert x != 1.1 * x + assert x != -x + @given(x=vectors(), y=vectors()) def test_not_equal_equivalent(x: Vector2, y: Vector2): - assert (x != y) == (not x == y) + assert (x != y) == (not x == y) diff --git a/tests/test_vector2_isclose.py b/tests/test_vector2_isclose.py index d017f386..41df9297 100644 --- a/tests/test_vector2_isclose.py +++ b/tests/test_vector2_isclose.py @@ -1,8 +1,8 @@ from ppb_vector import Vector2 -from pytest import raises # type: ignore +from pytest import raises # type: ignore from math import sqrt from utils import units, lengths, vectors -from hypothesis import assume, event, given, note, example +from hypothesis import assume, given, note, example from hypothesis.strategies import floats @@ -13,8 +13,10 @@ def test_isclose_to_self(x, abs_tol, rel_tol): EPSILON = 1e-8 -@given(x=vectors(max_magnitude=1e30), direction=units(), - abs_tol=lengths(max_value=1e30)) + +@given( + x=vectors(max_magnitude=1e30), direction=units(), abs_tol=lengths(max_value=1e30) +) def test_isclose_abs_error(x, direction, abs_tol): """Test x.isclose(rel_tol=0) near the boundary between “close” and “not close” @@ -34,10 +36,21 @@ def test_isclose_abs_error(x, direction, abs_tol): assert not x.isclose(negative, abs_tol=abs_tol, rel_tol=0) -@given(x=vectors(max_magnitude=1e30), direction=units(), - rel_tol=floats(min_value=EPSILON, max_value=1-sqrt(EPSILON))) -@example(x=Vector2(0.5030575955800033, 4183.540331936798), direction=Vector2(-0.21080691603913568, -0.97752772039982), rel_tol=1.0000044626502047e-08) -@example(x=Vector2(0.336348726648339, 4183.540331936798), direction=Vector2(-0.2108069159366941, -0.9775277204219119), rel_tol=1.0000009102918328e-08) +@given( + x=vectors(max_magnitude=1e30), + direction=units(), + rel_tol=floats(min_value=EPSILON, max_value=1 - sqrt(EPSILON)), +) +@example( + x=Vector2(0.503_057_595_580_003_3, 4183.540_331_936_798), + direction=Vector2(-0.210_806_916_039_135_68, -0.977_527_720_399_82), + rel_tol=1.000_004_462_650_204_7e-08, +) +@example( + x=Vector2(0.336_348_726_648_339, 4183.540_331_936_798), + direction=Vector2(-0.210_806_915_936_694_1, -0.977_527_720_421_911_9), + rel_tol=1.000_000_910_291_832_8e-08, +) def test_isclose_rel_error(x, direction, rel_tol): """Test x.isclose(abs_tol=0) near the boundary between “close” and “not close” @@ -50,8 +63,10 @@ def test_isclose_rel_error(x, direction, rel_tol): error = rel_tol * direction positive = x + (1 - sqrt(EPSILON)) * x.length * error - note(f"positive example: {positive} = x + {positive - x} =" - f"x + {(positive - x).length / x.length} * |x| * direction") + note( + f"positive example: {positive} = x + {positive - x} =" + f"x + {(positive - x).length / x.length} * |x| * direction" + ) assert x.isclose(positive, abs_tol=0, rel_tol=rel_tol) @@ -64,15 +79,17 @@ def test_isclose_rel_error(x, direction, rel_tol): # δ² > x² + rel_tol² δ² + 2δ x·error # (1 - rel_tol²) δ² - 2 x·error δ > x² # a δ² - 2 b δ > c with : - a = 1 - rel_tol*rel_tol + a = 1 - rel_tol * rel_tol b = x * error - c = x*x - δ = (b + sqrt(a*c + b*b))/a + c = x * x + δ = (b + sqrt(a * c + b * b)) / a note(f"δ: {δ}") negative = x + (1 + sqrt(EPSILON)) * max(x.length, δ) * error - note(f"negative example: {negative} = x + {negative - x} = " - f"x + {(negative - x).length / x.length} * |x| * direction") + note( + f"negative example: {negative} = x + {negative - x} = " + f"x + {(negative - x).length / x.length} * |x| * direction" + ) assert not x.isclose(negative, abs_tol=0, rel_tol=rel_tol) diff --git a/tests/test_vector2_length.py b/tests/test_vector2_length.py index 5a04b328..d9e46c8d 100644 --- a/tests/test_vector2_length.py +++ b/tests/test_vector2_length.py @@ -2,13 +2,10 @@ import pytest # type: ignore -@pytest.mark.parametrize("x, y, expected", [ - (6, 8, 10), - (8, 6, 10), - (0, 0, 0), - (-6, -8, 10), - (1, 2, 2.23606797749979) -]) +@pytest.mark.parametrize( + "x, y, expected", + [(6, 8, 10), (8, 6, 10), (0, 0, 0), (-6, -8, 10), (1, 2, 2.23606797749979)], +) def test_length(x, y, expected): vector = ppb_vector.Vector2(x, y) assert vector.length == expected diff --git a/tests/test_vector2_member_access.py b/tests/test_vector2_member_access.py index 3a282de1..b3678bcc 100644 --- a/tests/test_vector2_member_access.py +++ b/tests/test_vector2_member_access.py @@ -18,5 +18,5 @@ def test_vector2_index_access(vector): def test_vector2_key_access(vector): - assert vector['x'] == 10 - assert vector['y'] == 20 + assert vector["x"] == 10 + assert vector["y"] == 20 diff --git a/tests/test_vector2_negation.py b/tests/test_vector2_negation.py index 08e5fb60..d14eb30b 100644 --- a/tests/test_vector2_negation.py +++ b/tests/test_vector2_negation.py @@ -3,14 +3,17 @@ from ppb_vector import Vector2 from utils import vectors + @given(vector=vectors()) def test_negation_scalar(vector: Vector2): - assert - vector == (-1) * vector + assert -vector == (-1) * vector + @given(vector=vectors()) def test_negation_involutive(vector: Vector2): - assert vector == - (- vector) + assert vector == -(-vector) + @given(vector=vectors()) def test_negation_addition(vector: Vector2): - assert vector + (- vector) == (0, 0) + assert vector + (-vector) == (0, 0) diff --git a/tests/test_vector2_normalize.py b/tests/test_vector2_normalize.py index aacb8bfd..b0c0df0b 100644 --- a/tests/test_vector2_normalize.py +++ b/tests/test_vector2_normalize.py @@ -1,8 +1,6 @@ -import pytest # type: ignore from hypothesis import assume, given from math import isclose from utils import vectors -import ppb_vector @given(x=vectors()) diff --git a/tests/test_vector2_reflect.py b/tests/test_vector2_reflect.py index d228c7ff..c42561f0 100644 --- a/tests/test_vector2_reflect.py +++ b/tests/test_vector2_reflect.py @@ -10,11 +10,13 @@ (Vector2(1, 1), Vector2(-1, 0), Vector2(-1, 1)), (Vector2(0, 1), Vector2(0, -1), Vector2(0, -1)), (Vector2(-1, -1), Vector2(1, 0), Vector2(1, -1)), - (Vector2(-1, -1), Vector2(-1, 0), Vector2(1, -1)) + (Vector2(-1, -1), Vector2(-1, 0), Vector2(1, -1)), ) -@pytest.mark.parametrize("initial_vector, surface_normal, expected_vector", reflect_data) +@pytest.mark.parametrize( + "initial_vector, surface_normal, expected_vector", reflect_data +) def test_reflect(initial_vector, surface_normal, expected_vector): assert initial_vector.reflect(surface_normal).isclose(expected_vector) @@ -45,6 +47,4 @@ def test_reflect_prop(initial: Vector2, normal: Vector2): note(f"initial ⋅ normal: {initial * normal}") note(f"reflected ⋅ normal: {reflected * normal}") assert isclose((initial * normal), -(reflected * normal)) - assert angle_isclose(normal.angle(initial), - 180 - normal.angle(reflected) - ) + assert angle_isclose(normal.angle(initial), 180 - normal.angle(reflected)) diff --git a/tests/test_vector2_rotate.py b/tests/test_vector2_rotate.py index cc2b3931..6ea2a19e 100644 --- a/tests/test_vector2_rotate.py +++ b/tests/test_vector2_rotate.py @@ -8,8 +8,8 @@ data_exact = [ (Vector2(1, 1), -90, Vector2(1, -1)), - (Vector2(1, 1), 0, Vector2(1, 1)), - (Vector2(1, 1), 90, Vector2(-1, 1)), + (Vector2(1, 1), 0, Vector2(1, 1)), + (Vector2(1, 1), 90, Vector2(-1, 1)), (Vector2(1, 1), 180, Vector2(-1, -1)), ] @@ -17,24 +17,26 @@ (Vector2(3, -20), 53, Vector2(17.77816, -9.64039)), (Vector2(math.pi, -1 * math.e), 30, Vector2(4.07984, -0.7833)), (Vector2(math.pi, math.e), 67, Vector2(-1.27467, 3.95397)), - - (Vector2(1, 0), 30, Vector2(math.sqrt(3)/2, 0.5)), - (Vector2(1, 0), 60, Vector2(0.5, math.sqrt(3)/2)), + (Vector2(1, 0), 30, Vector2(math.sqrt(3) / 2, 0.5)), + (Vector2(1, 0), 60, Vector2(0.5, math.sqrt(3) / 2)), ] -@pytest.mark.parametrize('input, angle, expected', data_exact) + +@pytest.mark.parametrize("input, angle, expected", data_exact) def test_exact_rotations(input, angle, expected): assert input.rotate(angle) == expected assert input.angle(expected) == angle -@pytest.mark.parametrize('input, angle, expected', data_close) + +@pytest.mark.parametrize("input, angle, expected", data_close) def test_close_rotations(input, angle, expected): assert input.rotate(angle).isclose(expected) assert angle_isclose(input.angle(expected), angle) + def test_for_exception(): with pytest.raises(TypeError): - Vector2('gibberish', 1).rotate(180) + Vector2("gibberish", 1).rotate(180) @given(angle=angles()) @@ -44,7 +46,6 @@ def test_trig_stability(angle): We are testing that this equation holds, as otherwise rotations would (slightly) change the length of vectors they are applied to. """ - r = math.radians(angle) r_cos, r_sin = Vector2._trig(angle) # Don't use exponents here. Multiplication is generally more stable. @@ -81,10 +82,7 @@ def test_rotation_stability(angle, loops): assert math.isclose(fellswoop.length, initial.length, rel_tol=1e-15) -@given( - initial=vectors(), - angles=st.lists(angles()), -) +@given(initial=vectors(), angles=st.lists(angles())) def test_rotation_stability2(initial, angles): """Rotating by a sequence of angles is equivalent to rotating by the total.""" total_angle = sum(angles) @@ -100,28 +98,19 @@ def test_rotation_stability2(initial, angles): assert math.isclose(fellswoop.length, initial.length, rel_tol=1e-15) -@given( - x=vectors(), y=vectors(), - l=floats(), - angle=angles(), -) +@given(x=vectors(), y=vectors(), scalar=floats(), angle=angles()) # In this example: # * x * l == -y # * Rotation must not be an multiple of 90deg # * Must be sufficiently large -@example( - x=Vector2(1e10, 1e10), - y=Vector2(1e19, 1e19), - l=-1e9, - angle=45, -) -def test_rotation_linearity(x, y, l, angle): +@example(x=Vector2(1e10, 1e10), y=Vector2(1e19, 1e19), scalar=-1e9, angle=45) +def test_rotation_linearity(x, y, scalar, angle): """(l*x + y).rotate is equivalent to l*x.rotate + y.rotate""" - inner = (l * x + y).rotate(angle) - outer = l * x.rotate(angle) + y.rotate(angle) - note(f"l * x + y: {l * x + y}") - note(f"l * x.rotate(): {l * x.rotate(angle)}") + inner = (scalar * x + y).rotate(angle) + outer = scalar * x.rotate(angle) + y.rotate(angle) + note(f"scalar * x + y: {scalar * x + y}") + note(f"scalar * x.rotate(): {scalar * x.rotate(angle)}") note(f"y.rotate(): {y.rotate(angle)}") note(f"Inner: {inner}") note(f"Outer: {outer}") - assert inner.isclose(outer, rel_to=[x, l * x, y]) + assert inner.isclose(outer, rel_to=[x, scalar * x, y]) diff --git a/tests/test_vector2_scalar_multiplication.py b/tests/test_vector2_scalar_multiplication.py index c61b1876..f9f818b2 100644 --- a/tests/test_vector2_scalar_multiplication.py +++ b/tests/test_vector2_scalar_multiplication.py @@ -1,4 +1,3 @@ -import pytest # type: ignore from hypothesis import assume, given from math import isclose from utils import floats, vectors @@ -15,14 +14,17 @@ def test_scalar_coordinates(scalar: float, vector: Vector2): @given(scalar1=floats(), scalar2=floats(), x=vectors()) def test_scalar_associative(scalar1: float, scalar2: float, x: Vector2): """(scalar1 * scalar2) * x == scalar1 * (scalar2 * x)""" - left = (scalar1 * scalar2) * x - right = scalar1 * (scalar2 * x) + left = (scalar1 * scalar2) * x + right = scalar1 * (scalar2 * x) assert left.isclose(right) + @given(scalar=floats(), x=vectors(), y=vectors()) def test_scalar_linear(scalar: float, x: Vector2, y: Vector2): - assert (scalar * (x + y)).isclose(scalar*x + scalar*y, - rel_to=[x, y, scalar*x, scalar*y]) + assert (scalar * (x + y)).isclose( + scalar * x + scalar * y, rel_to=[x, y, scalar * x, scalar * y] + ) + @given(scalar=floats(), x=vectors()) def test_scalar_length(scalar: float, x: Vector2): @@ -33,4 +35,4 @@ def test_scalar_length(scalar: float, x: Vector2): def test_scalar_division(x: Vector2, scalar: float): """Test that (x / λ) = (1 / λ) * x""" assume(abs(scalar) > 1e-100) - assert (x / scalar).isclose((1/scalar) * x) + assert (x / scalar).isclose((1 / scalar) * x) diff --git a/tests/test_vector2_scale.py b/tests/test_vector2_scale.py index fa0186d2..bbe77202 100644 --- a/tests/test_vector2_scale.py +++ b/tests/test_vector2_scale.py @@ -1,4 +1,3 @@ -import pytest # type: ignore from hypothesis import assume, given from math import isclose from utils import angle_isclose, floats, lengths, vectors diff --git a/tests/test_vector2_substraction.py b/tests/test_vector2_substraction.py index d38d8bdb..5c56b70a 100644 --- a/tests/test_vector2_substraction.py +++ b/tests/test_vector2_substraction.py @@ -25,7 +25,7 @@ def test_substraction_vector_list(): def test_substraction_vector_dict(): test_vector = Vector2(7, 11) - test_dict = {'x': 3, 'y': 5} + test_dict = {"x": 3, "y": 5} result = test_vector - test_dict assert result == Vector2(4, 6) @@ -39,10 +39,10 @@ def test_substraction_vector_dict(): ([Vector2(42, 12), (-5, 23)], Vector2(47, -11)), ([Vector2(51, 28), [72, 31]], Vector2(-21, -3)), ([Vector2(1, 2), [2, 2]], Vector2(-1, 0)), - ([Vector2(1, 2), {'x': 2, 'y': 2}], Vector2(-1, 0)), + ([Vector2(1, 2), {"x": 2, "y": 2}], Vector2(-1, 0)), ] -@pytest.mark.parametrize('test_input, expected', data) +@pytest.mark.parametrize("test_input, expected", data) def test_multiples_values(test_input, expected): assert (test_input[0] - test_input[1]) == expected diff --git a/tests/test_vector2_truncate.py b/tests/test_vector2_truncate.py index d7d9a158..8576b6eb 100644 --- a/tests/test_vector2_truncate.py +++ b/tests/test_vector2_truncate.py @@ -1,4 +1,3 @@ -import pytest # type: ignore from hypothesis import assume, event, example, given, note from typing import Type, Union from utils import floats, lengths, vectors @@ -18,8 +17,8 @@ def test_truncate_invariant(x: Vector2, max_length: float): @given(x=vectors(max_magnitude=1e150), max_length=floats()) -@example( # Large example where x.length == max_length but 1 * x != x - x=Vector2(0.0, 7.1e+62), max_length=7.1e+62 +@example( # Large example where x.length == max_length but 1 * x != x + x=Vector2(0.0, 7.1e62), max_length=7.1e62 ) def test_truncate_equivalent_to_scale(x: Vector2, max_length: float): """Vector2.scale_to and truncate are equivalent when max_length <= x.length""" @@ -28,8 +27,8 @@ def test_truncate_equivalent_to_scale(x: Vector2, max_length: float): if max_length > 0: note(f"x.length = {x.length / max_length} * max_length") - scale : Union[Vector2, Type[Exception]] - truncate : Union[Vector2, Type[Exception]] + scale: Union[Vector2, Type[Exception]] + truncate: Union[Vector2, Type[Exception]] try: truncate = x.truncate(max_length) diff --git a/tests/utils.py b/tests/utils.py index f67a70ee..092110e8 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -7,35 +7,49 @@ def angles(): return st.floats(min_value=-360, max_value=360) + def floats(max_magnitude=1e75): return st.floats(min_value=-max_magnitude, max_value=max_magnitude) + def lengths(min_value=0, max_value=1e75): return st.floats(min_value=min_value, max_value=max_value) + def vectors(max_magnitude=1e75): - return st.builds(Vector2, - st.floats(min_value=-max_magnitude, max_value=max_magnitude), - st.floats(min_value=-max_magnitude, max_value=max_magnitude) + return st.builds( + Vector2, + st.floats(min_value=-max_magnitude, max_value=max_magnitude), + st.floats(min_value=-max_magnitude, max_value=max_magnitude), ) + def units(): return st.builds(Vector2(1, 0).rotate, angles()) -def angle_isclose(x, y, epsilon = 6.5e-5): +def angle_isclose(x, y, epsilon=6.5e-5): d = (x - y) % 360 return (d < epsilon) or (d > 360 - epsilon) -def isclose(x, y, abs_tol: float=1e-9, rel_tol: float=1e-9, rel_exp: float=1, - rel_to: Sequence[Union[float, Vector2]]=[]): + +def isclose( + x, + y, + abs_tol: float = 1e-9, + rel_tol: float = 1e-9, + rel_exp: float = 1, + rel_to: Sequence[Union[float, Vector2]] = [], +): if rel_exp < 1: raise ValueError(f"Expected rel_exp >= 1, got {rel_exp}") diff = abs(x - y) - rel_max = max(abs(x), abs(y), - *(abs(z) ** rel_exp for z in rel_to if isinstance(z, float)), - *(z.length ** rel_exp for z in rel_to if isinstance(z, Vector2)) + rel_max = max( + abs(x), + abs(y), + *(abs(z) ** rel_exp for z in rel_to if isinstance(z, float)), + *(z.length ** rel_exp for z in rel_to if isinstance(z, Vector2)), ) note(f"rel_max = {rel_max}") if rel_max > 0: @@ -47,49 +61,28 @@ def isclose(x, y, abs_tol: float=1e-9, rel_tol: float=1e-9, rel_exp: float=1, # List of operations that (Vector2, Vector2) -> Vector2 -BINARY_OPS = [ - Vector2.__add__, - Vector2.__sub__, - Vector2.reflect, -] +BINARY_OPS = [Vector2.__add__, Vector2.__sub__, Vector2.reflect] # List of (Vector2, Vector2) -> scalar operations -BINARY_SCALAR_OPS = [ - Vector2.angle, - Vector2.dot, -] +BINARY_SCALAR_OPS = [Vector2.angle, Vector2.dot] # List of (Vector2, Vector2) -> bool operations -BOOL_OPS = [ - Vector2.__eq__, - Vector2.isclose, -] +BOOL_OPS = [Vector2.__eq__, Vector2.isclose] # List of operations that (Vector2, Real) -> Vector2 -SCALAR_OPS = [ - Vector2.rotate, - Vector2.scale_by, - Vector2.scale_to, - Vector2.truncate, -] +SCALAR_OPS = [Vector2.rotate, Vector2.scale_by, Vector2.scale_to, Vector2.truncate] # List of operations that (Vector2) -> Vector2 -UNARY_OPS = [ - Vector2.__neg__, - Vector2.convert, - Vector2.normalize, -] +UNARY_OPS = [Vector2.__neg__, Vector2.convert, Vector2.normalize] # List of (Vector2) -> scalar operations UNARY_SCALAR_OPS = [ - Vector2.length.fget, # type: ignore - # mypy fails to typecheck properties' attributes: - # https://github.com/python/mypy/issues/220 + Vector2.length.fget, # type: ignore + # mypy fails to typecheck properties' attributes: + # https://github.com/python/mypy/issues/220 ] # Sequence of vector-likes equivalent to the input vector (def. to the x vector) -def vector_likes(v: Vector2=Vector2(1, 0)): - return ( - (v.x, v.y), [v.x, v.y], {"x": v.x, "y": v.y} - ) +def vector_likes(v: Vector2 = Vector2(1, 0)): + return ((v.x, v.y), [v.x, v.y], {"x": v.x, "y": v.y})