1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
| class TestArgumentAndReturn:
"""Test arguments and return value."""
def test_missing_arguments(self, get_function_to_test):
"""Should raise `TypeError` if required arguments are missing."""
f = get_function_to_test
with pytest.raises(TypeError):
assert not f()
assert not f("abc")
assert not f("abc", "a")
def test_unimplemented_input_validation(self, get_function_to_test):
"""Test for unimplemented input validation.
Should raise `AttributeError` if first argument is not indexable.
"""
f = get_function_to_test
with pytest.raises(AttributeError):
assert not f(123, 4, 5)
def test_return_datatype(self, get_function_to_test):
"""Return value type should be `bool`."""
f = get_function_to_test
assert isinstance(f("a", "b", "c"), bool)
class TestMeaninglessResults:
"""Test edge case inputs producing meaningless results."""
def test_truncated_input(self, get_function_to_test):
"""Should return False if length of first arg is < 2 chars."""
f = get_function_to_test
assert not f("a", "b", "c")
class TestMeaningfulResults:
"""Test expected input producing meaningful results."""
def test_identical_symbols(self, get_function_to_test):
"""Should return False if second and third args are identical."""
f = get_function_to_test
assert not f("abc", "a", "a")
def test_symbols_not_in_string(self, get_function_to_test):
"""Should correctly evaluate missing symbols.
Should return False if second and third args are not found
in first arg.
"""
f = get_function_to_test
assert not f("aa", "a", "x")
assert not f("aa", "x", "a")
assert not f("aa", "x", "x")
def test_symbols_out_of_order(self, get_function_to_test):
"""Should correctly evaluate symbols out of order.
Should return False if second and third args are found in first
arg, but not in the desired order.
"""
f = get_function_to_test
assert not f("abcde", "c", "a")
assert not f("abcde", "e", "a")
def test_case_sensitivity(self, get_function_to_test):
"""Detection of second and third args should be case sensitive."""
f = get_function_to_test
assert f("abc", "a", "b")
assert not f("abc", "A", "b")
assert not f("abc", "a", "B")
class TestProvidedTests:
"""Tests provided with problem."""
@pytest.mark.parametrize(
"s, sym_a, sym_b, expected",
[
("", "l", "o", False),
("almaz", "a", "l", True),
("almaz", "m", "a", False),
("almaz", "p", "p", False),
("almaz", "r", "a", False),
("almaz", "r", "l", False),
("list", "l", "l", False),
("list", "l", "o", False),
("panorama", "a", "n", True),
("world", "d", "w", False),
("world", "l", "o", False),
("world", "w", "o", True),
("world", "w", "r", False),
],
)
def test_provided_tests(
self, get_function_to_test, s, sym_a, sym_b, expected
):
"""Should correctly evaluate provided test data."""
f = get_function_to_test
assert f(s, sym_a, sym_b) == expected
class SequenceHelpers:
"""Helper functions for use with generated sequences."""
def __choose(self, excl: Tuple[str]) -> str:
"""Return a randomly chosen character.
If that character is one a designated excluded character, return an empty string.
"""
c = choice(ascii_lowercase)
return c if c not in excl else ""
def __middle_two(self, s: str) -> str:
"""Return a subsequence of two chars from the input string."""
mid: int = len(s) // 2
return s[mid: mid + 2]
def gen_excl(self) -> Tuple[str]:
"""Return a tuple of randomly chosen characters."""
a = choice(ascii_lowercase)
b = choice(ascii_lowercase)
while b == a:
b = choice(ascii_lowercase)
return (a, b)
def seq(self, start: int, end: int, excl: Tuple[str]) -> str:
"""Return a sequence of randomly chosen characters."""
return "".join(
SequenceHelpers.__choose(self, excl)
for x in range(randint(start, end))
)
def seq_pass(self, data: List[str]) -> Tuple[List[str], Tuple[str]]:
"""Return a passing sequence with excluded characters."""
seq = SequenceHelpers.__middle_two(self, data)
excl = tuple(seq)
log.info(f"with passing sequence '{seq}' in '{data}'")
return data, excl
def seq_fail(self, data: List[str]) -> Tuple[List[str], Tuple[str]]:
"""Return a failing sequence with excluded characters."""
data = data[0] + data
seq = data[1:3]
excl = tuple(seq)
log.info(f"with failing sequence '{seq}' in '{data}'")
return data, excl
class TestGeneratedSequences:
"""Tests of generated sequences."""
def __seq(self, begin: int, end: int, excl: Tuple[str]) -> str:
"""Construct a testable sequence."""
return SequenceHelpers.seq(self, begin, end, excl)
@pytest.mark.parametrize("begin, end", [(5, 10), (10, 12), (12, 15)])
def test_seqs(self, get_function_to_test, begin, end):
"""Should correctly evaluate randomly generated sequences."""
f = get_function_to_test
excl = SequenceHelpers.gen_excl(self)
mystr = (
self.__seq(begin, end, excl)
+ "".join(excl)
+ self.__seq(begin, end, excl)
)
log.info(f"with passing sequence '{''.join(excl)}' in '{mystr}'")
assert f(mystr, *excl)
excl = (choice(ascii_lowercase), choice(ascii_uppercase))
log.info(f"with failing sequence '{''.join(excl)}' in '{mystr}'")
assert not f(mystr, *excl)
class TestFakerTests:
"""Tests of sequences generated using Faker.
See https://faker.readthedocs.io.
"""
kwargs = {
"nb_elements": Faker().pyint(5, 15),
"variable_nb_elements": True,
"value_types": "str",
}
ss = Faker().pyset(**kwargs)
data: List[str] = ["".join(set(list(x))) for x in ss]
def test_seqs(self, get_function_to_test):
"""Should correctly evaluate generated sequences."""
f = get_function_to_test
for _ in self.data:
s, excl = SequenceHelpers.seq_pass(self, _)
assert f(s, *excl)
s, excl = SequenceHelpers.seq_fail(self, _)
assert not f(s, *excl)
class TestPropertyTests:
"""Tests of sequences generated using Hypothesis.
See https://hypothesis.readthedocs.io/.
"""
__given_data = given(permutations(ascii_lowercase))
@__given_data
def test_seqs(self, get_function_to_test, data):
"""Should correctly evaluate generated sequences."""
f = get_function_to_test
data = "".join(data)
s, excl = SequenceHelpers.seq_pass(self, data)
assert f(s, *excl)
s, excl = SequenceHelpers.seq_fail(self, data)
assert not f(s, *excl)
class TestBenchmarkTests:
"""Tests for use with pytest-benchmark.
See https://pytest-benchmark.readthedocs.io/.
"""
def test_benchmark(self, get_function_to_test, benchmark):
"""Should correctly evaluate a generated sequence."""
f = get_function_to_test
s = "a" * 1_000_000 + "bc"
result = benchmark(lambda: f(s, "b", "c"))
assert result
|