assertpy2 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- assertpy2/__init__.py +25 -0
- assertpy2/assertpy.py +468 -0
- assertpy2/base.py +435 -0
- assertpy2/collection.py +217 -0
- assertpy2/contains.py +386 -0
- assertpy2/date.py +219 -0
- assertpy2/dict.py +255 -0
- assertpy2/dynamic.py +113 -0
- assertpy2/exception.py +128 -0
- assertpy2/extracting.py +233 -0
- assertpy2/file.py +276 -0
- assertpy2/helpers.py +273 -0
- assertpy2/numeric.py +555 -0
- assertpy2/py.typed +0 -0
- assertpy2/snapshot.py +217 -0
- assertpy2/string.py +413 -0
- assertpy2-2.0.0.dist-info/METADATA +227 -0
- assertpy2-2.0.0.dist-info/RECORD +20 -0
- assertpy2-2.0.0.dist-info/WHEEL +4 -0
- assertpy2-2.0.0.dist-info/licenses/LICENSE +28 -0
assertpy2/contains.py
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# Copyright (c) 2015-2019, Activision Publishing, Inc.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
|
5
|
+
# are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
# list of conditions and the following disclaimer.
|
|
9
|
+
#
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
# and/or other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# 3. Neither the name of the copyright holder nor the names of its contributors
|
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
|
16
|
+
# specific prior written permission.
|
|
17
|
+
#
|
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
19
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
20
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
21
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
22
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
23
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
24
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
25
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
26
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
27
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from typing import TYPE_CHECKING
|
|
33
|
+
|
|
34
|
+
if TYPE_CHECKING:
|
|
35
|
+
from typing_extensions import Self
|
|
36
|
+
|
|
37
|
+
__tracebackhide__ = True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ContainsMixin:
|
|
41
|
+
"""Containment assertions mixin."""
|
|
42
|
+
|
|
43
|
+
def contains(self, *items) -> Self:
|
|
44
|
+
"""Asserts that val contains the given item or items.
|
|
45
|
+
|
|
46
|
+
Checks if the collection contains the given item or items using ``in`` operator.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
*items: the item or items expected to be contained
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
Usage::
|
|
53
|
+
|
|
54
|
+
assert_that('foo').contains('f')
|
|
55
|
+
assert_that('foo').contains('f', 'oo')
|
|
56
|
+
assert_that(['a', 'b']).contains('b', 'a')
|
|
57
|
+
assert_that((1, 2, 3)).contains(3, 2, 1)
|
|
58
|
+
assert_that({'a': 1, 'b': 2}).contains('b', 'a') # checks keys
|
|
59
|
+
assert_that({'a', 'b'}).contains('b', 'a')
|
|
60
|
+
assert_that([1, 2, 3]).is_type_of(list).contains(1, 2).does_not_contain(4, 5)
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
AssertionError: if val does **not** contain the item or items
|
|
67
|
+
|
|
68
|
+
Tip:
|
|
69
|
+
Use the :meth:`~assertpy.dict.DictMixin.contains_key` alias when working with
|
|
70
|
+
*dict-like* objects to be self-documenting.
|
|
71
|
+
|
|
72
|
+
See Also:
|
|
73
|
+
:meth:`~assertpy.string.StringMixin.contains_ignoring_case` - for case-insensitive string contains
|
|
74
|
+
"""
|
|
75
|
+
if len(items) == 0:
|
|
76
|
+
raise ValueError("one or more args must be given")
|
|
77
|
+
elif len(items) == 1:
|
|
78
|
+
if items[0] not in self.val:
|
|
79
|
+
if self._check_dict_like(self.val, return_as_bool=True):
|
|
80
|
+
return self.error("Expected <%s> to contain key <%s>, but did not." % (self.val, items[0]))
|
|
81
|
+
else:
|
|
82
|
+
return self.error("Expected <%s> to contain item <%s>, but did not." % (self.val, items[0]))
|
|
83
|
+
else:
|
|
84
|
+
missing = []
|
|
85
|
+
for i in items:
|
|
86
|
+
if i not in self.val:
|
|
87
|
+
missing.append(i)
|
|
88
|
+
if missing:
|
|
89
|
+
if self._check_dict_like(self.val, return_as_bool=True):
|
|
90
|
+
return self.error(
|
|
91
|
+
"Expected <%s> to contain keys %s, but did not contain key%s %s."
|
|
92
|
+
% (self.val, self._fmt_items(items), "" if len(missing) == 0 else "s", self._fmt_items(missing))
|
|
93
|
+
)
|
|
94
|
+
else:
|
|
95
|
+
return self.error(
|
|
96
|
+
"Expected <%s> to contain items %s, but did not contain %s."
|
|
97
|
+
% (self.val, self._fmt_items(items), self._fmt_items(missing))
|
|
98
|
+
)
|
|
99
|
+
return self
|
|
100
|
+
|
|
101
|
+
def does_not_contain(self, *items) -> Self:
|
|
102
|
+
"""Asserts that val does not contain the given item or items.
|
|
103
|
+
|
|
104
|
+
Checks if the collection excludes the given item or items using ``in`` operator.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
*items: the item or items expected to be excluded
|
|
108
|
+
|
|
109
|
+
Examples:
|
|
110
|
+
Usage::
|
|
111
|
+
|
|
112
|
+
assert_that('foo').does_not_contain('x')
|
|
113
|
+
assert_that(['a', 'b']).does_not_contain('x', 'y')
|
|
114
|
+
assert_that((1, 2, 3)).does_not_contain(4, 5)
|
|
115
|
+
assert_that({'a': 1, 'b': 2}).does_not_contain('x', 'y') # checks keys
|
|
116
|
+
assert_that({'a', 'b'}).does_not_contain('x', 'y')
|
|
117
|
+
assert_that([1, 2, 3]).is_type_of(list).contains(1, 2).does_not_contain(4, 5)
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
AssertionError: if val **does** contain the item or items
|
|
124
|
+
|
|
125
|
+
Tip:
|
|
126
|
+
Use the :meth:`~assertpy.dict.DictMixin.does_not_contain_key` alias when working with
|
|
127
|
+
*dict-like* objects to be self-documenting.
|
|
128
|
+
"""
|
|
129
|
+
if len(items) == 0:
|
|
130
|
+
raise ValueError("one or more args must be given")
|
|
131
|
+
elif len(items) == 1:
|
|
132
|
+
if items[0] in self.val:
|
|
133
|
+
return self.error("Expected <%s> to not contain item <%s>, but did." % (self.val, items[0]))
|
|
134
|
+
else:
|
|
135
|
+
found = []
|
|
136
|
+
for i in items:
|
|
137
|
+
if i in self.val:
|
|
138
|
+
found.append(i)
|
|
139
|
+
if found:
|
|
140
|
+
return self.error(
|
|
141
|
+
"Expected <%s> to not contain items %s, but did contain %s."
|
|
142
|
+
% (self.val, self._fmt_items(items), self._fmt_items(found))
|
|
143
|
+
)
|
|
144
|
+
return self
|
|
145
|
+
|
|
146
|
+
def contains_only(self, *items) -> Self:
|
|
147
|
+
"""Asserts that val contains *only* the given item or items.
|
|
148
|
+
|
|
149
|
+
Checks if the collection contains only the given item or items using ``in`` operator.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
*items: the *only* item or items expected to be contained
|
|
153
|
+
|
|
154
|
+
Examples:
|
|
155
|
+
Usage::
|
|
156
|
+
|
|
157
|
+
assert_that('foo').contains_only('f', 'o')
|
|
158
|
+
assert_that(['a', 'a', 'b']).contains_only('a', 'b')
|
|
159
|
+
assert_that((1, 1, 2)).contains_only(1, 2)
|
|
160
|
+
assert_that({'a': 1, 'a': 2, 'b': 3}).contains_only('a', 'b')
|
|
161
|
+
assert_that({'a', 'a', 'b'}).contains_only('a', 'b')
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
AssertionError: if val contains anything **not** item or items
|
|
168
|
+
"""
|
|
169
|
+
if len(items) == 0:
|
|
170
|
+
raise ValueError("one or more args must be given")
|
|
171
|
+
else:
|
|
172
|
+
extra = []
|
|
173
|
+
for i in self.val:
|
|
174
|
+
if i not in items:
|
|
175
|
+
extra.append(i)
|
|
176
|
+
if extra:
|
|
177
|
+
return self.error(
|
|
178
|
+
"Expected <%s> to contain only %s, but did contain %s."
|
|
179
|
+
% (self.val, self._fmt_items(items), self._fmt_items(extra))
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
missing = []
|
|
183
|
+
for i in items:
|
|
184
|
+
if i not in self.val:
|
|
185
|
+
missing.append(i)
|
|
186
|
+
if missing:
|
|
187
|
+
return self.error(
|
|
188
|
+
"Expected <%s> to contain only %s, but did not contain %s."
|
|
189
|
+
% (self.val, self._fmt_items(items), self._fmt_items(missing))
|
|
190
|
+
)
|
|
191
|
+
return self
|
|
192
|
+
|
|
193
|
+
def contains_sequence(self, *items) -> Self:
|
|
194
|
+
"""Asserts that val contains the given ordered sequence of items.
|
|
195
|
+
|
|
196
|
+
Checks if the collection contains the given sequence of items using ``in`` operator.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
*items: the sequence of items expected to be contained
|
|
200
|
+
|
|
201
|
+
Examples:
|
|
202
|
+
Usage::
|
|
203
|
+
|
|
204
|
+
assert_that('foo').contains_sequence('f', 'o')
|
|
205
|
+
assert_that('foo').contains_sequence('o', 'o')
|
|
206
|
+
assert_that(['a', 'b', 'c']).contains_sequence('b', 'c')
|
|
207
|
+
assert_that((1, 2, 3)).contains_sequence(1, 2)
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
211
|
+
|
|
212
|
+
Raises:
|
|
213
|
+
AssertionError: if val does **not** contains the given sequence of items
|
|
214
|
+
"""
|
|
215
|
+
if len(items) == 0:
|
|
216
|
+
raise ValueError("one or more args must be given")
|
|
217
|
+
if isinstance(self.val, str):
|
|
218
|
+
pos = 0
|
|
219
|
+
for item in items:
|
|
220
|
+
if not isinstance(item, str):
|
|
221
|
+
raise TypeError("given args must be strings when val is a string")
|
|
222
|
+
idx = self.val.find(item, pos)
|
|
223
|
+
if idx == -1:
|
|
224
|
+
return self.error(
|
|
225
|
+
"Expected <%s> to contain sequence %s, but did not." % (self.val, self._fmt_items(items))
|
|
226
|
+
)
|
|
227
|
+
pos = idx + len(item)
|
|
228
|
+
return self
|
|
229
|
+
try:
|
|
230
|
+
for i in range(len(self.val) - len(items) + 1):
|
|
231
|
+
for j in range(len(items)):
|
|
232
|
+
if self.val[i + j] != items[j]:
|
|
233
|
+
break
|
|
234
|
+
else:
|
|
235
|
+
return self
|
|
236
|
+
except TypeError:
|
|
237
|
+
raise TypeError("val is not iterable") from None
|
|
238
|
+
return self.error("Expected <%s> to contain sequence %s, but did not." % (self.val, self._fmt_items(items)))
|
|
239
|
+
|
|
240
|
+
def contains_duplicates(self) -> Self:
|
|
241
|
+
"""Asserts that val is iterable and *does* contain duplicates.
|
|
242
|
+
|
|
243
|
+
Examples:
|
|
244
|
+
Usage::
|
|
245
|
+
|
|
246
|
+
assert_that('foo').contains_duplicates()
|
|
247
|
+
assert_that(['a', 'a', 'b']).contains_duplicates()
|
|
248
|
+
assert_that((1, 1, 2)).contains_duplicates()
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
252
|
+
|
|
253
|
+
Raises:
|
|
254
|
+
AssertionError: if val does **not** contain any duplicates
|
|
255
|
+
"""
|
|
256
|
+
try:
|
|
257
|
+
if len(self.val) != len(set(self.val)):
|
|
258
|
+
return self
|
|
259
|
+
except TypeError:
|
|
260
|
+
raise TypeError("val is not iterable") from None
|
|
261
|
+
return self.error("Expected <%s> to contain duplicates, but did not." % self.val)
|
|
262
|
+
|
|
263
|
+
def does_not_contain_duplicates(self) -> Self:
|
|
264
|
+
"""Asserts that val is iterable and *does not* contain any duplicates.
|
|
265
|
+
|
|
266
|
+
Examples:
|
|
267
|
+
Usage::
|
|
268
|
+
|
|
269
|
+
assert_that('fox').does_not_contain_duplicates()
|
|
270
|
+
assert_that(['a', 'b', 'c']).does_not_contain_duplicates()
|
|
271
|
+
assert_that((1, 2, 3)).does_not_contain_duplicates()
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
275
|
+
|
|
276
|
+
Raises:
|
|
277
|
+
AssertionError: if val **does** contain duplicates
|
|
278
|
+
"""
|
|
279
|
+
try:
|
|
280
|
+
if len(self.val) == len(set(self.val)):
|
|
281
|
+
return self
|
|
282
|
+
except TypeError:
|
|
283
|
+
raise TypeError("val is not iterable") from None
|
|
284
|
+
return self.error("Expected <%s> to not contain duplicates, but did." % self.val)
|
|
285
|
+
|
|
286
|
+
def is_empty(self) -> Self:
|
|
287
|
+
"""Asserts that val is empty.
|
|
288
|
+
|
|
289
|
+
Examples:
|
|
290
|
+
Usage::
|
|
291
|
+
|
|
292
|
+
assert_that('').is_empty()
|
|
293
|
+
assert_that([]).is_empty()
|
|
294
|
+
assert_that(()).is_empty()
|
|
295
|
+
assert_that({}).is_empty()
|
|
296
|
+
assert_that(set()).is_empty()
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
300
|
+
|
|
301
|
+
Raises:
|
|
302
|
+
AssertionError: if val is **not** empty
|
|
303
|
+
"""
|
|
304
|
+
if len(self.val) != 0:
|
|
305
|
+
if isinstance(self.val, str):
|
|
306
|
+
return self.error("Expected <%s> to be empty string, but was not." % self.val)
|
|
307
|
+
else:
|
|
308
|
+
return self.error("Expected <%s> to be empty, but was not." % self.val)
|
|
309
|
+
return self
|
|
310
|
+
|
|
311
|
+
def is_not_empty(self) -> Self:
|
|
312
|
+
"""Asserts that val is *not* empty.
|
|
313
|
+
|
|
314
|
+
Examples:
|
|
315
|
+
Usage::
|
|
316
|
+
|
|
317
|
+
assert_that('foo').is_not_empty()
|
|
318
|
+
assert_that(['a', 'b']).is_not_empty()
|
|
319
|
+
assert_that((1, 2, 3)).is_not_empty()
|
|
320
|
+
assert_that({'a': 1, 'b': 2}).is_not_empty()
|
|
321
|
+
assert_that({'a', 'b'}).is_not_empty()
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
325
|
+
|
|
326
|
+
Raises:
|
|
327
|
+
AssertionError: if val **is** empty
|
|
328
|
+
"""
|
|
329
|
+
if len(self.val) == 0:
|
|
330
|
+
if isinstance(self.val, str):
|
|
331
|
+
return self.error("Expected not empty string, but was empty.")
|
|
332
|
+
else:
|
|
333
|
+
return self.error("Expected not empty, but was empty.")
|
|
334
|
+
return self
|
|
335
|
+
|
|
336
|
+
def is_in(self, *items) -> Self:
|
|
337
|
+
"""Asserts that val is equal to one of the given items.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
*items: the items expected to contain val
|
|
341
|
+
|
|
342
|
+
Examples:
|
|
343
|
+
Usage::
|
|
344
|
+
|
|
345
|
+
assert_that('foo').is_in('foo', 'bar', 'baz')
|
|
346
|
+
assert_that(1).is_in(0, 1, 2, 3)
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
350
|
+
|
|
351
|
+
Raises:
|
|
352
|
+
AssertionError: if val is **not** in the given items
|
|
353
|
+
"""
|
|
354
|
+
if len(items) == 0:
|
|
355
|
+
raise ValueError("one or more args must be given")
|
|
356
|
+
else:
|
|
357
|
+
for i in items:
|
|
358
|
+
if self.val == i:
|
|
359
|
+
return self
|
|
360
|
+
return self.error("Expected <%s> to be in %s, but was not." % (self.val, self._fmt_items(items)))
|
|
361
|
+
|
|
362
|
+
def is_not_in(self, *items) -> Self:
|
|
363
|
+
"""Asserts that val is not equal to one of the given items.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
*items: the items expected to exclude val
|
|
367
|
+
|
|
368
|
+
Examples:
|
|
369
|
+
Usage::
|
|
370
|
+
|
|
371
|
+
assert_that('foo').is_not_in('bar', 'baz', 'box')
|
|
372
|
+
assert_that(1).is_not_in(-1, -2, -3)
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
376
|
+
|
|
377
|
+
Raises:
|
|
378
|
+
AssertionError: if val **is** in the given items
|
|
379
|
+
"""
|
|
380
|
+
if len(items) == 0:
|
|
381
|
+
raise ValueError("one or more args must be given")
|
|
382
|
+
else:
|
|
383
|
+
for i in items:
|
|
384
|
+
if self.val == i:
|
|
385
|
+
return self.error("Expected <%s> to not be in %s, but was." % (self.val, self._fmt_items(items)))
|
|
386
|
+
return self
|
assertpy2/date.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Copyright (c) 2015-2019, Activision Publishing, Inc.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
|
5
|
+
# are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
# list of conditions and the following disclaimer.
|
|
9
|
+
#
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
# and/or other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# 3. Neither the name of the copyright holder nor the names of its contributors
|
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
|
16
|
+
# specific prior written permission.
|
|
17
|
+
#
|
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
19
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
20
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
21
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
22
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
23
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
24
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
25
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
26
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
27
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import datetime
|
|
32
|
+
from typing import TYPE_CHECKING
|
|
33
|
+
|
|
34
|
+
if TYPE_CHECKING:
|
|
35
|
+
from typing_extensions import Self
|
|
36
|
+
|
|
37
|
+
__tracebackhide__ = True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class DateMixin:
|
|
41
|
+
"""Date and time assertions mixin."""
|
|
42
|
+
|
|
43
|
+
def is_before(self, other) -> Self:
|
|
44
|
+
"""Asserts that val is a date and is before other date.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
other: the other date, expected to be after val
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
Usage::
|
|
51
|
+
|
|
52
|
+
import datetime
|
|
53
|
+
|
|
54
|
+
today = datetime.datetime.now()
|
|
55
|
+
yesterday = today - datetime.timedelta(days=1)
|
|
56
|
+
|
|
57
|
+
assert_that(yesterday).is_before(today)
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
AssertionError: if val is **not** before the given date
|
|
64
|
+
|
|
65
|
+
See Also:
|
|
66
|
+
:meth:`~assertpy.string.NumericMixin.is_less_than` - numeric assertion, but also works with datetime BR
|
|
67
|
+
:meth:`~assertpy.string.NumericMixin.is_less_than_or_equal_to` - numeric assertion, but also works with datetime
|
|
68
|
+
"""
|
|
69
|
+
if type(self.val) is not datetime.datetime:
|
|
70
|
+
raise TypeError("val must be datetime, but was type <%s>" % type(self.val).__name__)
|
|
71
|
+
if type(other) is not datetime.datetime:
|
|
72
|
+
raise TypeError("given arg must be datetime, but was type <%s>" % type(other).__name__)
|
|
73
|
+
if self.val >= other:
|
|
74
|
+
return self.error(
|
|
75
|
+
"Expected <%s> to be before <%s>, but was not."
|
|
76
|
+
% (self.val.strftime("%Y-%m-%d %H:%M:%S"), other.strftime("%Y-%m-%d %H:%M:%S"))
|
|
77
|
+
)
|
|
78
|
+
return self
|
|
79
|
+
|
|
80
|
+
def is_after(self, other) -> Self:
|
|
81
|
+
"""Asserts that val is a date and is after other date.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
other: the other date, expected to be before val
|
|
85
|
+
|
|
86
|
+
Examples:
|
|
87
|
+
Usage::
|
|
88
|
+
|
|
89
|
+
import datetime
|
|
90
|
+
|
|
91
|
+
today = datetime.datetime.now()
|
|
92
|
+
yesterday = today - datetime.timedelta(days=1)
|
|
93
|
+
|
|
94
|
+
assert_that(today).is_after(yesterday)
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
AssertionError: if val is **not** after the given date
|
|
101
|
+
|
|
102
|
+
See Also:
|
|
103
|
+
:meth:`~assertpy.string.NumericMixin.is_greater_than` - numeric assertion, but also works with datetime BR
|
|
104
|
+
:meth:`~assertpy.string.NumericMixin.is_greater_than_or_equal_to` - numeric assertion, but also works with datetime
|
|
105
|
+
"""
|
|
106
|
+
if type(self.val) is not datetime.datetime:
|
|
107
|
+
raise TypeError("val must be datetime, but was type <%s>" % type(self.val).__name__)
|
|
108
|
+
if type(other) is not datetime.datetime:
|
|
109
|
+
raise TypeError("given arg must be datetime, but was type <%s>" % type(other).__name__)
|
|
110
|
+
if self.val <= other:
|
|
111
|
+
return self.error(
|
|
112
|
+
"Expected <%s> to be after <%s>, but was not."
|
|
113
|
+
% (self.val.strftime("%Y-%m-%d %H:%M:%S"), other.strftime("%Y-%m-%d %H:%M:%S"))
|
|
114
|
+
)
|
|
115
|
+
return self
|
|
116
|
+
|
|
117
|
+
def is_equal_to_ignoring_milliseconds(self, other) -> Self:
|
|
118
|
+
"""Asserts that val is a date and is equal to other date to the second.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
other: the other date, expected to be equal to the second
|
|
122
|
+
|
|
123
|
+
Examples:
|
|
124
|
+
Usage::
|
|
125
|
+
|
|
126
|
+
import datetime
|
|
127
|
+
|
|
128
|
+
d1 = datetime.datetime(2020, 1, 2, 3, 4, 5, 6) # 2020-01-02 03:04:05.000006
|
|
129
|
+
d2 = datetime.datetime(2020, 1, 2, 3, 4, 5, 777777) # 2020-01-02 03:04:05.777777
|
|
130
|
+
|
|
131
|
+
assert_that(d1).is_equal_to_ignoring_milliseconds(d2)
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
135
|
+
|
|
136
|
+
Raises:
|
|
137
|
+
AssertionError: if val is **not** equal to the given date to the second
|
|
138
|
+
"""
|
|
139
|
+
if type(self.val) is not datetime.datetime:
|
|
140
|
+
raise TypeError("val must be datetime, but was type <%s>" % type(self.val).__name__)
|
|
141
|
+
if type(other) is not datetime.datetime:
|
|
142
|
+
raise TypeError("given arg must be datetime, but was type <%s>" % type(other).__name__)
|
|
143
|
+
if (
|
|
144
|
+
self.val.date() != other.date()
|
|
145
|
+
or self.val.hour != other.hour
|
|
146
|
+
or self.val.minute != other.minute
|
|
147
|
+
or self.val.second != other.second
|
|
148
|
+
):
|
|
149
|
+
return self.error(
|
|
150
|
+
"Expected <%s> to be equal to <%s>, but was not."
|
|
151
|
+
% (self.val.strftime("%Y-%m-%d %H:%M:%S"), other.strftime("%Y-%m-%d %H:%M:%S"))
|
|
152
|
+
)
|
|
153
|
+
return self
|
|
154
|
+
|
|
155
|
+
def is_equal_to_ignoring_seconds(self, other) -> Self:
|
|
156
|
+
"""Asserts that val is a date and is equal to other date to the minute.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
other: the other date, expected to be equal to the minute
|
|
160
|
+
|
|
161
|
+
Examples:
|
|
162
|
+
Usage::
|
|
163
|
+
|
|
164
|
+
import datetime
|
|
165
|
+
|
|
166
|
+
d1 = datetime.datetime(2020, 1, 2, 3, 4, 5) # 2020-01-02 03:04:05
|
|
167
|
+
d2 = datetime.datetime(2020, 1, 2, 3, 4, 55) # 2020-01-02 03:04:55
|
|
168
|
+
|
|
169
|
+
assert_that(d1).is_equal_to_ignoring_seconds(d2)
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
AssertionError: if val is **not** equal to the given date to the minute
|
|
176
|
+
"""
|
|
177
|
+
if type(self.val) is not datetime.datetime:
|
|
178
|
+
raise TypeError("val must be datetime, but was type <%s>" % type(self.val).__name__)
|
|
179
|
+
if type(other) is not datetime.datetime:
|
|
180
|
+
raise TypeError("given arg must be datetime, but was type <%s>" % type(other).__name__)
|
|
181
|
+
if self.val.date() != other.date() or self.val.hour != other.hour or self.val.minute != other.minute:
|
|
182
|
+
return self.error(
|
|
183
|
+
"Expected <%s> to be equal to <%s>, but was not."
|
|
184
|
+
% (self.val.strftime("%Y-%m-%d %H:%M"), other.strftime("%Y-%m-%d %H:%M"))
|
|
185
|
+
)
|
|
186
|
+
return self
|
|
187
|
+
|
|
188
|
+
def is_equal_to_ignoring_time(self, other) -> Self:
|
|
189
|
+
"""Asserts that val is a date and is equal to other date ignoring time.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
other: the other date, expected to be equal ignoring time
|
|
193
|
+
|
|
194
|
+
Examples:
|
|
195
|
+
Usage::
|
|
196
|
+
|
|
197
|
+
import datetime
|
|
198
|
+
|
|
199
|
+
d1 = datetime.datetime(2020, 1, 2, 3, 4, 5) # 2020-01-02 03:04:05
|
|
200
|
+
d2 = datetime.datetime(2020, 1, 2, 13, 44, 55) # 2020-01-02 13:44:55
|
|
201
|
+
|
|
202
|
+
assert_that(d1).is_equal_to_ignoring_time(d2)
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
AssertionBuilder: returns this instance to chain to the next assertion
|
|
206
|
+
|
|
207
|
+
Raises:
|
|
208
|
+
AssertionError: if val is **not** equal to the given date ignoring time
|
|
209
|
+
"""
|
|
210
|
+
if type(self.val) is not datetime.datetime:
|
|
211
|
+
raise TypeError("val must be datetime, but was type <%s>" % type(self.val).__name__)
|
|
212
|
+
if type(other) is not datetime.datetime:
|
|
213
|
+
raise TypeError("given arg must be datetime, but was type <%s>" % type(other).__name__)
|
|
214
|
+
if self.val.date() != other.date():
|
|
215
|
+
return self.error(
|
|
216
|
+
"Expected <%s> to be equal to <%s>, but was not."
|
|
217
|
+
% (self.val.strftime("%Y-%m-%d"), other.strftime("%Y-%m-%d"))
|
|
218
|
+
)
|
|
219
|
+
return self
|