Skip to content

Commit

Permalink
Docstrings and Array API compliance in test_arrays.py
Browse files Browse the repository at this point in the history
  • Loading branch information
honno committed Aug 31, 2021
1 parent ad4a474 commit d98ce1e
Showing 1 changed file with 70 additions and 66 deletions.
136 changes: 70 additions & 66 deletions hypothesis-python/tests/array_api/test_arrays.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,13 @@ def test_minimize_float_arrays():
assert xp.sum(smallest) in (1, 50)


def test_minimizes_to_fill():
"""Strategy with single fill value minimizes to arrays only containing said
fill value."""
smallest = minimal(xps.arrays(xp.float32, 10, fill=st.just(3.0)))
assert xp.all(smallest == 3.0)


def count_unique(array):
"""Returns the number of unique elements.
NaN values are treated as unique to each other.
Expand All @@ -201,6 +208,18 @@ def count_unique(array):
return n_unique


@given(
xps.arrays(
dtype=xp.float32,
elements=st.just(xp.nan),
shape=xps.array_shapes(),
)
)
def test_count_unique(array):
"""Utility counts unique elements of arrays generated by unique strategy."""
assert count_unique(array) == array.size


@given(xps.arrays(xp.int8, st.integers(0, 20), unique=True))
def test_generate_unique_arrays(array):
"""Generates unique arrays."""
Expand All @@ -209,8 +228,8 @@ def test_generate_unique_arrays(array):


def test_cannot_draw_unique_arrays_with_too_small_elements():
"""Unique strategy with elements range smaller than its size raises helpful
error."""
"""Unique strategy with elements strategy range smaller than its size raises
helpful error."""
strat = xps.arrays(xp.int8, 10, elements=st.integers(0, 5), unique=True)
with pytest.raises(Unsatisfiable):
strat.example()
Expand Down Expand Up @@ -239,8 +258,8 @@ def test_generate_unique_arrays_with_high_collision_elements(array):

@given(xps.arrays(xp.int8, (4,), elements=st.integers(0, 3), unique=True))
def test_generate_unique_arrays_using_all_elements(array):
"""Unique strategy with elements range equal to its size will only generate
arrays with one of each possible elements."""
"""Unique strategy with elements strategy range equal to its size will only
generate arrays with one of each possible element."""
if hasattr(xp, "unique"):
assert count_unique(array) == array.size

Expand Down Expand Up @@ -283,6 +302,8 @@ def test_may_not_fill_unique_array_with_non_nan(_):
@fails_with(InvalidArgument)
@given(st.data())
def test_may_not_use_overflowing_integers(kwargs, data):
"""Strategy with elements strategy range outside the dtype's bounds raises
helpful error."""
strat = xps.arrays(dtype=xp.int8, shape=1, **kwargs)
data.draw(strat)

Expand All @@ -298,6 +319,7 @@ def test_may_not_use_overflowing_integers(kwargs, data):
@fails_with(InvalidArgument)
@given(st.data())
def test_may_not_use_unrepresentable_elements(fill, dtype, strat, data):
"""Strategy with elements not representable by the dtype raises helpful error."""
if fill:
kw = {"elements": st.nothing(), "fill": strat}
else:
Expand All @@ -309,7 +331,9 @@ def test_may_not_use_unrepresentable_elements(fill, dtype, strat, data):
@given(
xps.arrays(dtype=xp.float32, shape=10, elements={"min_value": 0, "max_value": 1})
)
def test_floats_can_be_constrained_at_low_width(array):
def test_floats_can_be_constrained(array):
"""Strategy with float dtype and specified elements strategy range
(inclusive) generates arrays with elements inside said range."""
assert xp.all(array >= 0)
assert xp.all(array <= 1)

Expand All @@ -326,22 +350,13 @@ def test_floats_can_be_constrained_at_low_width(array):
},
)
)
def test_floats_can_be_constrained_at_low_width_excluding_endpoints(array):
def test_floats_can_be_constrained_excluding_endpoints(array):
"""Strategy with float dtype and specified elements strategy range
(exclusive) generates arrays with elements inside said range."""
assert xp.all(array > 0)
assert xp.all(array < 1)


@given(
xps.arrays(
dtype=xp.float32,
elements=st.just(xp.nan),
shape=xps.array_shapes(),
)
)
def test_count_unique(array):
assert count_unique(array) == array.size


@given(
xps.arrays(
dtype=xp.float32,
Expand All @@ -352,6 +367,7 @@ def test_count_unique(array):
)
)
def test_is_still_unique_with_nan_fill(array):
"""Unique strategy with NaN fill generates unique arrays."""
if hasattr(xp, "unique"):
assert count_unique(array) == array.size

Expand All @@ -366,26 +382,51 @@ def test_is_still_unique_with_nan_fill(array):
)
)
def test_unique_array_with_fill_can_use_all_elements(array):
"""Unique strategy with elements range equivalent to its size can generate
arrays with all possible values."""
if hasattr(xp, "unique"):
assume(count_unique(array) == array.size)


@given(xps.arrays(dtype=xp.uint8, shape=25, unique=True, fill=st.nothing()))
def test_unique_array_without_fill(array):
# This test covers the collision-related branches for fully dense unique arrayays.
# Choosing 25 of 256 possible elements means we're almost certain to see colisions
# thanks to the 'birthday paradox', but finding unique elemennts is still easy.
def test_generate_unique_arrays_without_fill(array):
"""Generate arrays from unique strategy with no fill.
Covers the collision-related branches for fully dense unique arrays.
Choosing 25 of 256 possible values means we're almost certain to see
colisions thanks to the birthday paradox, but finding unique values should
still be easy.
"""
if hasattr(xp, "unique"):
assume(count_unique(array) == array.size)


@given(xps.arrays(xp.bool, (), fill=st.nothing()))
def test_can_generate_0d_arrays_with_no_fill(array):
def test_generate_0d_arrays_with_no_fill(array):
"""Generate arrays with zero-dimensions and no fill."""
assert array.dtype == xp.bool
assert array.ndim == 0
assert array.shape == ()


@pytest.mark.parametrize("dtype", [xp.float32, xp.float64])
@pytest.mark.parametrize("low", [-2.0, -1.0, 0.0, 1.0])
@given(st.data())
def test_excluded_min_in_float_arrays(dtype, low, data):
"""Strategy with elements strategy excluding min does not generate arrays
with elements less or equal to said min."""
strat = xps.arrays(
dtype=dtype,
shape=(),
elements={
"min_value": low,
"max_value": low + 1,
"exclude_min": True,
},
)
array = data.draw(strat, label="array")
assert xp.all(array > low)


@st.composite
def distinct_integers(draw):
used = draw(st.shared(st.builds(set), key="distinct_integers.used"))
Expand All @@ -396,58 +437,21 @@ def distinct_integers(draw):

@given(xps.arrays(xp.uint64, 10, elements=distinct_integers()))
def test_does_not_reuse_distinct_integers(array):
# xp.unique() is optional for Array API libraries
"""Strategy with distinct integer elements strategy generates arrays with
distinct values."""
if hasattr(xp, "unique"):
unique_values = xp.unique(array)
assert unique_values.size == array.size
assert count_unique(array) == array.size


def test_may_reuse_distinct_integers_if_asked():
"""Strategy with shared elements and fill strategies of distinct integers
may generate arrays with non-distinct values."""
if hasattr(xp, "unique"):

def nunique(array):
unique_values = xp.unique(array)
return unique_values.size

find_any(
xps.arrays(
xp.uint64, 10, elements=distinct_integers(), fill=distinct_integers()
),
lambda x: nunique(x) < len(x),
lambda x: count_unique(x) < x.size,
)
else:
pytest.skip()


def test_minimizes_to_fill():
smallest = minimal(xps.arrays(xp.float32, 10, fill=st.just(3.0)))
assert xp.all(smallest == 3.0)


@given(
xps.arrays(
dtype=xp.float32,
elements=st.floats(width=32).filter(bool),
shape=(3, 3, 3),
fill=st.just(1.0),
)
)
def test_fills_everything(array):
assert xp.all(array)


@pytest.mark.parametrize("dtype", [xp.float32, xp.float64])
@pytest.mark.parametrize("low", [-2.0, -1.0, 0.0, 1.0])
@given(st.data())
def test_bad_float_exclude_min_in_array(dtype, low, data):
strat = xps.arrays(
dtype=dtype,
shape=(),
elements={
"min_value": low,
"max_value": low + 1,
"exclude_min": True,
},
)
array = data.draw(strat, label="array")
assert array > low

0 comments on commit d98ce1e

Please sign in to comment.