Skip to content

Commit 4a7c62a

Browse files
committed
TST, fix for issue pandas-dev#17978.
Addition of "hypothesis usage" in test cases of tests/reshape/test_util.py as kind of POC.
1 parent 766a480 commit 4a7c62a

File tree

1 file changed

+92
-13
lines changed

1 file changed

+92
-13
lines changed

pandas/tests/reshape/test_util.py

+92-13
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,101 @@
44
import pandas.util.testing as tm
55
from pandas.core.reshape.util import cartesian_product
66

7+
from hypothesis import strategies as st
8+
from hypothesis import given, settings, assume
9+
from datetime import date
10+
from dateutil import relativedelta
11+
import string
12+
13+
14+
NO_OF_EXAMPLES_PER_TEST_CASE = 20
15+
16+
17+
def get_elements(elem_type):
18+
strategy = st.nothing()
19+
if elem_type == bool:
20+
strategy = st.booleans()
21+
elif elem_type == int:
22+
strategy = st.integers()
23+
elif elem_type == float:
24+
strategy = st.floats()
25+
elif elem_type == str:
26+
strategy = st.text(string.ascii_letters, max_size=10)
27+
return strategy
28+
29+
30+
@st.composite
31+
def get_seq(draw, types, mixed=False, min_size=None, max_size=None, transform_func=None):
32+
"""helper function to generate strategy for creating lists. parameters define the nature of to be generated list.
33+
:param types: what type of elements constitute the list
34+
:param mixed: if True, list will contains elements from all types listed in arg, oterwise it will have elements only from types[0].
35+
:param min_size: minimum size of the list.
36+
:param max_size: maximum size of the list.
37+
:param transform_func: a callable which can be applied to whole list after it has been generated.
38+
"""
39+
strategy = st.nothing()
40+
if min_size is None:
41+
min_size = draw(st.integers(min_value=0, max_value=100))
42+
43+
if max_size is None:
44+
max_size = draw(st.integers(min_value=min_size, max_value=100))
45+
46+
assert min_size <= max_size, 'max_size must be greater than equal to min_size'
47+
48+
elem_strategies = []
49+
for elem_type in types:
50+
elem_strategies.append(get_elements(elem_type))
51+
if not mixed:
52+
break
53+
54+
if transform_func:
55+
strategy = draw(st.lists(st.one_of(elem_strategies),
56+
min_size=min_size, max_size=max_size).map(transform_func))
57+
else:
58+
strategy = draw(st.lists(st.one_of(elem_strategies),
59+
min_size=min_size, max_size=max_size))
60+
return strategy
61+
762

863
class TestCartesianProduct(object):
964

10-
def test_simple(self):
11-
x, y = list('ABC'), [1, 22]
65+
@settings(max_examples=NO_OF_EXAMPLES_PER_TEST_CASE)
66+
@given(get_seq((str,), False, 1, 1),
67+
get_seq((int,), False, 1, 2))
68+
def test_simple(self, x, y):
69+
x = list(x[0])
70+
# non-empty test case is handled in test_empty, therefore ignore it here
71+
assume(len(x) != 0)
1272
result1, result2 = cartesian_product([x, y])
13-
expected1 = np.array(['A', 'A', 'B', 'B', 'C', 'C'])
14-
expected2 = np.array([1, 22, 1, 22, 1, 22])
73+
expected1 = np.array([item1 for item1 in x for item2 in y])
74+
expected2 = np.array([item2 for item1 in x for item2 in y])
75+
1576
tm.assert_numpy_array_equal(result1, expected1)
1677
tm.assert_numpy_array_equal(result2, expected2)
1778

79+
@settings(max_examples=NO_OF_EXAMPLES_PER_TEST_CASE)
1880
def test_datetimeindex(self):
1981
# regression test for GitHub issue #6439
2082
# make sure that the ordering on datetimeindex is consistent
21-
x = date_range('2000-01-01', periods=2)
83+
d = st.dates(min_value=date(1900, 1, 1), max_value=date(2100, 1, 1)).example()
84+
n = d + relativedelta.relativedelta(days=1)
85+
x = date_range(d, periods=2)
2286
result1, result2 = [Index(y).day for y in cartesian_product([x, x])]
23-
expected1 = Index([1, 1, 2, 2])
24-
expected2 = Index([1, 2, 1, 2])
87+
expected1 = Index([d.day, d.day, n.day, n.day])
88+
expected2 = Index([d.day, n.day, d.day, n.day])
89+
2590
tm.assert_index_equal(result1, expected1)
2691
tm.assert_index_equal(result2, expected2)
2792

28-
def test_empty(self):
93+
@settings(max_examples=NO_OF_EXAMPLES_PER_TEST_CASE)
94+
@given(st.lists(st.nothing()),
95+
get_seq((int,), False),
96+
get_seq((str,), False))
97+
def test_empty(self, empty_list, list_of_int, list_of_str):
2998
# product of empty factors
30-
X = [[], [0, 1], []]
31-
Y = [[], [], ['a', 'b', 'c']]
99+
X = [empty_list, list_of_int, empty_list]
100+
Y = [empty_list, empty_list, list_of_str]
101+
32102
for x, y in zip(X, Y):
33103
expected1 = np.array([], dtype=np.asarray(x).dtype)
34104
expected2 = np.array([], dtype=np.asarray(y).dtype)
@@ -37,13 +107,22 @@ def test_empty(self):
37107
tm.assert_numpy_array_equal(result2, expected2)
38108

39109
# empty product (empty input):
40-
result = cartesian_product([])
110+
result = cartesian_product(empty_list)
41111
expected = []
42112
assert result == expected
43113

114+
@settings(max_examples=NO_OF_EXAMPLES_PER_TEST_CASE)
44115
def test_invalid_input(self):
45-
invalid_inputs = [1, [1], [1, 2], [[1], 2],
46-
'a', ['a'], ['a', 'b'], [['a'], 'b']]
116+
invalid_inputs = [st.integers().example(),
117+
st.tuples(st.integers()).example(),
118+
st.tuples(st.integers(), st.integers()).example(),
119+
st.text(string.ascii_letters, min_size=1, max_size=1).example(),
120+
st.tuples(st.text(string.ascii_letters, min_size=1, max_size=1)).example(),
121+
st.tuples(st.text(string.ascii_letters, min_size=1, max_size=1),
122+
st.text(string.ascii_letters, min_size=1, max_size=1)).example(),
123+
st.tuples(st.tuples(st.text(string.ascii_letters, min_size=1, max_size=1)),
124+
st.text(string.ascii_letters, min_size=1, max_size=1)).example()]
125+
47126
msg = "Input must be a list-like of list-likes"
48127
for X in invalid_inputs:
49128
tm.assert_raises_regex(TypeError, msg, cartesian_product, X=X)

0 commit comments

Comments
 (0)