forked from twisted/twisted
-
Notifications
You must be signed in to change notification settings - Fork 0
/
test_matchers.py
188 lines (156 loc) · 6.09 KB
/
test_matchers.py
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
"""
Tests for L{twisted.trial._dist.test.matchers}.
"""
from typing import Callable, Sequence, Tuple, Type
from hamcrest import anything, assert_that, contains, contains_string, equal_to, not_
from hamcrest.core.matcher import Matcher
from hamcrest.core.string_description import StringDescription
from hypothesis import given
from hypothesis.strategies import (
binary,
booleans,
integers,
just,
lists,
one_of,
sampled_from,
text,
tuples,
)
from twisted.python.failure import Failure
from twisted.trial.unittest import SynchronousTestCase
from .matchers import HasSum, IsSequenceOf, S, isFailure, similarFrame
Summer = Callable[[Sequence[S]], S]
concatInt = sum
concatStr = "".join
concatBytes = b"".join
class HasSumTests(SynchronousTestCase):
"""
Tests for L{HasSum}.
"""
summables = one_of(
tuples(lists(integers()), just(concatInt)),
tuples(lists(text()), just(concatStr)),
tuples(lists(binary()), just(concatBytes)),
)
@given(summables)
def test_matches(self, summable: Tuple[Sequence[S], Summer[S]]) -> None:
"""
L{HasSum} matches a sequence if the elements sum to a value matched by
the parameterized matcher.
:param summable: A tuple of a sequence of values to try to match and a
function which can compute the correct sum for that sequence.
"""
seq, sumFunc = summable
expected = sumFunc(seq)
zero = sumFunc([])
matcher = HasSum(equal_to(expected), zero)
description = StringDescription()
assert_that(matcher.matches(seq, description), equal_to(True))
assert_that(str(description), equal_to(""))
@given(summables)
def test_mismatches(
self,
summable: Tuple[
Sequence[S],
Summer[S],
],
) -> None:
"""
L{HasSum} does not match a sequence if the elements do not sum to a
value matched by the parameterized matcher.
:param summable: See L{test_matches}.
"""
seq, sumFunc = summable
zero = sumFunc([])
# A matcher that never matches.
sumMatcher: Matcher[S] = not_(anything())
matcher = HasSum(sumMatcher, zero)
actualDescription = StringDescription()
assert_that(matcher.matches(seq, actualDescription), equal_to(False))
sumMatcherDescription = StringDescription()
sumMatcherDescription.append_description_of(sumMatcher)
actualStr = str(actualDescription)
assert_that(actualStr, contains_string("a sequence with sum"))
assert_that(actualStr, contains_string(str(sumMatcherDescription)))
class IsSequenceOfTests(SynchronousTestCase):
"""
Tests for L{IsSequenceOf}.
"""
sequences = lists(booleans())
@given(integers(min_value=0, max_value=1000))
def test_matches(self, numItems: int) -> None:
"""
L{IsSequenceOf} matches a sequence if all of the elements are
matched by the parameterized matcher.
:param numItems: The length of a sequence to try to match.
"""
seq = [True] * numItems
matcher = IsSequenceOf(equal_to(True))
actualDescription = StringDescription()
assert_that(matcher.matches(seq, actualDescription), equal_to(True))
assert_that(str(actualDescription), equal_to(""))
@given(integers(min_value=0, max_value=1000), integers(min_value=0, max_value=1000))
def test_mismatches(self, numBefore: int, numAfter: int) -> None:
"""
L{IsSequenceOf} does not match a sequence if any of the elements
are not matched by the parameterized matcher.
:param numBefore: In the sequence to try to match, the number of
elements expected to match before an expected mismatch.
:param numAfter: In the sequence to try to match, the number of
elements expected expected to match after an expected mismatch.
"""
# Hide the non-matching value somewhere in the sequence.
seq = [True] * numBefore + [False] + [True] * numAfter
matcher = IsSequenceOf(equal_to(True))
actualDescription = StringDescription()
assert_that(matcher.matches(seq, actualDescription), equal_to(False))
actualStr = str(actualDescription)
assert_that(actualStr, contains_string("a sequence containing only"))
assert_that(
actualStr, contains_string(f"not sequence with element #{numBefore}")
)
class IsFailureTests(SynchronousTestCase):
"""
Tests for L{isFailure}.
"""
@given(sampled_from([ValueError, ZeroDivisionError, RuntimeError]))
def test_matches(self, excType: Type[BaseException]) -> None:
"""
L{isFailure} matches instances of L{Failure} with matching
attributes.
:param excType: An exception type to wrap in a L{Failure} to be
matched against.
"""
matcher = isFailure(type=equal_to(excType))
failure = Failure(excType())
assert_that(matcher.matches(failure), equal_to(True))
@given(sampled_from([ValueError, ZeroDivisionError, RuntimeError]))
def test_mismatches(self, excType: Type[BaseException]) -> None:
"""
L{isFailure} does not match instances of L{Failure} with
attributes that don't match.
:param excType: An exception type to wrap in a L{Failure} to be
matched against.
"""
matcher = isFailure(type=equal_to(excType), other=not_(anything()))
failure = Failure(excType())
assert_that(matcher.matches(failure), equal_to(False))
def test_frames(self):
"""
The L{similarFrame} matcher matches elements of the C{frames} list
of a L{Failure}.
"""
try:
raise ValueError("Oh no")
except BaseException:
f = Failure()
actualDescription = StringDescription()
matcher = isFailure(
frames=contains(similarFrame("test_frames", "test_matchers"))
)
assert_that(
matcher.matches(f, actualDescription),
equal_to(True),
actualDescription,
)