crosshair-tool 0.0.99__cp312-cp312-macosx_10_13_x86_64.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.
Files changed (176) hide show
  1. _crosshair_tracers.cpython-312-darwin.so +0 -0
  2. crosshair/__init__.py +42 -0
  3. crosshair/__main__.py +8 -0
  4. crosshair/_mark_stacks.h +790 -0
  5. crosshair/_preliminaries_test.py +18 -0
  6. crosshair/_tracers.h +94 -0
  7. crosshair/_tracers_pycompat.h +522 -0
  8. crosshair/_tracers_test.py +138 -0
  9. crosshair/abcstring.py +245 -0
  10. crosshair/auditwall.py +190 -0
  11. crosshair/auditwall_test.py +77 -0
  12. crosshair/codeconfig.py +113 -0
  13. crosshair/codeconfig_test.py +117 -0
  14. crosshair/condition_parser.py +1237 -0
  15. crosshair/condition_parser_test.py +497 -0
  16. crosshair/conftest.py +30 -0
  17. crosshair/copyext.py +155 -0
  18. crosshair/copyext_test.py +84 -0
  19. crosshair/core.py +1763 -0
  20. crosshair/core_and_libs.py +149 -0
  21. crosshair/core_regestered_types_test.py +82 -0
  22. crosshair/core_test.py +1316 -0
  23. crosshair/diff_behavior.py +314 -0
  24. crosshair/diff_behavior_test.py +261 -0
  25. crosshair/dynamic_typing.py +346 -0
  26. crosshair/dynamic_typing_test.py +210 -0
  27. crosshair/enforce.py +282 -0
  28. crosshair/enforce_test.py +182 -0
  29. crosshair/examples/PEP316/__init__.py +1 -0
  30. crosshair/examples/PEP316/bugs_detected/__init__.py +0 -0
  31. crosshair/examples/PEP316/bugs_detected/getattr_magic.py +16 -0
  32. crosshair/examples/PEP316/bugs_detected/hash_consistent_with_equals.py +31 -0
  33. crosshair/examples/PEP316/bugs_detected/shopping_cart.py +24 -0
  34. crosshair/examples/PEP316/bugs_detected/showcase.py +39 -0
  35. crosshair/examples/PEP316/correct_code/__init__.py +0 -0
  36. crosshair/examples/PEP316/correct_code/arith.py +60 -0
  37. crosshair/examples/PEP316/correct_code/chess.py +77 -0
  38. crosshair/examples/PEP316/correct_code/nesting_inference.py +17 -0
  39. crosshair/examples/PEP316/correct_code/numpy_examples.py +132 -0
  40. crosshair/examples/PEP316/correct_code/rolling_average.py +35 -0
  41. crosshair/examples/PEP316/correct_code/showcase.py +104 -0
  42. crosshair/examples/__init__.py +0 -0
  43. crosshair/examples/check_examples_test.py +146 -0
  44. crosshair/examples/deal/__init__.py +1 -0
  45. crosshair/examples/icontract/__init__.py +1 -0
  46. crosshair/examples/icontract/bugs_detected/__init__.py +0 -0
  47. crosshair/examples/icontract/bugs_detected/showcase.py +41 -0
  48. crosshair/examples/icontract/bugs_detected/wrong_sign.py +8 -0
  49. crosshair/examples/icontract/correct_code/__init__.py +0 -0
  50. crosshair/examples/icontract/correct_code/arith.py +51 -0
  51. crosshair/examples/icontract/correct_code/showcase.py +94 -0
  52. crosshair/fnutil.py +391 -0
  53. crosshair/fnutil_test.py +75 -0
  54. crosshair/fuzz_core_test.py +516 -0
  55. crosshair/libimpl/__init__.py +0 -0
  56. crosshair/libimpl/arraylib.py +161 -0
  57. crosshair/libimpl/binascii_ch_test.py +30 -0
  58. crosshair/libimpl/binascii_test.py +67 -0
  59. crosshair/libimpl/binasciilib.py +150 -0
  60. crosshair/libimpl/bisectlib_test.py +23 -0
  61. crosshair/libimpl/builtinslib.py +5228 -0
  62. crosshair/libimpl/builtinslib_ch_test.py +1191 -0
  63. crosshair/libimpl/builtinslib_test.py +3735 -0
  64. crosshair/libimpl/codecslib.py +86 -0
  65. crosshair/libimpl/codecslib_test.py +86 -0
  66. crosshair/libimpl/collectionslib.py +264 -0
  67. crosshair/libimpl/collectionslib_ch_test.py +252 -0
  68. crosshair/libimpl/collectionslib_test.py +332 -0
  69. crosshair/libimpl/copylib.py +23 -0
  70. crosshair/libimpl/copylib_test.py +18 -0
  71. crosshair/libimpl/datetimelib.py +2559 -0
  72. crosshair/libimpl/datetimelib_ch_test.py +354 -0
  73. crosshair/libimpl/datetimelib_test.py +112 -0
  74. crosshair/libimpl/decimallib.py +5257 -0
  75. crosshair/libimpl/decimallib_ch_test.py +78 -0
  76. crosshair/libimpl/decimallib_test.py +76 -0
  77. crosshair/libimpl/encodings/__init__.py +23 -0
  78. crosshair/libimpl/encodings/_encutil.py +187 -0
  79. crosshair/libimpl/encodings/ascii.py +44 -0
  80. crosshair/libimpl/encodings/latin_1.py +40 -0
  81. crosshair/libimpl/encodings/utf_8.py +93 -0
  82. crosshair/libimpl/encodings_ch_test.py +83 -0
  83. crosshair/libimpl/fractionlib.py +16 -0
  84. crosshair/libimpl/fractionlib_test.py +80 -0
  85. crosshair/libimpl/functoolslib.py +34 -0
  86. crosshair/libimpl/functoolslib_test.py +56 -0
  87. crosshair/libimpl/hashliblib.py +30 -0
  88. crosshair/libimpl/hashliblib_test.py +18 -0
  89. crosshair/libimpl/heapqlib.py +47 -0
  90. crosshair/libimpl/heapqlib_test.py +21 -0
  91. crosshair/libimpl/importliblib.py +18 -0
  92. crosshair/libimpl/importliblib_test.py +38 -0
  93. crosshair/libimpl/iolib.py +216 -0
  94. crosshair/libimpl/iolib_ch_test.py +128 -0
  95. crosshair/libimpl/iolib_test.py +19 -0
  96. crosshair/libimpl/ipaddresslib.py +8 -0
  97. crosshair/libimpl/itertoolslib.py +44 -0
  98. crosshair/libimpl/itertoolslib_test.py +44 -0
  99. crosshair/libimpl/jsonlib.py +984 -0
  100. crosshair/libimpl/jsonlib_ch_test.py +42 -0
  101. crosshair/libimpl/jsonlib_test.py +51 -0
  102. crosshair/libimpl/mathlib.py +179 -0
  103. crosshair/libimpl/mathlib_ch_test.py +44 -0
  104. crosshair/libimpl/mathlib_test.py +67 -0
  105. crosshair/libimpl/oslib.py +7 -0
  106. crosshair/libimpl/pathliblib_test.py +10 -0
  107. crosshair/libimpl/randomlib.py +178 -0
  108. crosshair/libimpl/randomlib_test.py +120 -0
  109. crosshair/libimpl/relib.py +846 -0
  110. crosshair/libimpl/relib_ch_test.py +169 -0
  111. crosshair/libimpl/relib_test.py +493 -0
  112. crosshair/libimpl/timelib.py +72 -0
  113. crosshair/libimpl/timelib_test.py +82 -0
  114. crosshair/libimpl/typeslib.py +15 -0
  115. crosshair/libimpl/typeslib_test.py +36 -0
  116. crosshair/libimpl/unicodedatalib.py +75 -0
  117. crosshair/libimpl/unicodedatalib_test.py +42 -0
  118. crosshair/libimpl/urlliblib.py +23 -0
  119. crosshair/libimpl/urlliblib_test.py +19 -0
  120. crosshair/libimpl/weakreflib.py +13 -0
  121. crosshair/libimpl/weakreflib_test.py +69 -0
  122. crosshair/libimpl/zliblib.py +15 -0
  123. crosshair/libimpl/zliblib_test.py +13 -0
  124. crosshair/lsp_server.py +261 -0
  125. crosshair/lsp_server_test.py +30 -0
  126. crosshair/main.py +973 -0
  127. crosshair/main_test.py +543 -0
  128. crosshair/objectproxy.py +376 -0
  129. crosshair/objectproxy_test.py +41 -0
  130. crosshair/opcode_intercept.py +601 -0
  131. crosshair/opcode_intercept_test.py +304 -0
  132. crosshair/options.py +218 -0
  133. crosshair/options_test.py +10 -0
  134. crosshair/patch_equivalence_test.py +75 -0
  135. crosshair/path_cover.py +209 -0
  136. crosshair/path_cover_test.py +138 -0
  137. crosshair/path_search.py +161 -0
  138. crosshair/path_search_test.py +52 -0
  139. crosshair/pathing_oracle.py +271 -0
  140. crosshair/pathing_oracle_test.py +21 -0
  141. crosshair/pure_importer.py +27 -0
  142. crosshair/pure_importer_test.py +16 -0
  143. crosshair/py.typed +0 -0
  144. crosshair/register_contract.py +273 -0
  145. crosshair/register_contract_test.py +190 -0
  146. crosshair/simplestructs.py +1165 -0
  147. crosshair/simplestructs_test.py +283 -0
  148. crosshair/smtlib.py +24 -0
  149. crosshair/smtlib_test.py +14 -0
  150. crosshair/statespace.py +1199 -0
  151. crosshair/statespace_test.py +108 -0
  152. crosshair/stubs_parser.py +352 -0
  153. crosshair/stubs_parser_test.py +43 -0
  154. crosshair/test_util.py +329 -0
  155. crosshair/test_util_test.py +26 -0
  156. crosshair/tools/__init__.py +0 -0
  157. crosshair/tools/check_help_in_doc.py +264 -0
  158. crosshair/tools/check_init_and_setup_coincide.py +119 -0
  159. crosshair/tools/generate_demo_table.py +127 -0
  160. crosshair/tracers.py +544 -0
  161. crosshair/tracers_test.py +154 -0
  162. crosshair/type_repo.py +151 -0
  163. crosshair/unicode_categories.py +589 -0
  164. crosshair/unicode_categories_test.py +27 -0
  165. crosshair/util.py +741 -0
  166. crosshair/util_test.py +173 -0
  167. crosshair/watcher.py +307 -0
  168. crosshair/watcher_test.py +107 -0
  169. crosshair/z3util.py +76 -0
  170. crosshair/z3util_test.py +11 -0
  171. crosshair_tool-0.0.99.dist-info/METADATA +144 -0
  172. crosshair_tool-0.0.99.dist-info/RECORD +176 -0
  173. crosshair_tool-0.0.99.dist-info/WHEEL +6 -0
  174. crosshair_tool-0.0.99.dist-info/entry_points.txt +3 -0
  175. crosshair_tool-0.0.99.dist-info/licenses/LICENSE +93 -0
  176. crosshair_tool-0.0.99.dist-info/top_level.txt +2 -0
@@ -0,0 +1,3735 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import collections.abc
4
+ import copy
5
+ import dataclasses
6
+ import enum
7
+ import math
8
+ import operator
9
+ import re
10
+ import sys
11
+ from abc import ABC, abstractmethod
12
+ from array import array
13
+ from numbers import Integral
14
+ from typing import (
15
+ Callable,
16
+ Dict,
17
+ FrozenSet,
18
+ Hashable,
19
+ Iterable,
20
+ List,
21
+ Mapping,
22
+ MutableMapping,
23
+ Optional,
24
+ Sequence,
25
+ Set,
26
+ SupportsAbs,
27
+ SupportsBytes,
28
+ SupportsFloat,
29
+ SupportsInt,
30
+ SupportsRound,
31
+ Tuple,
32
+ Type,
33
+ TypeVar,
34
+ Union,
35
+ get_type_hints,
36
+ )
37
+ from unittest.mock import patch
38
+
39
+ import pytest
40
+ import z3 # type: ignore
41
+
42
+ from crosshair import type_repo
43
+ from crosshair.core import (
44
+ analyze_function,
45
+ deep_realize,
46
+ proxy_for_type,
47
+ realize,
48
+ standalone_statespace,
49
+ )
50
+ from crosshair.core_and_libs import run_checkables
51
+ from crosshair.dynamic_typing import origin_of
52
+ from crosshair.libimpl.builtinslib import (
53
+ LazyIntSymbolicStr,
54
+ ModelingDirector,
55
+ PreciseIeeeSymbolicFloat,
56
+ RealBasedSymbolicFloat,
57
+ SymbolicArrayBasedUniformTuple,
58
+ SymbolicBool,
59
+ SymbolicByteArray,
60
+ SymbolicBytes,
61
+ SymbolicInt,
62
+ SymbolicList,
63
+ SymbolicObject,
64
+ SymbolicRange,
65
+ SymbolicType,
66
+ crosshair_types_for_python_type,
67
+ )
68
+ from crosshair.options import AnalysisOptionSet
69
+ from crosshair.statespace import (
70
+ CANNOT_CONFIRM,
71
+ CONFIRMED,
72
+ EXEC_ERR,
73
+ POST_FAIL,
74
+ MessageType,
75
+ StateSpace,
76
+ )
77
+ from crosshair.test_util import (
78
+ check_exec_err,
79
+ check_messages,
80
+ check_states,
81
+ summarize_execution,
82
+ )
83
+ from crosshair.tracers import NoTracing, ResumedTracing
84
+ from crosshair.util import (
85
+ CrossHairInternal,
86
+ CrossHairValue,
87
+ IgnoreAttempt,
88
+ UnknownSatisfiability,
89
+ )
90
+
91
+
92
+ class Cat:
93
+ def size(self) -> int:
94
+ return 1
95
+
96
+
97
+ class BiggerCat(Cat):
98
+ def size(self) -> int:
99
+ return 2
100
+
101
+
102
+ class AbstractBase(ABC):
103
+ @abstractmethod
104
+ def do(self):
105
+ pass
106
+
107
+
108
+ class ConcreteSubclass(AbstractBase):
109
+ def do(self):
110
+ pass
111
+
112
+
113
+ # Type repo doesn't load crosshair classes by default; load manually:
114
+ type_repo._add_class(AbstractBase)
115
+ type_repo._add_class(ConcreteSubclass)
116
+
117
+
118
+ class Color(enum.Enum):
119
+ RED = 0
120
+ BLUE = 1
121
+ GREEN = 2
122
+
123
+
124
+ @dataclasses.dataclass
125
+ class SmokeDetector:
126
+ """inv: not (self._is_plugged_in and self._in_original_packaging)"""
127
+
128
+ _in_original_packaging: bool
129
+ _is_plugged_in: bool
130
+
131
+ def signaling_alarm(self, air_samples: List[str]) -> bool:
132
+ """
133
+ pre: self._is_plugged_in
134
+ post: implies('smoke' in air_samples, _ == True)
135
+ """
136
+ return "smoke" in air_samples
137
+
138
+
139
+ if sys.version_info >= (3, 9):
140
+ from typing import TypedDict
141
+
142
+ class Movie(TypedDict):
143
+ name: str
144
+ year: int
145
+
146
+
147
+ INF = float("inf")
148
+ NAN = float("nan")
149
+
150
+
151
+ def test_crosshair_types_for_python_type() -> None:
152
+ assert crosshair_types_for_python_type(int) == ((SymbolicInt, 1.0),)
153
+ assert crosshair_types_for_python_type(SmokeDetector) == ()
154
+
155
+
156
+ def test_isinstance():
157
+ with standalone_statespace:
158
+ with NoTracing():
159
+ f = RealBasedSymbolicFloat("f")
160
+ assert isinstance(f, float)
161
+ assert not isinstance(f, int)
162
+
163
+
164
+ def test_smtfloat_like_a_float():
165
+ with standalone_statespace:
166
+ with NoTracing():
167
+ f1 = RealBasedSymbolicFloat("f")
168
+ f2 = type(f1)(12)
169
+ with NoTracing():
170
+ assert isinstance(f2, float)
171
+ assert f2 == 12.0
172
+
173
+
174
+ def test_bool_simple_conditional_fail() -> None:
175
+ def f(a: bool, b: bool) -> bool:
176
+ """post: _ == a"""
177
+ return True if a else b
178
+
179
+ check_states(f, POST_FAIL)
180
+
181
+
182
+ def test_bool_simple_conditional_ok() -> None:
183
+ def f(a: bool, b: bool) -> bool:
184
+ """post: _ == a or b"""
185
+ return True if a else b
186
+
187
+ check_states(f, CONFIRMED)
188
+
189
+
190
+ def test_bool_ors_fail() -> None:
191
+ def f(a: bool, b: bool, c: bool, d: bool) -> bool:
192
+ """post: _ == (a ^ b) or (c ^ d)"""
193
+ return a or b or c or d
194
+
195
+ check_states(f, POST_FAIL)
196
+
197
+
198
+ def test_bool_ors() -> None:
199
+ def f(a: bool, b: bool, c: bool, d: bool) -> bool:
200
+ """
201
+ pre: (not a) and (not d)
202
+ post: _ == (a ^ b) or (c ^ d)
203
+ """
204
+ return a or b or c or d
205
+
206
+ check_states(f, CONFIRMED)
207
+
208
+
209
+ def test_bool_as_numbers() -> None:
210
+ def f(a: bool, b: bool) -> int:
211
+ """post: _ in (1, 2)"""
212
+ return (a * b) + True
213
+
214
+ check_states(f, CONFIRMED)
215
+
216
+
217
+ def test_int___floordiv___ok() -> None:
218
+ def f(n: int, d: int) -> Tuple[int, int]:
219
+ """
220
+ pre: n in (5, -5)
221
+ pre: d in (5, 3, -3, -5)
222
+ post: _[0] == _[1]
223
+ """
224
+ return ((n // d), (int(n) // int(d)))
225
+
226
+ check_states(f, CONFIRMED)
227
+
228
+
229
+ def test_number_simple_compare_ok() -> None:
230
+ def f(i: List[int]) -> bool:
231
+ """
232
+ pre: 10 < len(i)
233
+ post: _
234
+ """
235
+ return 9 < len(i[1:])
236
+
237
+ check_states(f, CONFIRMED)
238
+
239
+
240
+ def test_number_promotion_compare_ok() -> None:
241
+ def f(i: int, f: float) -> bool:
242
+ """
243
+ pre: i == 7
244
+ pre: f == 7.0
245
+ post: _
246
+ """
247
+ return i == f and f >= i and i >= f
248
+
249
+ check_states(f, CONFIRMED)
250
+
251
+
252
+ def test_numeric_promotions() -> None:
253
+ def f(b: bool, i: int) -> Tuple[int, float, float]:
254
+ """
255
+ post: _ != (101, 4.14, 13.14)
256
+ """
257
+ return ((b + 100), (b + 3.14), (i + 3.14))
258
+
259
+ check_states(f, POST_FAIL)
260
+
261
+
262
+ def test_float_as_bool() -> None:
263
+ def f(x: float, y: float):
264
+ """
265
+ pre: math.isfinite(x) and math.isfinite(y)
266
+ post: _ == x or _ == y
267
+ """
268
+ return x or y
269
+
270
+ check_states(f, CONFIRMED)
271
+
272
+
273
+ def test_int_reverse_operators() -> None:
274
+ def f(i: int) -> float:
275
+ """
276
+ pre: i != 0
277
+ post: _ != 1
278
+ """
279
+ return (1 + i) + (1 - i) + (1 / i)
280
+
281
+ check_states(f, POST_FAIL)
282
+
283
+
284
+ def test_int_constant_bounds_tracking(space: StateSpace) -> None:
285
+ x = proxy_for_type(int, "x")
286
+ with patch(
287
+ "crosshair.statespace.StateSpace.choose_possible", return_value=False
288
+ ) as mock_solver:
289
+ with ResumedTracing():
290
+ assert mock_solver.call_count == 0
291
+ if x < 5:
292
+ assert False, "Should be unreachable"
293
+ assert mock_solver.call_count == 1
294
+ if x < 4:
295
+ assert False, "Should be unreachable"
296
+ assert mock_solver.call_count == 1
297
+ if x > 20:
298
+ assert False, "Should be unreachable"
299
+ assert mock_solver.call_count == 2
300
+ neg_x = -x
301
+ if neg_x < -30:
302
+ assert False, "Should be unreachable"
303
+ assert mock_solver.call_count == 2
304
+ abs_neg_x = abs(neg_x)
305
+ with ResumedTracing():
306
+ assert space.is_possible(neg_x == -5)
307
+ assert not space.is_possible(neg_x == -4)
308
+ assert space.is_possible(abs_neg_x == 5)
309
+ assert not space.is_possible(abs_neg_x == 21)
310
+ assert not space.is_possible(abs_neg_x != x)
311
+
312
+
313
+ @pytest.mark.demo
314
+ def test_int___add___method():
315
+ def f(a: int, b: int) -> int:
316
+ """
317
+ Can the sum of two consecutive integers be 37?
318
+
319
+ pre: a + 1 == b
320
+ post: _ != 37
321
+ """
322
+ return a + b
323
+
324
+ check_states(f, POST_FAIL)
325
+
326
+
327
+ @pytest.mark.demo
328
+ def test_int___mod___method():
329
+ def f(a: int) -> int:
330
+ """
331
+ Can the last digit of a given large number be 3?
332
+
333
+ pre: a > 1234
334
+ post: _ != 3
335
+ """
336
+ return a % 10
337
+
338
+ check_states(f, POST_FAIL)
339
+
340
+
341
+ @pytest.mark.demo
342
+ def test_int___mul___method():
343
+ def f(a: int, b: int) -> int:
344
+ """
345
+ Can we multiply two integers and return 42?
346
+
347
+ NOTE: Although this example works, nonlinear integer arithmetic can not
348
+ always be effectively reasoned about.
349
+
350
+ pre: a > b > 1
351
+ post: _ != 42
352
+ """
353
+ return a * b
354
+
355
+ check_states(f, POST_FAIL)
356
+
357
+
358
+ @pytest.mark.demo("yellow")
359
+ def test_int___pow___method():
360
+ def f(a: int) -> int:
361
+ """
362
+ Can the given integer, cubed, equal 343?
363
+
364
+ NOTE: Although this example works, nonlinear integer arithmetic can not
365
+ always be effectively reasoned about. This is particularly true when
366
+ the exponent is symbolic.
367
+
368
+
369
+ post: _ != 343
370
+ """
371
+ return a**3
372
+
373
+ check_states(f, POST_FAIL)
374
+
375
+
376
+ def test_int___pow___to_ieee_float():
377
+ with standalone_statespace as space:
378
+ with NoTracing():
379
+ space.extra(ModelingDirector).global_representations[
380
+ float
381
+ ] = PreciseIeeeSymbolicFloat
382
+ a = SymbolicInt("a")
383
+ with pytest.raises(UnknownSatisfiability):
384
+ sqrt_a = a**0.5
385
+
386
+
387
+ def test_int___pow___to_real_based_float():
388
+ with standalone_statespace as space:
389
+ with NoTracing():
390
+ space.extra(ModelingDirector).global_representations[
391
+ float
392
+ ] = RealBasedSymbolicFloat
393
+ a = SymbolicInt("a")
394
+ sqrt_a = a**0.5
395
+ with pytest.raises(UnknownSatisfiability):
396
+ realize(sqrt_a == 3)
397
+
398
+
399
+ @pytest.mark.demo
400
+ def test_int___sub___method():
401
+ def f(a: int) -> int:
402
+ """
403
+ Can we subtract from 42 and get something larger?
404
+
405
+ post: _ <= 42
406
+ """
407
+ return 42 - a
408
+
409
+ check_states(f, POST_FAIL)
410
+
411
+
412
+ def test_int___rsub__() -> None:
413
+ def f(i: int) -> float:
414
+ """
415
+ post: _ != 42
416
+ """
417
+ return 1 - i
418
+
419
+ check_states(f, POST_FAIL)
420
+
421
+
422
+ @pytest.mark.demo
423
+ def test_int___floordiv___method() -> None:
424
+ def f(a: int, b: int) -> int:
425
+ """
426
+ Can the average of two integers equal either one?
427
+
428
+ pre: a < b
429
+ post: a < _ < b
430
+ """
431
+ return (a + b) // 2
432
+
433
+ check_states(f, POST_FAIL)
434
+
435
+
436
+ def test_int___floordiv___bounds() -> None:
437
+ def f(a: int, b: int) -> int:
438
+ """
439
+ pre: a < b
440
+ post: a <= _ < b
441
+ """
442
+ return (a + b) // 2
443
+
444
+ check_states(f, CONFIRMED)
445
+
446
+
447
+ def test_int_bitwise_fail() -> None:
448
+ def f(a: int, b: int) -> int:
449
+ """
450
+ pre: 0 <= a <= 3
451
+ pre: 0 <= b <= 3
452
+ post: _ < 7
453
+ """
454
+ return (a << 1) ^ b
455
+
456
+ check_states(f, POST_FAIL)
457
+
458
+
459
+ def test_int_bitwise_ok() -> None:
460
+ def f(a: int, b: int) -> int:
461
+ """
462
+ pre: 0 <= a <= 3
463
+ pre: 0 <= b <= 3
464
+ post: _ <= 7
465
+ """
466
+ return (a << 1) ^ b
467
+
468
+ check_states(f, CONFIRMED)
469
+
470
+
471
+ @pytest.mark.demo
472
+ def test_int___truediv___method() -> None:
473
+ def f(a: int, b: int) -> float:
474
+ """
475
+ Can we find an integer that is half as large as another?
476
+
477
+ pre: b != 0
478
+ post: _ != 0.5
479
+ """
480
+ return a / b
481
+
482
+ check_states(f, POST_FAIL)
483
+
484
+
485
+ def test_trunc_fail() -> None:
486
+ def f(n: float) -> int:
487
+ """
488
+ pre: math.isfinite(n)
489
+ pre: n > 100
490
+ post: _ < n
491
+ """
492
+ return math.trunc(n)
493
+
494
+ check_states(f, POST_FAIL)
495
+
496
+
497
+ @pytest.mark.demo
498
+ def test_range() -> None:
499
+ def f(n: int) -> Sequence[int]:
500
+ """
501
+ Can the length of range() be different than the integer we pass to it?
502
+
503
+ post: len(_) == n
504
+ """
505
+ return range(n)
506
+
507
+ check_states(f, POST_FAIL)
508
+
509
+
510
+ def test_round_fail() -> None:
511
+ def f(n1: int, n2: int) -> Tuple[int, int]:
512
+ """
513
+ pre: n1 < n2
514
+ post: _[0] < _[1] # because we round towards even
515
+ """
516
+ return (round(n1 + 0.5), round(n2 + 0.5))
517
+
518
+ check_states(f, POST_FAIL)
519
+
520
+
521
+ def test_round_unknown() -> None:
522
+ def f(num: float, ndigits: Optional[int]) -> float:
523
+ """
524
+ pre: math.isfinite(num)
525
+ post: isinstance(_, int) == (ndigits is None)
526
+ """
527
+ return round(num, ndigits)
528
+
529
+ check_states(f, CANNOT_CONFIRM)
530
+
531
+
532
+ def test_float_isinstance() -> None:
533
+ def f(x: float) -> float:
534
+ """post: isinstance(_, float)"""
535
+ return x
536
+
537
+ check_states(f, CONFIRMED)
538
+
539
+
540
+ def test_mismatched_types() -> None:
541
+ def f(x: float, y: list) -> float:
542
+ """
543
+ pre: x == 1.0 and y == []
544
+ post: _ == 1
545
+ """
546
+ return x + y # type: ignore
547
+
548
+ (actual, expected) = check_exec_err(f, "TypeError: unsupported operand type")
549
+ assert actual == expected
550
+
551
+
552
+ def test_bool_bitwise_negation() -> None:
553
+ def f(x: bool) -> float:
554
+ """
555
+ pre: x == True
556
+ post: _ == -2
557
+ """
558
+ return ~x
559
+
560
+ check_states(f, CONFIRMED)
561
+
562
+
563
+ def test_float_from_hex() -> None:
564
+ def f(s: str) -> float:
565
+ """
566
+ pre: s == '0x3.a7p10'
567
+ post: _ == 3740.0
568
+ """
569
+ return float.fromhex(s)
570
+
571
+ check_states(f, CONFIRMED)
572
+
573
+
574
+ def test_int_from_byte_iterator(space) -> None:
575
+ byts = proxy_for_type(bytes, "byts")
576
+ with ResumedTracing():
577
+ space.add(len(byts) == 2)
578
+ number = int.from_bytes(iter(byts), byteorder="little")
579
+ assert space.is_possible(number == 5)
580
+
581
+
582
+ def test_int_from_bytes(space) -> None:
583
+ byts = proxy_for_type(bytes, "byts")
584
+ with ResumedTracing():
585
+ space.add(len(byts) == 2)
586
+ number = int.from_bytes(byts, byteorder="little")
587
+ assert space.is_possible(number == 5)
588
+
589
+
590
+ def test_int_nonlinear() -> None:
591
+ def make_bigger(x: int, e: int) -> float:
592
+ """
593
+ pre: e > 1
594
+ post: __return__ != 592704
595
+ """
596
+ # Expenentation is not SMT-solvable. (z3 gives unsat for this)
597
+ # But CrossHair gracefully falls back to realized values, yielding
598
+ # the counterexample of: 84 ** 3
599
+ return x**e
600
+
601
+ check_states(make_bigger, POST_FAIL)
602
+
603
+
604
+ @pytest.mark.demo
605
+ def test_int___str___method() -> None:
606
+ def f(x: int) -> str:
607
+ """
608
+ Can any input make this function return the string "321"?
609
+
610
+ post: _ != "321"
611
+ """
612
+ return str(x)
613
+
614
+ check_states(f, POST_FAIL)
615
+
616
+
617
+ @pytest.mark.demo
618
+ def test_int___repr___method() -> None:
619
+ def f(x: int) -> str:
620
+ """
621
+ Can any input make this function return the string "321"?
622
+
623
+ post: _ != '321'
624
+ """
625
+ return repr(x)
626
+
627
+ check_states(f, POST_FAIL)
628
+
629
+
630
+ @pytest.mark.parametrize("b", (False, 1, -2.0, NAN, INF, -INF))
631
+ @pytest.mark.parametrize("op", (operator.lt, operator.eq, operator.add, operator.mul))
632
+ def test_bool_ops(b, op):
633
+ with standalone_statespace as space:
634
+ with NoTracing():
635
+ a = SymbolicBool("a")
636
+ space.add(a)
637
+ symbolic_ret = summarize_execution(lambda: op(a, b))
638
+ concrete_ret = summarize_execution(lambda: op(realize(a), b), detach_path=False)
639
+ assert symbolic_ret == concrete_ret
640
+
641
+
642
+ @pytest.mark.parametrize("b", (False, 1, -2.0, NAN, INF, -INF))
643
+ @pytest.mark.parametrize(
644
+ "op",
645
+ (operator.lt, operator.eq, operator.add, operator.mul, operator.eq, operator.ne),
646
+ )
647
+ def test_float_ops(b, op):
648
+ with standalone_statespace as space:
649
+ with NoTracing():
650
+ a = space.extra(ModelingDirector).choose(float)("a")
651
+ space.add(a < 0)
652
+ symbolic_ret = summarize_execution(lambda: op(a, b))
653
+ concrete_ret = summarize_execution(lambda: op(realize(a), b), detach_path=False)
654
+ assert symbolic_ret == concrete_ret
655
+
656
+
657
+ def test_int_from_str():
658
+ def f(a: str) -> int:
659
+ """
660
+ post: _ != 7
661
+ raises: ValueError
662
+ """
663
+ return int(a)
664
+
665
+ check_states(f, POST_FAIL)
666
+
667
+
668
+ def test_int_from_str_with_bases(space):
669
+ i = proxy_for_type(int, "i")
670
+ s_11 = proxy_for_type(str, "s_11")
671
+ s_a = proxy_for_type(str, "s_a")
672
+ with ResumedTracing():
673
+ space.add(len(s_11) == 2)
674
+ space.add(s_11 == "11")
675
+ space.add(len(s_a) == 1)
676
+ space.add(s_a == "a")
677
+ assert int(s_11, 4) == 5
678
+ assert int(s_11, 16) == 17
679
+ assert int(s_11, 36) == 37
680
+ assert int(s_11, 0) == 11
681
+ assert int(s_a, 16) == 10
682
+ with pytest.raises(TypeError):
683
+ assert int(s_a, base=2.5)
684
+ with pytest.raises(TypeError):
685
+ int(i, base="foo")
686
+
687
+
688
+ def test_easy_float_from_str():
689
+ def f(a: str) -> float:
690
+ """
691
+ post: _ != 0.0
692
+ raises: ValueError
693
+ """
694
+ return float(a)
695
+
696
+ check_states(
697
+ f,
698
+ MessageType.POST_FAIL,
699
+ AnalysisOptionSet(max_iterations=100),
700
+ )
701
+
702
+
703
+ def test_float_from_three_digit_str():
704
+ with standalone_statespace as space:
705
+ with NoTracing():
706
+ codepoints = [
707
+ proxy_for_type(int, "xat0"),
708
+ proxy_for_type(int, "xat1"),
709
+ proxy_for_type(int, "xat2"),
710
+ ]
711
+ x = LazyIntSymbolicStr(codepoints)
712
+ for point in codepoints:
713
+ space.add(point >= ord("0"))
714
+ space.add(point <= ord("9"))
715
+ asfloat = float(x)
716
+ assert space.is_possible(asfloat <= 999)
717
+ assert not space.is_possible(asfloat > 999)
718
+ assert space.is_possible(asfloat == 0) # (because "000" is a valid float)
719
+ assert not space.is_possible(asfloat == 500.5)
720
+
721
+
722
+ @pytest.mark.demo("yellow")
723
+ def test_float___pow___operator():
724
+ def f(a: float) -> float:
725
+ """
726
+ Can the given float, cubed, equal 0.125?
727
+
728
+ NOTE: Although this example works, nonlinear arithmetic is quite difficult
729
+ to reason about in most cases.
730
+
731
+ post: _ != 0.125
732
+ """
733
+ return a**3
734
+
735
+ check_states(f, POST_FAIL)
736
+
737
+
738
+ def test_int_bitwise_find_negative_input():
739
+ def f(x: int) -> int:
740
+ """
741
+ pre: x < 0
742
+ post: _ != 7
743
+ """
744
+ return x & 255
745
+
746
+ check_states(f, POST_FAIL)
747
+
748
+
749
+ @pytest.mark.parametrize("val", [-256, 2**16] + list(range(-4, 9, 2)))
750
+ def test_int_bit_length(val):
751
+ with standalone_statespace as space:
752
+ x = proxy_for_type(int, "x")
753
+ space.add(x == val)
754
+ assert realize(x.bit_length()) == val.bit_length()
755
+
756
+
757
+ @pytest.mark.parametrize(
758
+ "val", [-256, -(2**15), 2**9, 2**15 - 1] + list(range(-4, 9, 3))
759
+ )
760
+ def test_int_to_bytes(val):
761
+ with standalone_statespace as space:
762
+ x = proxy_for_type(int, "x")
763
+ space.add(x == val)
764
+ assert realize(x.to_bytes(2, "big", signed=True)) == val.to_bytes(
765
+ 2, "big", signed=True
766
+ )
767
+
768
+
769
+ def test_int_format():
770
+ with standalone_statespace as space:
771
+ with NoTracing():
772
+ x = SymbolicInt("x")
773
+ space.add(x == 42)
774
+ assert x.__format__("") == "42"
775
+ # TODO this fails:
776
+ # assert x.__format__("f") == "42.000000"
777
+
778
+
779
+ def test_class_format():
780
+ with standalone_statespace as space:
781
+ with NoTracing():
782
+ t = SymbolicType("t", Type[int])
783
+ space.add(t.var == SymbolicType._coerce_to_smt_sort(int))
784
+ assert "a{}b".format(t) == "a<class 'int'>b"
785
+
786
+
787
+ @pytest.mark.demo
788
+ def test_sorted() -> None:
789
+ def f(lst: List[int]) -> List[int]:
790
+ """
791
+ Can sorting shift the number 4002 to the front?
792
+
793
+ pre: len(lst) >= 3
794
+ pre: lst[0] > 4002
795
+ post: _[0] != 4002
796
+ """
797
+ return list(sorted(lst))
798
+
799
+ check_states(f, POST_FAIL)
800
+
801
+
802
+ def test_str___bool__() -> None:
803
+ def f(a: str) -> str:
804
+ """post: a"""
805
+ return a
806
+
807
+ check_states(f, POST_FAIL)
808
+
809
+
810
+ @pytest.mark.demo
811
+ def test_str___mul___method() -> None:
812
+ def f(a: str) -> str:
813
+ """
814
+ Can this string-trippling-function produce a 6-character string?
815
+
816
+ post: len(_) != 6
817
+ """
818
+ return 3 * a
819
+
820
+ check_states(f, POST_FAIL)
821
+
822
+
823
+ def test_str___mul___ok() -> None:
824
+ def f(a: str) -> str:
825
+ """
826
+ pre: len(a) == 2
827
+ post: len(_) == 10
828
+ """
829
+ return a * 3 + 2 * a
830
+
831
+ check_states(f, CONFIRMED)
832
+
833
+
834
+ def test_str___mul___by_symbolic_fail() -> None:
835
+ def f(i: int) -> str:
836
+ """post: len(_) != 6"""
837
+ return "a\x00b" * i
838
+
839
+ check_states(f, POST_FAIL)
840
+
841
+
842
+ def test_str___mul___full_symbolic_multiply_unknown() -> None:
843
+ def f(s: str, i: int) -> str:
844
+ """
845
+ pre: s and i > 0
846
+ post: _[0] == s[0]
847
+ """
848
+ return s * i
849
+
850
+ check_states(f, CANNOT_CONFIRM)
851
+
852
+
853
+ def test_str___add___prefixing_fail() -> None:
854
+ def f(a: str, indent: bool) -> str:
855
+ """post: len(_) == len(a) + indent"""
856
+ return (" " if indent else "") + a
857
+
858
+ check_states(f, POST_FAIL)
859
+
860
+
861
+ def test_str___add___prefixing_ok() -> None:
862
+ def f(a: str, indent: bool) -> str:
863
+ """post: len(_) == len(a) + (2 if indent else 0)"""
864
+ return (" " if indent else "") + a
865
+
866
+ check_states(f, CONFIRMED)
867
+
868
+
869
+ @pytest.mark.demo
870
+ def test_str___contains___method() -> None:
871
+ def f(a: str) -> str:
872
+ """
873
+ What 3 characters can we append to "purple" so that the result contains "please"?
874
+
875
+ pre: len(a) == 3
876
+ post: "please" not in _
877
+ """
878
+ return "purple" + a
879
+
880
+ check_states(f, POST_FAIL)
881
+
882
+
883
+ def test_str_find_does_not_realize_string_length() -> None:
884
+ def f(a: str) -> str:
885
+ """post: len(_) != 100"""
886
+ a.find("abc")
887
+ return a
888
+
889
+ check_states(f, POST_FAIL)
890
+
891
+
892
+ def test_str_find_with_limits_ok() -> None:
893
+ def f(a: str) -> int:
894
+ """post: _ == -1"""
895
+ return a.find("abc", 1, 3)
896
+
897
+ check_states(f, CONFIRMED)
898
+
899
+
900
+ def test_str_find_with_negative_limits_fail() -> None:
901
+ def f(a: str) -> int:
902
+ """post: _ == -1"""
903
+ return a.find("ab", -2, 3)
904
+
905
+ check_states(f, POST_FAIL)
906
+
907
+
908
+ def test_str_ljust_fail() -> None:
909
+ def f(s: str) -> str:
910
+ """post: len(_) == len(s)"""
911
+ return s.ljust(3, " ")
912
+
913
+ check_states(f, POST_FAIL)
914
+
915
+
916
+ def test_str_rfind_with_limits_ok() -> None:
917
+ def f(a: str) -> int:
918
+ """post: _ == -1"""
919
+ return a.rfind("abc", 1, 3)
920
+
921
+ check_states(f, CONFIRMED)
922
+
923
+
924
+ def test_str_rfind_with_negative_limits_fail() -> None:
925
+ def f(a: str) -> int:
926
+ """post: _ == -1"""
927
+ return a.rfind("ab", -2, 3)
928
+
929
+ check_states(f, POST_FAIL)
930
+
931
+
932
+ def test_str_rindex_fail() -> None:
933
+ def f(a: str) -> int:
934
+ """post: _ != 2"""
935
+ try:
936
+ return a.rindex("abc")
937
+ except ValueError:
938
+ return 0
939
+
940
+ check_states(f, POST_FAIL)
941
+
942
+
943
+ def test_str_rindex_err() -> None:
944
+ def f(a: str) -> int:
945
+ """post: True"""
946
+ return a.rindex("abc", 1, 3)
947
+
948
+ check_states(f, EXEC_ERR)
949
+
950
+
951
+ def test_str_rjust_fail() -> None:
952
+ def f(s: str) -> str:
953
+ """post: len(_) == len(s)"""
954
+ return s.rjust(3, "x")
955
+
956
+ check_states(f, POST_FAIL)
957
+
958
+
959
+ @pytest.mark.demo
960
+ def test_str_replace_method() -> None:
961
+ def f(a: str) -> str:
962
+ """
963
+ Can this function return a changed string?
964
+
965
+ post: _ == a
966
+ """
967
+ return a.replace("abc", "x", 1)
968
+
969
+ check_states(f, POST_FAIL)
970
+
971
+
972
+ def test_str_startswith(space) -> None:
973
+ symbolic_char = proxy_for_type(str, "x")
974
+ symbolic_empty = proxy_for_type(str, "y")
975
+ with ResumedTracing():
976
+ space.add(len(symbolic_char) == 1)
977
+ space.add(len(symbolic_empty) == 0)
978
+ assert symbolic_char.startswith(symbolic_empty)
979
+ assert symbolic_char.startswith(symbolic_char)
980
+ assert symbolic_char.startswith(("foo", symbolic_empty))
981
+ assert not symbolic_char.startswith(("foo", "bar"))
982
+ assert symbolic_char.startswith(("", "bar"))
983
+ assert symbolic_char.startswith("")
984
+ assert symbolic_char.startswith(symbolic_empty, 1)
985
+ assert symbolic_char.startswith(symbolic_empty, 1, 1)
986
+ assert str.startswith(symbolic_char, symbolic_empty)
987
+ assert "foo".startswith(symbolic_empty)
988
+ assert not "".startswith(symbolic_char)
989
+
990
+ # Yes, the empty string is findable off the left side but not the right
991
+ assert "x".startswith("", -10, -9)
992
+ assert symbolic_char.startswith(symbolic_empty, -10, -9)
993
+ assert not "x".startswith("", 9, 10)
994
+ assert not symbolic_char.startswith(symbolic_empty, 9, 10)
995
+
996
+
997
+ @pytest.mark.demo
998
+ def test_str_index_method() -> None:
999
+ def f(a: str) -> int:
1000
+ """
1001
+ Can we find "abc" at position 2?
1002
+
1003
+ raises: ValueError
1004
+ post: _ != 2
1005
+ """
1006
+ return a.rindex("abc")
1007
+
1008
+ check_states(f, POST_FAIL)
1009
+
1010
+
1011
+ def test_str_index_err() -> None:
1012
+ def f(s1: str, s2: str) -> int:
1013
+ """
1014
+ pre: s1 == 'aba'
1015
+ pre: 'ab' in s2
1016
+ post: True
1017
+ """
1018
+ return s1.index(s2)
1019
+
1020
+ # index() raises ValueError when a match isn't found:
1021
+ (actual, expected) = check_exec_err(f, "ValueError")
1022
+ assert actual == expected
1023
+
1024
+
1025
+ def test_str_negative_index_slicing() -> None:
1026
+ def f(s: str) -> Tuple[str, str]:
1027
+ """post: sum(map(len, _)) == len(s) - 1"""
1028
+ idx = s.find(":")
1029
+ return (s[:idx], s[idx + 1 :])
1030
+
1031
+ check_states(f, POST_FAIL) # (fails when idx == -1)
1032
+
1033
+
1034
+ def test_str_starts_and_ends_ok() -> None:
1035
+ def f(s: str) -> str:
1036
+ """
1037
+ pre: s == 'aba'
1038
+ post: s.startswith('ab')
1039
+ post: s.endswith('ba')
1040
+ """
1041
+ return s
1042
+
1043
+ check_states(f, CONFIRMED)
1044
+
1045
+
1046
+ @pytest.mark.demo
1047
+ def test_str_count_method() -> None:
1048
+ def f(s: str) -> int:
1049
+ """
1050
+ Can this function find two "a" characters?
1051
+
1052
+ post: _ != 2
1053
+ """
1054
+ return s.count("a")
1055
+
1056
+ check_states(f, POST_FAIL)
1057
+
1058
+
1059
+ @pytest.mark.demo
1060
+ def test_str_split_method() -> None:
1061
+ def f(s: str) -> list:
1062
+ """
1063
+ Does any string comma-split into "a" and "b"?
1064
+
1065
+ post: _ != ['a', 'b']
1066
+ """
1067
+ return s.split(",")
1068
+
1069
+ check_states(f, POST_FAIL)
1070
+
1071
+
1072
+ def test_str_rsplit_fail() -> None:
1073
+ def f(s: str) -> list:
1074
+ """post: __return__ != ['a', 'b']"""
1075
+ return s.rsplit(":", 1)
1076
+
1077
+ check_states(f, POST_FAIL)
1078
+
1079
+
1080
+ def test_str_partition_ok() -> None:
1081
+ def f(s: str) -> tuple:
1082
+ """
1083
+ pre: len(s) == 3
1084
+ post: len(_) == 3
1085
+ """
1086
+ return s.partition(":")
1087
+
1088
+ check_states(f, CONFIRMED)
1089
+
1090
+
1091
+ @pytest.mark.smoke
1092
+ @pytest.mark.demo
1093
+ def test_str_partition_method() -> None:
1094
+ def f(s: str) -> tuple:
1095
+ """
1096
+ Does any input to this partitioning yield ("a", "bc", "d")?
1097
+
1098
+ post: _ != ("a", "bc", "d")
1099
+ """
1100
+ return s.partition("bc")
1101
+
1102
+ check_states(f, POST_FAIL)
1103
+
1104
+
1105
+ def test_str_rpartition_ok() -> None:
1106
+ def f(s: str) -> tuple:
1107
+ """
1108
+ pre: len(s) == 2
1109
+ post: len(_) == 3
1110
+ """
1111
+ return s.rpartition(":")
1112
+
1113
+ check_states(f, CONFIRMED)
1114
+
1115
+
1116
+ def test_str_rpartition_fail() -> None:
1117
+ def f(s: str) -> tuple:
1118
+ """
1119
+ pre: len(s) == 4
1120
+ post: _ != ("abb", "b", "")
1121
+ """
1122
+ return s.rpartition("b")
1123
+
1124
+ check_states(f, POST_FAIL)
1125
+
1126
+
1127
+ def test_str___ge___fail() -> None:
1128
+ def f(s1: str, s2: str) -> bool:
1129
+ """post: _"""
1130
+ return s1 >= s2
1131
+
1132
+ check_states(f, POST_FAIL)
1133
+
1134
+
1135
+ @pytest.mark.demo
1136
+ def test_str___le___method() -> None:
1137
+ def f(a: str, b: str) -> bool:
1138
+ """
1139
+ Can a be greater than b, even though its first charater is not?
1140
+
1141
+ pre: a[0] <= b[0]
1142
+ post: _
1143
+ """
1144
+ return a <= b
1145
+
1146
+ check_states(f, POST_FAIL)
1147
+
1148
+
1149
+ def test_str_realized_compare() -> None:
1150
+ def f(a: str, b: str) -> bool:
1151
+ """
1152
+ post: implies(_, a == b)
1153
+ """
1154
+ return realize(a) == b
1155
+
1156
+ check_states(f, CANNOT_CONFIRM)
1157
+
1158
+
1159
+ def test_str_int_comparison_fail() -> None:
1160
+ def f(a: int, b: str) -> Tuple[bool, bool]:
1161
+ """post: (not _[0]) or (not _[1])"""
1162
+ return (a != b, b != a)
1163
+
1164
+ check_states(f, POST_FAIL)
1165
+
1166
+
1167
+ def test_str_int_comparison_ok() -> None:
1168
+ def f(a: int, b: str) -> bool:
1169
+ """post: _ == False"""
1170
+ return a == b or b == a
1171
+
1172
+ check_states(f, CONFIRMED)
1173
+
1174
+
1175
+ def test_str_formatting_wrong_key() -> None:
1176
+ def f(o: object) -> str:
1177
+ """post: True"""
1178
+ return "object of type {typ} with repr {zzzzz}".format( # type: ignore
1179
+ typ=type(o), rep=repr(o)
1180
+ )
1181
+
1182
+ check_states(f, EXEC_ERR)
1183
+
1184
+
1185
+ def test_str_format_symbolic_format() -> None:
1186
+ def f(fmt: str) -> str:
1187
+ """
1188
+ pre: '{}' in fmt
1189
+ post: True
1190
+ """
1191
+ return fmt.format(ver=sys.version, platform=sys.platform)
1192
+
1193
+ check_states(f, EXEC_ERR)
1194
+
1195
+
1196
+ def test_str_format_percent_unknown() -> None:
1197
+ def f(fmt: str) -> str:
1198
+ """
1199
+ pre: '%' not in fmt
1200
+ post: True
1201
+ """
1202
+ return fmt % ()
1203
+
1204
+ check_states(f, CANNOT_CONFIRM)
1205
+
1206
+
1207
+ @pytest.mark.demo
1208
+ def test_str_join_method() -> None:
1209
+ def f(items: List[str]) -> str:
1210
+ """
1211
+ Any inputs that produce a 5-character string?
1212
+
1213
+ pre: len(items) == 2
1214
+ post: len(_) != 5
1215
+ """
1216
+ return "and".join(items)
1217
+
1218
+ check_states(f, POST_FAIL)
1219
+
1220
+
1221
+ def test_str_upper_fail() -> None:
1222
+ def f(s: str) -> str:
1223
+ """
1224
+ Does any character uppercase to "F"?
1225
+
1226
+ pre: len(s) == 1
1227
+ pre: s != "F"
1228
+ post: __return__ != "F"
1229
+ """
1230
+ return s.upper()
1231
+
1232
+ # TODO: make this use case more efficient.
1233
+ options = AnalysisOptionSet(per_path_timeout=20.0)
1234
+ check_states(f, POST_FAIL, options)
1235
+
1236
+
1237
+ def test_csv_example() -> None:
1238
+ def f(lines: List[str]) -> List[str]:
1239
+ """
1240
+ pre: all(',' in line for line in lines)
1241
+ post: __return__ == [line.split(',')[0] for line in lines]
1242
+ """
1243
+ return [line[: line.index(",")] for line in lines]
1244
+
1245
+ check_states(f, CANNOT_CONFIRM)
1246
+
1247
+
1248
+ @pytest.mark.demo
1249
+ def test_str_zfill_method() -> None:
1250
+ def f(s: str) -> str:
1251
+ """
1252
+ Can zero-filling a two-character string produce "0ab"?
1253
+
1254
+ pre: len(s) == 2
1255
+ post: _ != "0ab"
1256
+ """
1257
+ return s.zfill(3)
1258
+
1259
+ check_states(f, POST_FAIL)
1260
+
1261
+
1262
+ @pytest.mark.demo("yellow")
1263
+ def test_str_format_method() -> None:
1264
+ def f(s: str) -> str:
1265
+ """
1266
+ Does any substitution produce the string "abcdef"?
1267
+
1268
+ NOTE: CrossHair will not be effective with a symbolic template string;
1269
+ e.g. trying to solve s.format("cd") is much more difficult.
1270
+
1271
+ post: _ != "abcdef"
1272
+ """
1273
+ return "ab{}ef".format(s)
1274
+
1275
+ check_states(f, POST_FAIL)
1276
+
1277
+
1278
+ def test_str_constructor() -> None:
1279
+ with standalone_statespace as space:
1280
+ with NoTracing():
1281
+ x = LazyIntSymbolicStr("x")
1282
+ assert str(x) is x
1283
+
1284
+
1285
+ def test_str_str() -> None:
1286
+ with standalone_statespace:
1287
+ with NoTracing():
1288
+ x = LazyIntSymbolicStr("x")
1289
+ strx = x.__str__()
1290
+ with NoTracing():
1291
+ assert isinstance(strx, str)
1292
+
1293
+
1294
+ def test_str_center():
1295
+ with standalone_statespace as space:
1296
+ with NoTracing():
1297
+ string = LazyIntSymbolicStr("string")
1298
+ fillch = LazyIntSymbolicStr("fillch")
1299
+ sz = SymbolicInt("sz")
1300
+ sz6 = SymbolicInt("sz6")
1301
+ space.add(string.__len__() == 3)
1302
+ space.add(fillch.__len__() == 1)
1303
+ space.add(sz > 5)
1304
+ space.add(sz6 == 6)
1305
+ assert "boo".center(sz6) == " boo "
1306
+ symbolic_centered = "boo".center(sz, fillch)
1307
+ starts_with_nonfill = ord(symbolic_centered[0]) != ord(fillch)
1308
+ assert not space.is_possible(starts_with_nonfill)
1309
+
1310
+
1311
+ def test_str_map_chars() -> None:
1312
+ with standalone_statespace:
1313
+ with NoTracing():
1314
+ string = LazyIntSymbolicStr(list(map(ord, "ab")))
1315
+ codepoints = list(map(ord, string))
1316
+
1317
+
1318
+ @pytest.mark.demo
1319
+ def test_str___add___method() -> None:
1320
+ def f(s: str) -> str:
1321
+ """
1322
+ Can any input make this function return "Hello World"?
1323
+
1324
+ post: _ != "Hello World"
1325
+ """
1326
+ return s + "World"
1327
+
1328
+ check_states(f, POST_FAIL)
1329
+
1330
+
1331
+ def test_str_bool():
1332
+ with standalone_statespace as space, NoTracing():
1333
+ a = LazyIntSymbolicStr("a")
1334
+ with ResumedTracing():
1335
+ space.add(a.__len__() > 0)
1336
+ assert bool(a)
1337
+ # Can we retain our symbolic state after forcing a positive truthiness?:
1338
+ assert space.is_possible(a == "this")
1339
+ assert space.is_possible(a == "that")
1340
+
1341
+
1342
+ def test_str_eq():
1343
+ with standalone_statespace, NoTracing():
1344
+ assert LazyIntSymbolicStr([]) == ""
1345
+
1346
+
1347
+ def test_str_getitem():
1348
+ with standalone_statespace, NoTracing():
1349
+ assert LazyIntSymbolicStr(list(map(ord, "abc")))[0] == "a"
1350
+ assert LazyIntSymbolicStr(list(map(ord, "abc")))[-1] == "c"
1351
+ assert LazyIntSymbolicStr(list(map(ord, "abc")))[-5:2] == "ab"
1352
+
1353
+
1354
+ def test_str_filter():
1355
+ with standalone_statespace, NoTracing():
1356
+ string = LazyIntSymbolicStr(list(map(ord, " ")))
1357
+ with ResumedTracing():
1358
+ ret = list(filter(str.isspace, [string]))
1359
+ assert ret == [string]
1360
+
1361
+
1362
+ def test_str_filter_with_none():
1363
+ with standalone_statespace as space, NoTracing():
1364
+ string = LazyIntSymbolicStr([ord("a")])
1365
+ truthyint = proxy_for_type(int, "truthyint")
1366
+ falseyint = proxy_for_type(int, "falseyint")
1367
+ with ResumedTracing():
1368
+ space.add(truthyint == 10)
1369
+ space.add(falseyint == 0)
1370
+ ret = deep_realize(list(filter(None, [falseyint, 42, 0, truthyint])))
1371
+ assert ret == [42, 10]
1372
+
1373
+
1374
+ def test_str_find() -> None:
1375
+ with standalone_statespace, NoTracing():
1376
+ string = LazyIntSymbolicStr(list(map(ord, "aabc")))
1377
+ assert string.find("ab") == 1
1378
+
1379
+
1380
+ def test_str_find_symbolic() -> None:
1381
+ def f(s: str) -> int:
1382
+ """
1383
+ pre: len(s) == 3
1384
+ post: _ == -1
1385
+ """
1386
+ return "haystack".find(s)
1387
+
1388
+ check_states(f, POST_FAIL)
1389
+
1390
+
1391
+ def test_str_find_notfound() -> None:
1392
+ with standalone_statespace, NoTracing():
1393
+ string = LazyIntSymbolicStr([])
1394
+ assert string.find("abc", 1, 3) == -1
1395
+
1396
+
1397
+ def test_str_format_basic():
1398
+ with standalone_statespace as space:
1399
+ with NoTracing():
1400
+ s = LazyIntSymbolicStr("s")
1401
+ space.add(s.__len__() == 1)
1402
+ assert space.is_possible(s == "z")
1403
+ assert space.is_possible(ord("a{0}c".format(s)[1]) == ord("b"))
1404
+
1405
+
1406
+ def test_str_format_map():
1407
+ with standalone_statespace as space:
1408
+ with NoTracing():
1409
+ s = LazyIntSymbolicStr("s")
1410
+ space.add(s.__len__() == 1)
1411
+ assert space.is_possible(s == "z")
1412
+ assert space.is_possible(ord("a{foo}c".format_map({"foo": s})[1]) == ord("b"))
1413
+
1414
+
1415
+ def test_str_rfind() -> None:
1416
+ with standalone_statespace, NoTracing():
1417
+ string = LazyIntSymbolicStr(list(map(ord, "ababb")))
1418
+ assert string.rfind("ab") == 2
1419
+
1420
+
1421
+ def test_str_rfind_notfound() -> None:
1422
+ with standalone_statespace, NoTracing():
1423
+ string = LazyIntSymbolicStr(list(map(ord, "ababb")))
1424
+ assert string.rfind("ab") == 2
1425
+
1426
+
1427
+ def test_str_split_limits():
1428
+ with standalone_statespace, NoTracing():
1429
+ string = LazyIntSymbolicStr(list(map(ord, "a:b:c")))
1430
+ parts = realize(string.split(":", 1))
1431
+ assert parts == ["a", "b:c"]
1432
+ parts = realize(string.split(":"))
1433
+ assert parts == ["a", "b", "c"]
1434
+
1435
+
1436
+ def test_str_rsplit():
1437
+ with standalone_statespace, NoTracing():
1438
+ string = LazyIntSymbolicStr(list(map(ord, "a:b:c")))
1439
+ parts = realize(string.rsplit(":", 1))
1440
+ assert parts == ["a:b", "c"]
1441
+
1442
+
1443
+ def test_str_contains():
1444
+ with standalone_statespace:
1445
+ with NoTracing():
1446
+ small = LazyIntSymbolicStr([ord("b"), ord("c")])
1447
+ big = LazyIntSymbolicStr([ord("a"), ord("b"), ord("c"), ord("d")])
1448
+ assert small in big
1449
+ assert big not in small
1450
+ assert small in "bc"
1451
+ assert small not in "b"
1452
+ assert "c" in small
1453
+ assert "cd" not in small
1454
+
1455
+
1456
+ def test_str_deep_realize():
1457
+ with standalone_statespace, NoTracing():
1458
+ a = LazyIntSymbolicStr("a")
1459
+ tupl = (a, (a,))
1460
+ realized = deep_realize(tupl)
1461
+ assert list(map(type, realized)) == [str, tuple]
1462
+ assert list(map(type, realized[1])) == [str]
1463
+ assert realized[0] is realized[1][0]
1464
+
1465
+
1466
+ def test_str_strip():
1467
+ with standalone_statespace:
1468
+ with NoTracing():
1469
+ x = LazyIntSymbolicStr(list(map(ord, " A b\n")))
1470
+ assert x.strip() == "A b"
1471
+
1472
+
1473
+ def test_str_lower():
1474
+ chr_Idot = "\u0130" # Capital I with dot above
1475
+ # (it's the only unicde char that lower()s to 2 characters)
1476
+ with standalone_statespace:
1477
+ with NoTracing():
1478
+ x = LazyIntSymbolicStr(list(map(ord, "Ab" + chr_Idot)))
1479
+ assert x.lower() == "abi\u0307"
1480
+
1481
+
1482
+ def test_str_title():
1483
+ chr_lj = "\u01c9" # "lj"
1484
+ chr_Lj = "\u01c8" # "Lj" (different from "LJ", "\u01c7")
1485
+ with standalone_statespace:
1486
+ with NoTracing():
1487
+ lj = LazyIntSymbolicStr(list(map(ord, chr_lj)))
1488
+ lja_b = LazyIntSymbolicStr(list(map(ord, chr_lj + "a_b")))
1489
+ assert lja_b.title() == chr_Lj + "a_B"
1490
+
1491
+
1492
+ def test_object_deep_realize():
1493
+ @dataclasses.dataclass
1494
+ class Container:
1495
+ contents: int
1496
+
1497
+ with standalone_statespace as space, NoTracing():
1498
+ a = SymbolicObject("a", Container)
1499
+ shallow = realize(a)
1500
+ assert type(shallow) is Container
1501
+ assert type(shallow.contents) is not int
1502
+ deep = deep_realize(a)
1503
+ assert type(deep) is Container
1504
+ assert type(deep.contents) is int
1505
+
1506
+
1507
+ def test_seq_string_deep_realize():
1508
+ with standalone_statespace as space, NoTracing():
1509
+ tupl = SymbolicArrayBasedUniformTuple("s", List[str])
1510
+ space.add(tupl._len() == 2)
1511
+ realized = deep_realize(tupl)
1512
+ assert list(map(type, realized)) == [str, str]
1513
+
1514
+
1515
+ @pytest.mark.demo
1516
+ def test_tuple___add___method():
1517
+ def f(a: Tuple[int, ...]):
1518
+ """
1519
+ Can we get this function to return (1, 2, 3, 4)?
1520
+
1521
+ post: _ != (1, 2, 3, 4)
1522
+ """
1523
+ return (1,) + a + (4,)
1524
+
1525
+ check_states(f, POST_FAIL)
1526
+
1527
+
1528
+ @pytest.mark.demo
1529
+ def test_tuple___getitem___method() -> None:
1530
+ def f(t: Tuple[int, ...], idx: int) -> int:
1531
+ """
1532
+ Can we find 42 in the given tuple at the given index?
1533
+
1534
+ pre: idx >= 0 and idx < len(t)
1535
+ post: _ != 42
1536
+ """
1537
+ return t[idx]
1538
+
1539
+ check_states(f, POST_FAIL)
1540
+
1541
+
1542
+ @pytest.mark.demo
1543
+ def test_tuple___len___method():
1544
+ def f(a: Tuple[int, ...]):
1545
+ """
1546
+ Can we find a tuple of length 8?
1547
+
1548
+ post: _ != 8
1549
+ """
1550
+ return len(a)
1551
+
1552
+ check_states(f, POST_FAIL)
1553
+
1554
+
1555
+ def test_tuple___repr__symbolic_in_concrete(space) -> None:
1556
+ x = proxy_for_type(int, "x")
1557
+ with ResumedTracing():
1558
+ space.add(x == 4) # type: ignore
1559
+ container = (x, x)
1560
+ assert repr(container) == "(4, 4)"
1561
+
1562
+
1563
+ def test_tuple___repr__symbolic_in_concrete_namedtuple(space) -> None:
1564
+ NamedTupleClass = collections.namedtuple("NamedTupleClass", ["target"])
1565
+ x = proxy_for_type(int, "x")
1566
+ with ResumedTracing():
1567
+ space.add(x == 4) # type: ignore
1568
+ container = NamedTupleClass(target=x)
1569
+ assert repr(container) == "NamedTupleClass(target=4)"
1570
+
1571
+
1572
+ def test_tuple_range_intersection_fail() -> None:
1573
+ def f(a: Tuple[int, int], b: Tuple[int, int]) -> Optional[Tuple[int, int]]:
1574
+ """
1575
+ pre: a[0] < a[1] and b[0] < b[1]
1576
+ post: _[0] <= _[1]
1577
+ """
1578
+ return (max(a[0], b[0]), min(a[1], b[1]))
1579
+
1580
+ check_states(f, POST_FAIL)
1581
+
1582
+
1583
+ def test_tuple_range_intersection_ok() -> None:
1584
+ def f(a: Tuple[int, int], b: Tuple[int, int]) -> Optional[Tuple[int, int]]:
1585
+ """
1586
+ pre: a[0] < a[1] and b[0] < b[1]
1587
+ post: _ is None or _[0] <= _[1]
1588
+ """
1589
+ if a[1] > b[0] and a[0] < b[1]: # (if the ranges overlap)
1590
+ return (max(a[0], b[0]), min(a[1], b[1]))
1591
+ else:
1592
+ return None
1593
+
1594
+ check_states(f, CONFIRMED)
1595
+
1596
+
1597
+ def test_tuple_with_uniform_values_fail() -> None:
1598
+ def f(a: Tuple[int, ...]) -> float:
1599
+ """
1600
+ post: True
1601
+ """
1602
+ return sum(a) / len(a)
1603
+
1604
+ check_states(f, EXEC_ERR)
1605
+
1606
+
1607
+ def test_tuple_with_uniform_values_ok() -> None:
1608
+ def f(a: Tuple[int, ...]) -> Tuple[int, ...]:
1609
+ """
1610
+ pre: len(a) < 4
1611
+ post: 0 not in _
1612
+ """
1613
+ return tuple(x for x in a if x)
1614
+
1615
+ check_states(f, CONFIRMED)
1616
+
1617
+
1618
+ def test_tuple_runtime_type() -> None:
1619
+ def f(t: Tuple) -> Tuple:
1620
+ """post: t != (1, 2)"""
1621
+ return t
1622
+
1623
+ check_states(f, POST_FAIL)
1624
+
1625
+
1626
+ def test_empty_tuple(space) -> None:
1627
+ t = proxy_for_type(Tuple[()], "t")
1628
+ with ResumedTracing():
1629
+ assert type(t) is tuple
1630
+ assert t == ()
1631
+
1632
+
1633
+ def test_tuple_isinstance_check() -> None:
1634
+ def f(uniform_tuple: Tuple[List, ...], basic_tuple: tuple) -> Tuple[bool, bool]:
1635
+ """post: _ == (True, True)"""
1636
+ return (isinstance(uniform_tuple, tuple), isinstance(basic_tuple, tuple))
1637
+
1638
+ check_states(f, CONFIRMED)
1639
+
1640
+
1641
+ def test_range___len___method() -> None:
1642
+ def f(a: int) -> Iterable[int]:
1643
+ """
1644
+ post: len(_) == a or a < 0
1645
+ """
1646
+ return range(a)
1647
+
1648
+ check_states(f, CONFIRMED)
1649
+
1650
+
1651
+ @pytest.mark.demo
1652
+ def test_range___reversed___method() -> None:
1653
+ def f(start: int, stop: int, step: int) -> List[int]:
1654
+ """
1655
+ Does some reversed range equal [12, 8]?
1656
+
1657
+ pre: step != 0
1658
+ post: _ != [12, 8]
1659
+ """
1660
+ return list(reversed(range(start, stop, step)))
1661
+
1662
+ check_states(f, POST_FAIL)
1663
+
1664
+
1665
+ def test_range_slicing(space) -> None:
1666
+ assert list(range(3, 40, 2)[5::-1]) == [13, 11, 9, 7, 5, 3]
1667
+ # Repeat this with a symbolic range:
1668
+ rng = proxy_for_type(range, "rng")
1669
+ newstart = proxy_for_type(int, "newstart")
1670
+ with ResumedTracing():
1671
+ space.add(rng.start == 3) # type: ignore
1672
+ space.add(rng.stop == 40) # type: ignore
1673
+ space.add(rng.step == 2) # type: ignore
1674
+ space.add(newstart == 5) # type: ignore
1675
+ assert list(rng[newstart::-1]) == [13, 11, 9, 7, 5, 3]
1676
+
1677
+
1678
+ def test_range_realization(space) -> None:
1679
+ rng = proxy_for_type(range, "rng")
1680
+ assert isinstance(rng, SymbolicRange)
1681
+ with ResumedTracing():
1682
+ assert isinstance(rng, range)
1683
+ realized_range = realize(rng)
1684
+ assert isinstance(realized_range, range)
1685
+ with ResumedTracing():
1686
+ assert realized_range == rng
1687
+ assert hash(realized_range) == hash(rng)
1688
+
1689
+
1690
+ @pytest.mark.demo
1691
+ def test_list___contains___method() -> None:
1692
+ def f(a: int, b: List[int]) -> bool:
1693
+ """
1694
+ Is full containment checking equivalent to checking the first 3 elements?
1695
+
1696
+ post: _ == (a in b[:3])
1697
+ """
1698
+ return a in b
1699
+
1700
+ check_states(f, POST_FAIL)
1701
+
1702
+
1703
+ def test_list___contains___ok() -> None:
1704
+ def f(a: int, b: List[int]) -> bool:
1705
+ """
1706
+ pre: 1 == len(b)
1707
+ post: _ == (a == b[0])
1708
+ """
1709
+ return a in b
1710
+
1711
+ check_states(f, CONFIRMED)
1712
+
1713
+
1714
+ @pytest.mark.demo
1715
+ def test_list___add___method() -> None:
1716
+ def f(a: List[int]) -> List[int]:
1717
+ """
1718
+ Does doubling the list always make it longer?
1719
+
1720
+ post: len(_) > len(a)
1721
+ """
1722
+ return a + a
1723
+
1724
+ check_states(f, POST_FAIL)
1725
+
1726
+
1727
+ def test_list___repr___symbolic_in_concrete(space) -> None:
1728
+ x = proxy_for_type(int, "x")
1729
+ with ResumedTracing():
1730
+ space.add(x == 4) # type: ignore
1731
+ continer = [x]
1732
+ assert f"{continer=}" == "continer=[4]"
1733
+
1734
+
1735
+ def test_list_doubling_ok() -> None:
1736
+ def f(a: List[int]) -> List[int]:
1737
+ """
1738
+ post: len(_) > len(a) or not a
1739
+ """
1740
+ return a + a
1741
+
1742
+ check_states(f, CONFIRMED)
1743
+
1744
+
1745
+ def test_list_multiply_ok() -> None:
1746
+ def f(a: List[int]) -> List[int]:
1747
+ """post: len(_) == len(a) * 5"""
1748
+ return a * 3 + 2 * a
1749
+
1750
+ check_states(f, CONFIRMED)
1751
+
1752
+
1753
+ def test_list_average() -> None:
1754
+ def average(numbers: List[float]) -> float:
1755
+ """
1756
+ pre: len(numbers) > 0
1757
+ post: min(numbers) <= _ <= max(numbers)
1758
+ """
1759
+ return sum(numbers) / len(numbers)
1760
+
1761
+ check_states(average, CANNOT_CONFIRM)
1762
+
1763
+
1764
+ def test_list_mixed_symbolic_and_literal_concat_ok() -> None:
1765
+ def f(ls: List[int], i: int) -> List[int]:
1766
+ """
1767
+ pre: i >= 0
1768
+ post: len(_) == len(ls) + 1
1769
+ """
1770
+ return (
1771
+ ls[:i]
1772
+ + [
1773
+ 42,
1774
+ ]
1775
+ + ls[i:]
1776
+ )
1777
+
1778
+ check_states(f, CONFIRMED)
1779
+
1780
+
1781
+ def test_list_range_fail() -> None:
1782
+ def f(ls: List[int]) -> List[int]:
1783
+ """
1784
+ pre: len(ls) == 3
1785
+ post: len(_) > len(ls)
1786
+ """
1787
+ n: List[int] = []
1788
+ for i in range(len(ls)):
1789
+ n.append(ls[i] + 1)
1790
+ return n
1791
+
1792
+ check_states(f, POST_FAIL)
1793
+
1794
+
1795
+ def test_list_range_ok() -> None:
1796
+ def f(ls: List[int]) -> List[int]:
1797
+ """
1798
+ pre: ls and len(ls) < 10 # (max is to cap runtime)
1799
+ post: _[0] == ls[0] + 1
1800
+ """
1801
+ n: List[int] = []
1802
+ for i in range(len(ls)):
1803
+ n.append(ls[i] + 1)
1804
+ return n
1805
+
1806
+ check_states(f, CONFIRMED)
1807
+
1808
+
1809
+ def test_list_equality() -> None:
1810
+ def f(ls: List[int]) -> List[int]:
1811
+ """
1812
+ pre: len(ls) > 0
1813
+ post: _ != ls
1814
+ """
1815
+ # extra check for positive equality:
1816
+ assert ls == [x for x in ls], "list does not equal itself"
1817
+ nl = ls[:]
1818
+ nl[0] = 42
1819
+ return nl
1820
+
1821
+ check_states(f, POST_FAIL)
1822
+
1823
+
1824
+ def test_list_extend_literal_unknown() -> None:
1825
+ def f(ls: List[int]) -> List[int]:
1826
+ """
1827
+ post: _[:2] == [1, 2]
1828
+ """
1829
+ r = [1, 2, 3]
1830
+ r.extend(ls)
1831
+ return r
1832
+
1833
+ check_states(f, CANNOT_CONFIRM)
1834
+
1835
+
1836
+ @pytest.mark.demo
1837
+ def test_list___getitem___method() -> None:
1838
+ def f(ls: List[int], idx: int) -> int:
1839
+ """
1840
+ Can we find 42 in the given list at the given index?
1841
+
1842
+ pre: idx >= 0 and idx < len(ls)
1843
+ post: _ != 42
1844
+ """
1845
+ return ls[idx]
1846
+
1847
+ check_states(f, POST_FAIL)
1848
+
1849
+
1850
+ def test_list____getitem___error() -> None:
1851
+ def f(ls: List[int], idx: int) -> int:
1852
+ """
1853
+ pre: idx >= 0 and len(ls) > 2
1854
+ post: True
1855
+ """
1856
+ return ls[idx]
1857
+
1858
+ (actual, expected) = check_exec_err(f, "IndexError")
1859
+ assert actual == expected
1860
+
1861
+
1862
+ def test_list____getitem___type_error() -> None:
1863
+ def f(ls: List[int]) -> int:
1864
+ """post: True"""
1865
+ return ls[0.0:] # type: ignore
1866
+
1867
+ (actual, expected) = check_exec_err(f, "TypeError")
1868
+ assert actual == expected
1869
+
1870
+
1871
+ def test_list____getitem___ok() -> None:
1872
+ def f(ls: List[int]) -> bool:
1873
+ """
1874
+ pre: len(ls) <= 3
1875
+ post: _ == (7 in ls)
1876
+ """
1877
+ try:
1878
+ return ls[ls.index(7)] == 7
1879
+ return True
1880
+ except ValueError:
1881
+ return False
1882
+
1883
+ check_states(f, CONFIRMED)
1884
+
1885
+
1886
+ def test_list_nested_lists_fail() -> None:
1887
+ def f(ls: List[List[int]]) -> int:
1888
+ """
1889
+ post: _ > 0
1890
+ """
1891
+ total = 0
1892
+ for i in ls:
1893
+ total += len(i)
1894
+ return total
1895
+
1896
+ check_states(f, POST_FAIL)
1897
+
1898
+
1899
+ def test_list_nested_lists_ok() -> None:
1900
+ def f(ls: List[List[int]]) -> int:
1901
+ """
1902
+ pre: len(ls) < 4
1903
+ post: _ >= 0
1904
+ """
1905
+ total = 0
1906
+ for i in ls:
1907
+ total += len(i)
1908
+ return total
1909
+
1910
+ check_states(f, CONFIRMED)
1911
+
1912
+
1913
+ def test_list_iterable() -> None:
1914
+ def f(a: Iterable[int]) -> int:
1915
+ """
1916
+ pre: a
1917
+ post: _ in a
1918
+ """
1919
+ return next(iter(a))
1920
+
1921
+ check_states(f, CONFIRMED)
1922
+
1923
+
1924
+ def test_list_isinstance_check() -> None:
1925
+ def f(ls: List) -> bool:
1926
+ """post: _"""
1927
+ return isinstance(ls, list)
1928
+
1929
+ check_states(f, CONFIRMED)
1930
+
1931
+
1932
+ def test_list_slice_outside_range_ok() -> None:
1933
+ def f(ls: List[int], i: int) -> List[int]:
1934
+ """
1935
+ pre: i >= len(ls)
1936
+ post: _ == ls
1937
+ """
1938
+ return ls[:i]
1939
+
1940
+ check_states(f, CONFIRMED)
1941
+
1942
+
1943
+ def test_list_slice_amount() -> None:
1944
+ def f(ls: List[int]) -> List[int]:
1945
+ """
1946
+ pre: len(ls) >= 3
1947
+ post: len(_) == 1
1948
+ """
1949
+ return ls[2:3]
1950
+
1951
+ check_states(f, CONFIRMED)
1952
+
1953
+
1954
+ def test_list____setitem___ok() -> None:
1955
+ def f(ls: List[int]) -> None:
1956
+ """
1957
+ pre: len(ls) >= 2
1958
+ post[ls]:
1959
+ ls[1] == 42
1960
+ ls[2] == 43
1961
+ len(ls) == 4
1962
+ """
1963
+ ls[1:-1] = [42, 43]
1964
+
1965
+ check_states(f, CONFIRMED)
1966
+
1967
+
1968
+ def test_list___setitem___out_of_bounds() -> None:
1969
+ def f(ls: List[int], i: int) -> None:
1970
+ """
1971
+ pre: i != -1
1972
+ post: ls == __old__.ls[:i] + __old__.ls[i+1:]
1973
+ """
1974
+ ls[i : i + 1] = []
1975
+
1976
+ check_states(f, CANNOT_CONFIRM)
1977
+
1978
+
1979
+ def test_list_insert_ok() -> None:
1980
+ def f(ls: List[int]) -> None:
1981
+ """
1982
+ pre: len(ls) == 4
1983
+ post[ls]:
1984
+ len(ls) == 5
1985
+ ls[2] == 42
1986
+ """
1987
+ ls.insert(-2, 42)
1988
+
1989
+ check_states(f, CONFIRMED)
1990
+
1991
+
1992
+ def test_list_insert_with_conversions() -> None:
1993
+ def f(ls: List[Set[int]], a: bool, b: int) -> None:
1994
+ """
1995
+ # self.insert(a,b) with {'a': True, 'b': 10, 'self': [{0}]}
1996
+ post: True
1997
+ """
1998
+ ls.insert(a, b) # type: ignore
1999
+
2000
+ check_states(f, CONFIRMED)
2001
+
2002
+
2003
+ def test_list_pop_ok() -> None:
2004
+ def f(ls: List[int]) -> None:
2005
+ """
2006
+ pre: ls == [4, 5]
2007
+ post: ls == [4]
2008
+ """
2009
+ ls.pop()
2010
+
2011
+ check_states(f, CONFIRMED)
2012
+
2013
+
2014
+ def test_list_count_ok() -> None:
2015
+ def f(ls: List[Dict[int, Dict[int, int]]]) -> int:
2016
+ """
2017
+ pre: ls == [{1: {2: 3}}]
2018
+ post: _ == 1
2019
+ """
2020
+ return ls.count({1: {2: 3}})
2021
+
2022
+ check_states(f, CONFIRMED)
2023
+
2024
+
2025
+ @pytest.mark.smoke
2026
+ def test_list___setitem___ok() -> None:
2027
+ def f(ls: List[int]) -> None:
2028
+ """
2029
+ pre: len(ls) >= 4
2030
+ post[ls]: ls[3] == 42
2031
+ """
2032
+ ls[3] = 42
2033
+
2034
+ check_states(f, CONFIRMED)
2035
+
2036
+
2037
+ @pytest.mark.demo
2038
+ def test_list___delitem___method() -> None:
2039
+ def f(ls: List[int]) -> None:
2040
+ """
2041
+ Can we trim the tail two elements and have three left over?
2042
+
2043
+ post[ls]: len(ls) != 3
2044
+ """
2045
+ del ls[-2:]
2046
+
2047
+ check_states(f, POST_FAIL)
2048
+
2049
+
2050
+ def test_list___delitem___ok() -> None:
2051
+ def f(ls: List[int]) -> None:
2052
+ """
2053
+ pre: len(ls) == 5
2054
+ post[ls]: len(ls) == 4
2055
+ """
2056
+ del ls[2]
2057
+
2058
+ check_states(f, CONFIRMED)
2059
+
2060
+
2061
+ def test_list___delitem___type_error() -> None:
2062
+ def f(ls: List[float]) -> None:
2063
+ """
2064
+ pre: len(ls) == 0
2065
+ post: True
2066
+ """
2067
+ del ls[1.0] # type: ignore
2068
+
2069
+ (actual, expected) = check_exec_err(f, "TypeError")
2070
+ assert actual == expected
2071
+
2072
+
2073
+ def test_list___delitem___out_of_bounds() -> None:
2074
+ def f(ls: List[float]) -> None:
2075
+ """post: True"""
2076
+ del ls[1]
2077
+
2078
+ (actual, expected) = check_exec_err(f, "IndexError")
2079
+ assert actual == expected
2080
+
2081
+
2082
+ def test_list_sort_ok() -> None:
2083
+ def f(ls: List[int]) -> None:
2084
+ """
2085
+ pre: len(ls) == 3
2086
+ post[ls]: ls[0] == min(ls)
2087
+ """
2088
+ ls.sort()
2089
+
2090
+ check_states(f, CONFIRMED)
2091
+
2092
+
2093
+ def test_list_reverse_ok() -> None:
2094
+ def f(ls: List[int]) -> None:
2095
+ """
2096
+ pre: len(ls) == 2
2097
+ post[ls]: ls[0] == 42
2098
+ """
2099
+ ls.append(42)
2100
+ ls.reverse()
2101
+
2102
+ check_states(f, CONFIRMED)
2103
+
2104
+
2105
+ def test_list_comparison_type_error() -> None:
2106
+ def f(a: List[Set], b: str):
2107
+ """post: True"""
2108
+ return a <= b # type: ignore
2109
+
2110
+ (actual, expected) = check_exec_err(f, "TypeError")
2111
+ assert actual == expected
2112
+
2113
+
2114
+ def test_list_shallow_realization():
2115
+ with standalone_statespace as space:
2116
+ nums = proxy_for_type(List[int], "nums")
2117
+ numslen = len(nums)
2118
+ space.add(numslen == 1)
2119
+ with NoTracing():
2120
+ realized = realize(nums)
2121
+ assert type(realized) is list
2122
+ assert len(realized) == 1
2123
+ assert type(realized[0]) is SymbolicInt
2124
+
2125
+
2126
+ def test_list_concrete_with_symbolic_slice(space):
2127
+ idx = proxy_for_type(int, "i")
2128
+ with ResumedTracing():
2129
+ space.add(1 <= idx)
2130
+ space.add(idx <= 3)
2131
+ prefix = [0, 1, 2, 3][:idx]
2132
+ prefixlen = len(prefix)
2133
+ assert isinstance(prefix, CrossHairValue)
2134
+ assert isinstance(prefixlen, CrossHairValue)
2135
+ with ResumedTracing():
2136
+ assert space.is_possible(prefixlen == 1)
2137
+ assert space.is_possible(prefixlen == 3)
2138
+
2139
+
2140
+ def test_list_copy(space):
2141
+ lst = proxy_for_type(List[int], "lst")
2142
+ with ResumedTracing():
2143
+ # Mostly just ensure the various ways of copying don't explode
2144
+ assert lst[:] is not lst
2145
+ assert lst.copy() is not lst
2146
+ assert copy.deepcopy(lst) is not lst
2147
+ assert copy.copy(lst) is not lst
2148
+
2149
+
2150
+ def test_list_copy_compare_without_forking(space):
2151
+ lst = proxy_for_type(List[int], "lst")
2152
+ with ResumedTracing():
2153
+ lst2 = copy.deepcopy(lst)
2154
+ assert lst.inner is not lst2.inner
2155
+ assert type(lst2) is SymbolicList
2156
+ assert lst.inner.var is lst2.inner.var
2157
+ with ResumedTracing():
2158
+ are_same = lst == lst2
2159
+ assert type(are_same) is SymbolicBool
2160
+ assert not space.is_possible(z3.Not(are_same.var))
2161
+
2162
+
2163
+ def test_dict___bool___ok() -> None:
2164
+ def f(a: Dict[int, str]) -> bool:
2165
+ """
2166
+ post[a]: _ == True
2167
+ """
2168
+ a[0] = "zero"
2169
+ return bool(a)
2170
+
2171
+ check_states(f, CONFIRMED)
2172
+
2173
+
2174
+ def test_dict___iter___fail() -> None:
2175
+ def f(a: Dict[int, str]) -> List[int]:
2176
+ """
2177
+ post[a]: 5 in _
2178
+ """
2179
+ a[10] = "ten"
2180
+ return list(a.__iter__())
2181
+
2182
+ check_states(f, POST_FAIL)
2183
+
2184
+
2185
+ def test_dict___iter___ok() -> None:
2186
+ def f(a: Dict[int, str]) -> List[int]:
2187
+ """
2188
+ pre: len(a) < 3
2189
+ post[a]: 10 in _
2190
+ """
2191
+ a[10] = "ten"
2192
+ return list(a.__iter__())
2193
+
2194
+ check_states(f, CONFIRMED)
2195
+
2196
+
2197
+ def test_dict___or___method():
2198
+ with standalone_statespace as space:
2199
+ d = proxy_for_type(Dict[int, int], "d")
2200
+ space.add(len(d) == 0)
2201
+ with pytest.raises(TypeError):
2202
+ d | set()
2203
+ if sys.version_info >= (3, 9):
2204
+ assert d | {1: 2} == {1: 2}
2205
+ else:
2206
+ with pytest.raises(TypeError):
2207
+ d | {1: 2}
2208
+
2209
+
2210
+ @pytest.mark.demo("yellow")
2211
+ def test_dict___delitem___method() -> None:
2212
+ def f(a: Dict[str, int]) -> None:
2213
+ """
2214
+ Can deleting the key "foo" leave an empty dictionary?
2215
+
2216
+ NOTE: Deleting a symbolic key from a concrete dictionary is not effectively
2217
+ reasoned about at present:
2218
+
2219
+ dictionary | key | effective?
2220
+ -----------+----------+-----------
2221
+ symbolic | * | yes
2222
+ * | concrete | yes
2223
+ concrete | symbolic | no
2224
+
2225
+
2226
+ raises: KeyError
2227
+ post[a]: len(a) != 0
2228
+ """
2229
+ del a["foo"]
2230
+
2231
+ check_states(f, POST_FAIL)
2232
+
2233
+
2234
+ @pytest.mark.demo
2235
+ def test_dict___eq___method() -> None:
2236
+ def f(t: dict) -> dict:
2237
+ """
2238
+ Can we find a dictionary that maps 50 to 100?
2239
+
2240
+ post: t != {50: 100}
2241
+ """
2242
+ return t
2243
+
2244
+ check_states(f, POST_FAIL)
2245
+
2246
+
2247
+ def test_dict___eq___deep() -> None:
2248
+ def f(a: Dict[bool, set], b: List[Set[float]]) -> object:
2249
+ """
2250
+ pre: a == {True: set()}
2251
+ pre: b == [set(), {1.0}]
2252
+ post: _
2253
+ """
2254
+ if a == {True: set()}:
2255
+ if b == [set(), {1.0}]:
2256
+ return False
2257
+ return True
2258
+
2259
+ check_states(f, POST_FAIL)
2260
+
2261
+
2262
+ def test_dict___eq___ok() -> None:
2263
+ def f(d: Dict[int, int]) -> Dict[int, int]:
2264
+ """post: _ == {**_}"""
2265
+ return d
2266
+
2267
+ check_states(f, CANNOT_CONFIRM)
2268
+
2269
+
2270
+ @pytest.mark.demo
2271
+ def test_dict___getitem___method() -> None:
2272
+ def f(m: Dict[int, int]):
2273
+ """
2274
+ Can we make a path from 0 to 2, by indexing into the dictionary twice?
2275
+
2276
+ pre: len(m) == 2
2277
+ raises: KeyError
2278
+ post: _ != 2
2279
+ """
2280
+ return m[m[0]]
2281
+
2282
+ check_states(f, POST_FAIL)
2283
+
2284
+
2285
+ def test_dict___getitem___implicit_conversion_for_keys_fail() -> None:
2286
+ def f(m: Dict[complex, float], b: bool, i: int):
2287
+ """
2288
+ pre: not m
2289
+ post: len(m) != 1
2290
+ """
2291
+ m[b] = 2.0
2292
+ m[i] = 3.0
2293
+
2294
+ check_states(f, POST_FAIL)
2295
+
2296
+
2297
+ def test_dict__items__works_with_symbolic_self(space) -> None:
2298
+ x = proxy_for_type(Dict[int, int], "x")
2299
+ with ResumedTracing():
2300
+ x[42] = 42
2301
+ assert (42, 42) in list(dict.items(x))
2302
+
2303
+
2304
+ def test_dict___repr___symbolic_in_concrete(space) -> None:
2305
+ x = proxy_for_type(int, "x")
2306
+ with ResumedTracing():
2307
+ space.add(x == 4) # type: ignore
2308
+ container = {x: x}
2309
+ assert repr(container) == "{4: 4}"
2310
+
2311
+
2312
+ @pytest.mark.demo("yellow")
2313
+ def test_dict___setitem___method() -> None:
2314
+ def f(a: Dict[int, int], k: int, v: int) -> None:
2315
+ """
2316
+ Can we make a dictionary assignment, and be left with {4: 5, 10: 20}?
2317
+
2318
+ NOTE: CrossHair cannot effectively handle the assignment of a symbolic key on a
2319
+ concrete dictionary, e.g. `d={4:5}; d[k] = 20`
2320
+
2321
+ dictionary | key | value | effective?
2322
+ -----------+----------+-------+-----------
2323
+ symbolic | * | * | yes
2324
+ * | concrete | * | yes
2325
+ concrete | symbolic | * | no
2326
+
2327
+
2328
+ post[a]: a != {4: 5, 10: 20}
2329
+ """
2330
+ a[k] = v
2331
+
2332
+ check_states(f, POST_FAIL)
2333
+
2334
+
2335
+ def test_dict___setitem___ok() -> None:
2336
+ def f(a: Dict[int, int], k: int, v: int) -> None:
2337
+ """
2338
+ post[a]: a[k] == v
2339
+ """
2340
+ a[k] = v
2341
+
2342
+ check_states(f, CONFIRMED)
2343
+
2344
+
2345
+ def test_dict___setitem___on_copy() -> None:
2346
+ def f(d: Dict[int, int]) -> Dict[int, int]:
2347
+ """post: _ != d"""
2348
+ d = d.copy()
2349
+ d[42] = 100
2350
+ return d
2351
+
2352
+ check_states(f, POST_FAIL)
2353
+
2354
+
2355
+ def TODO_test_dict___setitem___on_concrete() -> None:
2356
+ # NOTE: This is very challenging to implement: opcode interception could upgrade
2357
+ # the concrete dictionary to a symbolic, but the original dictionary may be aliased.
2358
+ # One investigation: start everything out as symbolic by intercepting BUILD_MAP and
2359
+ # BUILD_SET. This potentially also lets us detect writes to persistent state
2360
+ # (because all pre-existing dicts/sets will be concrete).
2361
+ def f(k: int, v: int) -> Dict[int, int]:
2362
+ """post: _[100] == 200"""
2363
+ d = {100: 200}
2364
+ d[k] = v
2365
+ return d
2366
+
2367
+ check_states(f, POST_FAIL)
2368
+
2369
+
2370
+ def test_dict___str__() -> None:
2371
+ def f(a: Dict[int, str]) -> str:
2372
+ """
2373
+ pre: len(a) == 0
2374
+ post: _ == '{}'
2375
+ """
2376
+ return str(a)
2377
+
2378
+ check_states(f, CONFIRMED)
2379
+
2380
+
2381
+ @pytest.mark.demo
2382
+ def test_dict_get_method():
2383
+ def f(x: int) -> int:
2384
+ """
2385
+ Can we find the key that is mapped to 5?
2386
+
2387
+ post: _ != 5
2388
+ """
2389
+ a = {2: 3, 4: 5, 6: 7}
2390
+ return a.get(x, 9)
2391
+
2392
+ check_states(f, POST_FAIL)
2393
+
2394
+
2395
+ def test_dict_get_with_defaults_ok() -> None:
2396
+ def f(a: Dict[int, int]) -> int:
2397
+ """post: (_ == 2) or (_ == a[4])"""
2398
+ return a.get(4, 2)
2399
+
2400
+ check_states(f, CONFIRMED)
2401
+
2402
+
2403
+ def test_dict_items_ok() -> None:
2404
+ def f(a: Dict[int, str]) -> Iterable[Tuple[int, str]]:
2405
+ """
2406
+ pre: len(a) < 5
2407
+ post[a]: (10,'ten') in _
2408
+ """
2409
+ a[10] = "ten"
2410
+ return a.items()
2411
+
2412
+ check_states(f, CONFIRMED)
2413
+
2414
+
2415
+ def test_dict_setdefault_float_int_comparison() -> None:
2416
+ def f(a: Dict[int, int]):
2417
+ """
2418
+ pre: a == {2: 0}
2419
+ post: _ == 0
2420
+ """
2421
+ return a.setdefault(2.0, {True: "0"}) # type: ignore
2422
+
2423
+ check_states(f, CONFIRMED)
2424
+
2425
+
2426
+ def test_dict_construction_from_generator(space):
2427
+ with ResumedTracing():
2428
+ d = dict((k, v) for k, v in [(1, 2), (3, 4)])
2429
+ assert d == {1: 2, 3: 4}
2430
+
2431
+
2432
+ def test_dict_construction_copies_dict(space):
2433
+ with ResumedTracing():
2434
+ source = {1: 2, 3: 4}
2435
+ copy = dict(source)
2436
+ source[5] = 6
2437
+ assert copy == {1: 2, 3: 4}
2438
+
2439
+
2440
+ def test_dict_construction_with_duplicate_keys(space):
2441
+ with ResumedTracing():
2442
+ d = dict([(1, 2), (3, 4), (1, 10), (5, 6), (3, 10)])
2443
+ assert d == {1: 10, 3: 10, 5: 6}
2444
+
2445
+
2446
+ def test_dict_over_objects() -> None:
2447
+ def f(a: Dict[object, object]) -> int:
2448
+ """
2449
+ post: _ >= 0
2450
+ """
2451
+ return len(a)
2452
+
2453
+ check_states(f, CONFIRMED)
2454
+
2455
+
2456
+ def test_dict_over_heap_objects() -> None:
2457
+ def f(a: Dict[Tuple[int], int]) -> Optional[int]:
2458
+ """
2459
+ post: _ != 10
2460
+ """
2461
+ return a.get((5,))
2462
+
2463
+ check_states(f, POST_FAIL)
2464
+
2465
+
2466
+ def test_dict_complex_contents() -> None:
2467
+ def f(d: Dict[Tuple[int, bool], Tuple[float, int]]) -> int:
2468
+ """
2469
+ post: _ > 0
2470
+ """
2471
+ if (42, True) in d:
2472
+ return d[(42, True)][1]
2473
+ else:
2474
+ return 42
2475
+
2476
+ check_states(f, MessageType.POST_FAIL)
2477
+
2478
+
2479
+ def test_dict_isinstance_check() -> None:
2480
+ def f(smtdict: Dict[int, int], heapdict: Dict) -> Tuple[bool, bool]:
2481
+ """post: _ == (True, True)"""
2482
+ return (isinstance(smtdict, dict), isinstance(heapdict, dict))
2483
+
2484
+ check_states(f, CONFIRMED)
2485
+
2486
+
2487
+ def test_dict_subtype_lookup() -> None:
2488
+ def f(d: Dict[Tuple[int, str], int]) -> None:
2489
+ """
2490
+ pre: not d
2491
+ post[d]: [(42, 'fourty-two')] == list(d.keys())
2492
+ """
2493
+ d[(42, "fourty-two")] = 1
2494
+
2495
+ check_states(f, CONFIRMED)
2496
+
2497
+
2498
+ def test_dict_complex_keys() -> None:
2499
+ def f(dx: Dict[Tuple[int, str], int]) -> None:
2500
+ """
2501
+ pre: not dx
2502
+ post[dx]:
2503
+ len(dx) == 1
2504
+ dx[(42, 'fourty-two')] == 1
2505
+ """
2506
+ dx[(42, "fourty-two")] = 1
2507
+
2508
+ check_states(f, CONFIRMED)
2509
+
2510
+
2511
+ def test_dict_has_unique_keys() -> None:
2512
+ def f(d: Dict[Tuple[int, str], int]) -> None:
2513
+ """
2514
+ pre: len(d) == 2 and (1, 'one') in d
2515
+ post[d]: (1, 'one') not in d
2516
+ """
2517
+ del d[(1, "one")]
2518
+
2519
+ check_states(f, CONFIRMED)
2520
+
2521
+
2522
+ def test_dict_wrong_key_type() -> None:
2523
+ def f(d: Dict[int, int], s: str, i: int) -> bool:
2524
+ """
2525
+ post: _
2526
+ raises: KeyError
2527
+ """
2528
+ if i == 0:
2529
+ del d[s] # type: ignore
2530
+ elif i < 0:
2531
+ d[s] = 7 # type: ignore
2532
+ else:
2533
+ _val = d[s] # type: ignore
2534
+ return True
2535
+
2536
+ check_states(f, CANNOT_CONFIRM)
2537
+
2538
+
2539
+ def test_dict_key_type_union() -> None:
2540
+ def f(d: Dict[Union[int, str], int]) -> Dict:
2541
+ """
2542
+ pre: len(d) == 2
2543
+ post: not (42 in d and '42' in d)
2544
+ """
2545
+ return d
2546
+
2547
+ check_states(f, POST_FAIL)
2548
+
2549
+
2550
+ if sys.version_info >= (3, 10):
2551
+
2552
+ def test_dict_type_union_operator() -> None:
2553
+ def f(a: int | str, b: int | str) -> Tuple[int | str, int | str]:
2554
+ """post: _ != (42, "hi")"""
2555
+ return (a, b)
2556
+
2557
+ check_states(f, POST_FAIL)
2558
+
2559
+
2560
+ @pytest.mark.smoke
2561
+ def test_dict_nonuniform_dict_key_types() -> None:
2562
+ def f(a: Dict[Hashable, int]) -> Dict[Hashable, int]:
2563
+ """
2564
+ pre: len(a) == 1
2565
+ post: _[0] == 100
2566
+ """
2567
+ b: Dict[Hashable, int] = {0: 100}
2568
+ b.update(a)
2569
+ return b
2570
+
2571
+ check_states(f, POST_FAIL)
2572
+
2573
+
2574
+ def test_dict_inside_lists() -> None:
2575
+ def f(dicts: List[Dict[int, int]]) -> Dict[int, int]:
2576
+ """
2577
+ pre: len(dicts) <= 1 # to narrow search space (would love to make this larger)
2578
+ post: len(_) <= len(dicts)
2579
+ """
2580
+ ret = {}
2581
+ for d in dicts:
2582
+ ret.update(d)
2583
+ return ret
2584
+
2585
+ check_states(f, POST_FAIL)
2586
+
2587
+
2588
+ @pytest.mark.skip(
2589
+ reason="flakey; seems to get stuck sometimes despite timeout extensions"
2590
+ )
2591
+ def test_dict_inside_lists_with_identity() -> None:
2592
+ def f(dicts: List[Dict[int, int]]):
2593
+ """
2594
+ Removes duplicate keys.
2595
+ pre: len(dicts) == 2
2596
+ pre: len(dicts[0]) == 1
2597
+ post: len(dicts[0]) == 1
2598
+ """
2599
+ # crosshair: max_uninteresting_iterations=50
2600
+ seen: Set[int] = set()
2601
+ for d in dicts:
2602
+ for k in d.keys():
2603
+ if k in seen:
2604
+ del d[k]
2605
+ else:
2606
+ seen.add(k)
2607
+
2608
+ check_states(f, POST_FAIL)
2609
+
2610
+
2611
+ def test_dict_consistent_ordering() -> None:
2612
+ def f(symbolic: Dict[int, int]) -> Tuple[List[int], List[int]]:
2613
+ """post: _[0] == _[1]"""
2614
+ return (list(symbolic.keys()), list(symbolic.keys()))
2615
+
2616
+ check_states(f, CANNOT_CONFIRM)
2617
+
2618
+
2619
+ def test_dict_ordering_after_mutations() -> None:
2620
+ def f(d: Dict[int, int]) -> Tuple[Tuple[int, int], Tuple[int, int]]:
2621
+ """
2622
+ pre: len(d) == 3
2623
+ post[d]: _[0] == _[1]
2624
+ """
2625
+ o1, middle, o2 = d.keys()
2626
+ d[o1] = 42
2627
+ d[o2] = 42
2628
+ del d[middle]
2629
+ n1, n2 = d.keys()
2630
+ return ((o1, o2), (n1, n2))
2631
+
2632
+ check_states(f, CONFIRMED)
2633
+
2634
+
2635
+ def test_dict_alternate_mapping_types() -> None:
2636
+ def f(m1: Mapping[int, int], m2: MutableMapping[int, int]) -> int:
2637
+ """
2638
+ pre: 1 in m1 and 2 in m2
2639
+ post: _ != 10
2640
+ """
2641
+ return m1[1] + m2[2]
2642
+
2643
+ check_states(f, POST_FAIL)
2644
+
2645
+
2646
+ def test_dict_untyped_access():
2647
+ def f(d: dict, k: int) -> dict:
2648
+ """
2649
+ pre: len(d) == 2 # (just to bound the search space a little)
2650
+ pre: 42 in d
2651
+ post: 42 in __return__
2652
+ raises: KeyError
2653
+ """
2654
+ del d[k]
2655
+ return d
2656
+
2657
+ check_states(f, MessageType.POST_FAIL)
2658
+
2659
+
2660
+ # NOTE: TypedDict appeared earlier than 3.9, but was not runtime-detectable until then
2661
+ if sys.version_info >= (3, 9):
2662
+
2663
+ def test_TypedDict_fail() -> None:
2664
+ def f(td: Movie):
2665
+ '''post: _['year'] != 2020 or _['name'] != "hi"'''
2666
+ return td
2667
+
2668
+ check_states(f, POST_FAIL)
2669
+
2670
+ def test_TypedDict_in_container_fail() -> None:
2671
+ def f(tdlist: List[Movie]):
2672
+ """post: _[1]['year'] != 2020"""
2673
+ return tdlist
2674
+
2675
+ check_states(f, POST_FAIL)
2676
+
2677
+
2678
+ @pytest.mark.smoke
2679
+ def test_set_basic_fail() -> None:
2680
+ def f(a: Set[int], k: int) -> None:
2681
+ """
2682
+ pre: len(a) <= 2
2683
+ post[a]: k+1 in a
2684
+ """
2685
+ a.add(k)
2686
+
2687
+ check_states(f, POST_FAIL)
2688
+
2689
+
2690
+ def test_set_basic_ok() -> None:
2691
+ def f(a: Set[int], k: int) -> None:
2692
+ """
2693
+ post[a]: k in a
2694
+ """
2695
+ a.add(k)
2696
+
2697
+ check_states(f, CONFIRMED)
2698
+
2699
+
2700
+ def test_set_union_fail() -> None:
2701
+ def f(a: Set[str], b: Set[str]) -> Set[str]:
2702
+ """
2703
+ pre: len(a) == len(b) == 1 # (just for test performance)
2704
+ post: all(((i in a) and (i in b)) for i in _)
2705
+ """
2706
+ return a | b
2707
+
2708
+ check_states(f, POST_FAIL)
2709
+
2710
+
2711
+ def test_set_union_ok() -> None:
2712
+ def f(a: Set[str], b: Set[str]) -> Set[str]:
2713
+ """
2714
+ post: all(((i in a) or (i in b)) for i in _)
2715
+ """
2716
+ return a | b
2717
+
2718
+ check_states(f, CANNOT_CONFIRM)
2719
+
2720
+
2721
+ def test_set_contains_different_but_equivalent() -> None:
2722
+ def f(s: Set[Union[int, str]]) -> str:
2723
+ """
2724
+ pre: "foobar" in s
2725
+ post: (_ + "bar") in s
2726
+ """
2727
+ return "foo"
2728
+
2729
+ check_states(f, CANNOT_CONFIRM)
2730
+
2731
+
2732
+ # The heaprefs + deferred set assumptions make this too expensive.
2733
+ # TODO: Optimize & re-enable
2734
+ def TODO_set_test_subtype_union() -> None:
2735
+ def f(s: Set[Union[int, str]]) -> Set[Union[int, str]]:
2736
+ """post: not ((42 in s) and ('42' in s))"""
2737
+ return s
2738
+
2739
+ check_states(f, MessageType.POST_FAIL)
2740
+
2741
+
2742
+ def test_set_subset_compare_ok() -> None:
2743
+ # a >= b with {'a': {0.0, 1.0}, 'b': {2.0}}
2744
+ def f(s1: Set[int], s2: Set[int]) -> bool:
2745
+ """
2746
+ pre: s1 == {0, 1}
2747
+ pre: s2 == {2}
2748
+ post: not _
2749
+ """
2750
+ return s1 >= s2
2751
+
2752
+ check_states(f, CONFIRMED)
2753
+
2754
+
2755
+ def test_set_numeric_promotion() -> None:
2756
+ def f(b: bool, s: Set[int]) -> bool:
2757
+ """
2758
+ pre: b == True
2759
+ pre: s == {1}
2760
+ post: _
2761
+ """
2762
+ return b in s
2763
+
2764
+ check_states(f, CONFIRMED)
2765
+
2766
+
2767
+ def test_set_runtime_type_ok() -> None:
2768
+ def f(s: set) -> bool:
2769
+ """post: _"""
2770
+ return True
2771
+
2772
+ check_states(f, CONFIRMED)
2773
+
2774
+
2775
+ def test_set_isinstance_check() -> None:
2776
+ def f(s: Set[object]) -> bool:
2777
+ """post: _"""
2778
+ return isinstance(s, set)
2779
+
2780
+ check_states(f, CONFIRMED)
2781
+
2782
+
2783
+ def test_frozenset___eq__(space):
2784
+ fs = proxy_for_type(Set[FrozenSet[int]], "fs")
2785
+ with ResumedTracing():
2786
+ space.add(len(fs) == 1)
2787
+ space.add(len(next(iter(fs))) == 0)
2788
+ linearset = set([frozenset()])
2789
+ iseq = realize(fs == linearset)
2790
+ assert iseq is True
2791
+
2792
+
2793
+ def test_set___eq__() -> None:
2794
+ def f(a: Set[FrozenSet[int]]) -> object:
2795
+ """
2796
+ pre: a == {frozenset({7}), frozenset({42})}
2797
+ post: _ in ('{frozenset({7}), frozenset({42})}', '{frozenset({42}), frozenset({7})}')
2798
+ """
2799
+ return repr(a)
2800
+
2801
+ check_states(
2802
+ f,
2803
+ MessageType.CONFIRMED,
2804
+ AnalysisOptionSet(per_path_timeout=10, per_condition_timeout=10),
2805
+ )
2806
+
2807
+
2808
+ def test_frozenset___repr__symbolic_in_concrete(space) -> None:
2809
+ x = proxy_for_type(int, "x")
2810
+ with ResumedTracing():
2811
+ space.add(x == 4) # type: ignore
2812
+ container = frozenset([x])
2813
+ assert repr(container) == "frozenset({4})"
2814
+
2815
+
2816
+ def test_set___repr__symbolic_in_concrete(space) -> None:
2817
+ x = proxy_for_type(int, "x")
2818
+ with ResumedTracing():
2819
+ space.add(x == 4) # type: ignore
2820
+ container = {x}
2821
+ assert repr(container) == "{4}"
2822
+
2823
+
2824
+ def test_set_copy(space):
2825
+ x = proxy_for_type(Set[int], "x")
2826
+ with ResumedTracing():
2827
+ space.add(len(x) == 1)
2828
+ (y, z) = copy.deepcopy((x, x))
2829
+ assert not space.is_possible(len(y) != 1)
2830
+ assert not space.is_possible(list(x)[0] != list(y)[0])
2831
+
2832
+
2833
+ def test_set_no_duplicates() -> None:
2834
+ def f(s: Set[int]) -> int:
2835
+ """
2836
+ pre: len(s) == 2
2837
+ post: _
2838
+ """
2839
+ i = iter(s)
2840
+ x = next(i)
2841
+ y = next(i)
2842
+ return x != y
2843
+
2844
+ check_states(f, CONFIRMED)
2845
+
2846
+
2847
+ def test_set_additions_to_concrete() -> None:
2848
+ class F:
2849
+ set_type = set
2850
+
2851
+ def f(self: F, x: int) -> Set[int]:
2852
+ """
2853
+ post: _ != set([1234])
2854
+ """
2855
+ return self.set_type([x, x])
2856
+
2857
+ check_states(f, POST_FAIL)
2858
+
2859
+
2860
+ def test_frozenset_realize():
2861
+ with standalone_statespace as space:
2862
+ with NoTracing():
2863
+ x = proxy_for_type(FrozenSet[int], "x")
2864
+ y = realize(x)
2865
+ assert type(y) is frozenset
2866
+ assert type(x) is frozenset
2867
+
2868
+
2869
+ def test_frozenset_covariance():
2870
+ with standalone_statespace as space:
2871
+ frozen_set = proxy_for_type(FrozenSet[AbstractBase], "x")
2872
+ space.add(frozen_set.__len__() == 1)
2873
+ with NoTracing():
2874
+ assert isinstance(next(iter(frozen_set)), ConcreteSubclass)
2875
+
2876
+
2877
+ def test_set_invariance():
2878
+ # `set` is invariant, but that doesn't mean it cannot contain subclasses -
2879
+ # it just means that something *typed* as a subclass container cannot be
2880
+ # assigned to a superclass container.
2881
+ # Therefore, CrossHair should happily create set contents using subclasses:
2882
+ with standalone_statespace as space:
2883
+ mutable_set = proxy_for_type(Set[AbstractBase], "x")
2884
+ space.add(mutable_set.__len__() == 1)
2885
+ with NoTracing():
2886
+ assert isinstance(next(iter(mutable_set)), ConcreteSubclass)
2887
+
2888
+
2889
+ def test_set_realize():
2890
+ with standalone_statespace as space:
2891
+ with NoTracing():
2892
+ x = proxy_for_type(Set[str], "x")
2893
+ assert type(x) is not set
2894
+ y = realize(x)
2895
+ assert type(y) is set
2896
+ assert type(x) is set
2897
+
2898
+
2899
+ def test_set_iter_partial():
2900
+ with standalone_statespace as space:
2901
+ with NoTracing():
2902
+ x = proxy_for_type(Set[int], "x")
2903
+ space.add(x.__len__() == 2)
2904
+ itr = iter(x)
2905
+ first = next(itr)
2906
+ # leave the iterator incomplete; looking for generator + context mgr problems
2907
+
2908
+
2909
+ def test_set_containment_check_without_iteration():
2910
+ with standalone_statespace as space:
2911
+ x = proxy_for_type(FrozenSet[int], "x")
2912
+ with ResumedTracing():
2913
+ space.add(len(x) == 1)
2914
+ assert space.is_possible(x._is_consistent())
2915
+ space.add(x.__contains__(42))
2916
+ assert space.is_possible(x._is_consistent())
2917
+ space.add(x.__contains__(10))
2918
+ assert not space.is_possible(x._is_consistent())
2919
+
2920
+
2921
+ def test_set_independence(space):
2922
+ with ResumedTracing():
2923
+ s = set()
2924
+ copy = set(s)
2925
+ s |= s
2926
+ s != copy
2927
+
2928
+
2929
+ def test_frozenset___or__(space):
2930
+ x = proxy_for_type(int, "x")
2931
+ y = proxy_for_type(int, "y")
2932
+ with ResumedTracing():
2933
+ space.add(x != y)
2934
+ s1 = frozenset([x])
2935
+ s2 = frozenset([y])
2936
+ assert len(s1 | s2) == 2
2937
+
2938
+
2939
+ class TestProtocols:
2940
+ # TODO: move most of this into a collectionslib_test.py file
2941
+ def test_hashable_values_fail(self) -> None:
2942
+ def f(b: bool, i: int, t: Tuple[str, ...]) -> int:
2943
+ """post: _ % 5 != 0"""
2944
+ return hash((b, i, t))
2945
+
2946
+ check_states(f, POST_FAIL)
2947
+
2948
+ def test_hashable_values_ok(self) -> None:
2949
+ def f(a: Tuple[str, int, float, bool], b: Tuple[str, int, float, bool]) -> int:
2950
+ """post: _ or not (a == b)"""
2951
+ return hash(a) == hash(b)
2952
+
2953
+ check_states(f, CANNOT_CONFIRM)
2954
+
2955
+ def test_symbolic_hashable(self) -> None:
2956
+ def f(a: Hashable) -> int:
2957
+ """post[]: 0 <= _ <= 1"""
2958
+ return hash(a) % 2
2959
+
2960
+ check_states(f, CONFIRMED)
2961
+
2962
+ def test_symbolic_supports(self) -> None:
2963
+ def f(
2964
+ a: SupportsAbs,
2965
+ f: SupportsFloat,
2966
+ i: SupportsInt,
2967
+ r: SupportsRound,
2968
+ # c: SupportsComplex, # TODO: symbolic complex not yet really working
2969
+ b: SupportsBytes,
2970
+ ) -> float:
2971
+ """
2972
+ pre: math.isfinite(f) and math.isfinite(r)
2973
+ post: _.real <= 0
2974
+ """
2975
+ return abs(a) + float(f) + int(i) + round(r) + len(bytes(b))
2976
+ # + complex(c)
2977
+
2978
+ check_states(f, POST_FAIL)
2979
+
2980
+ def test_iterable(self) -> None:
2981
+ T = TypeVar("T")
2982
+
2983
+ def f(a: Iterable[T]) -> T:
2984
+ """
2985
+ pre: a
2986
+ post: _ in a
2987
+ """
2988
+ return next(iter(a))
2989
+
2990
+ check_states(f, CANNOT_CONFIRM)
2991
+
2992
+ def test_bare_type(self) -> None:
2993
+ def f(a: List) -> bool:
2994
+ """
2995
+ pre: a
2996
+ post: _
2997
+ """
2998
+ return bool(a)
2999
+
3000
+ check_states(f, CONFIRMED)
3001
+
3002
+
3003
+ def test_enum_identity_matches_equality() -> None:
3004
+ def f(color1: Color, color2: Color) -> bool:
3005
+ """post: _ == (color1 is color2)"""
3006
+ return color1 == color2
3007
+
3008
+ check_states(f, CONFIRMED)
3009
+
3010
+
3011
+ def test_enum_in_container() -> None:
3012
+ def f(colors: List[Color]) -> bool:
3013
+ """post: not _"""
3014
+ return Color.RED in colors and Color.BLUE in colors
3015
+
3016
+ check_states(f, POST_FAIL)
3017
+
3018
+
3019
+ def test_type_issubclass_ok() -> None:
3020
+ def f(typ: Type[SmokeDetector]):
3021
+ """post: _"""
3022
+ return issubclass(typ, SmokeDetector)
3023
+
3024
+ check_states(f, CONFIRMED)
3025
+
3026
+
3027
+ def test_type_can_be_a_subclass() -> None:
3028
+ def f(typ: Type[Cat]):
3029
+ """post: _ == "<class '__main__.Cat'>" """
3030
+ return str(typ)
3031
+
3032
+ # False when the type is instantiated as "BiggerCat":
3033
+ check_states(f, POST_FAIL)
3034
+
3035
+
3036
+ def test_type_issubclass_fail() -> None:
3037
+ def f(typ: Type):
3038
+ """post: _"""
3039
+ return issubclass(typ, str)
3040
+
3041
+ check_states(f, POST_FAIL)
3042
+
3043
+
3044
+ def test_type_symbolics_without_literal_types() -> None:
3045
+ def f(typ1: Type, typ2: Type[bool], typ3: Type):
3046
+ """post: implies(_, issubclass(typ1, typ3))"""
3047
+ # The counterexample we expect: typ1==str typ2==bool typ3==int
3048
+ return issubclass(typ2, typ3) and typ2 != typ3
3049
+
3050
+ check_states(
3051
+ f,
3052
+ POST_FAIL,
3053
+ )
3054
+
3055
+
3056
+ def test_type_instance_creation() -> None:
3057
+ def f(t: Type[Cat]):
3058
+ """post: _.size() > 0"""
3059
+ return t()
3060
+
3061
+ check_states(f, CONFIRMED)
3062
+
3063
+
3064
+ def test_type_comparison() -> None:
3065
+ def f(t: Type) -> bool:
3066
+ """post: _"""
3067
+ return t == int
3068
+
3069
+ check_states(f, POST_FAIL)
3070
+
3071
+
3072
+ def test_type_as_bool() -> None:
3073
+ def f(t: Type) -> bool:
3074
+ """post: _"""
3075
+ return bool(t)
3076
+
3077
+ check_states(f, CONFIRMED)
3078
+
3079
+
3080
+ def test_type_generic_object_and_type() -> None:
3081
+ def f(thing: object, detector_kind: Type[SmokeDetector]):
3082
+ """post: True"""
3083
+ if isinstance(thing, detector_kind):
3084
+ return thing._is_plugged_in
3085
+ return False
3086
+
3087
+ check_states(f, CANNOT_CONFIRM)
3088
+
3089
+
3090
+ def test_object___eq__() -> None:
3091
+ def f(thing: object, i: int):
3092
+ """post: not _"""
3093
+ return thing == i
3094
+
3095
+ check_states(f, POST_FAIL)
3096
+
3097
+
3098
+ def test_object_get_type_hints(space: StateSpace) -> None:
3099
+ typ = proxy_for_type(type, "typ")
3100
+ with ResumedTracing():
3101
+ get_type_hints(typ)
3102
+
3103
+
3104
+ def test_issubclass_abc():
3105
+ with standalone_statespace as space:
3106
+ with NoTracing():
3107
+ dict_subtype = SymbolicType("dict_subtype", Type[dict])
3108
+ issub = issubclass(dict_subtype, collections.abc.Mapping)
3109
+ with NoTracing():
3110
+ # `issub` is lazily determined:
3111
+ assert type(issub) is SymbolicBool
3112
+ assert space.is_possible(issub.var)
3113
+ assert space.is_possible(z3.Not(issub.var))
3114
+ # We can artificially assert that this dict type is somehow not a Mapping:
3115
+ space.add(z3.Not(issub.var))
3116
+ # And CrossHair will give up when it comes time to find some such a type:
3117
+ with pytest.raises(IgnoreAttempt):
3118
+ realize(dict_subtype)
3119
+
3120
+
3121
+ def test_object_with_comparison():
3122
+ def f(obj):
3123
+ """post: _"""
3124
+ return obj != b"abc"
3125
+
3126
+ check_states(f, POST_FAIL)
3127
+
3128
+
3129
+ def test_object_addition():
3130
+ def f(a, b):
3131
+ """post: True"""
3132
+ return a + b
3133
+
3134
+ actual, expected = check_messages(
3135
+ analyze_function(f),
3136
+ state=MessageType.EXEC_ERR,
3137
+ # We check the message here, because we used to produce an
3138
+ # (incorrect) counterexample of f('', '')
3139
+ # See https://github.com/pschanely/CrossHair/issues/235
3140
+ message="TypeError: when calling f('', 0)",
3141
+ )
3142
+ assert actual == expected
3143
+
3144
+
3145
+ def test_invoke_integer():
3146
+ # https://github.com/pschanely/CrossHair/issues/236
3147
+ def f(a: int):
3148
+ """post: True"""
3149
+ return a(1) # type: ignore
3150
+
3151
+ check_states(f, EXEC_ERR)
3152
+
3153
+
3154
+ def test_callable_zero_args() -> None:
3155
+ def f(size: int, initializer: Callable[[], int]) -> Tuple[int, ...]:
3156
+ """
3157
+ pre: size >= 1
3158
+ post: _[0] != 707
3159
+ """
3160
+ return tuple(initializer() for _ in range(size))
3161
+
3162
+ check_states(f, POST_FAIL)
3163
+
3164
+
3165
+ @pytest.mark.smoke
3166
+ def test_callable_one_arg() -> None:
3167
+ def f(size: int, mapfn: Callable[[int], int]) -> Tuple[int, ...]:
3168
+ """
3169
+ pre: size >= 1
3170
+ post: _[0] != 707
3171
+ """
3172
+ return tuple(mapfn(i) for i in range(size))
3173
+
3174
+ check_states(f, POST_FAIL)
3175
+
3176
+
3177
+ def test_callable_two_args() -> None:
3178
+ def f(i: int, c: Callable[[int, int], int]) -> int:
3179
+ """post: _ != i"""
3180
+ return c(i, i)
3181
+
3182
+ check_states(f, POST_FAIL)
3183
+
3184
+
3185
+ def test_callable_as_bool() -> None:
3186
+ def f(fn: Callable[[int], int]) -> bool:
3187
+ """post: _"""
3188
+ return bool(fn)
3189
+
3190
+ check_states(f, CONFIRMED)
3191
+
3192
+
3193
+ def test_callable_can_return_different_values(space) -> None:
3194
+ fn = proxy_for_type(Callable[[], int], "fn")
3195
+ with ResumedTracing():
3196
+ first_return = fn()
3197
+ second_return = fn()
3198
+ returns_are_equal = first_return == second_return
3199
+ returns_are_not_equal = first_return != second_return
3200
+ assert space.is_possible(returns_are_equal)
3201
+ assert space.is_possible(returns_are_not_equal)
3202
+
3203
+
3204
+ @pytest.mark.smoke
3205
+ def test_callable_repr() -> None:
3206
+ def f(f1: Callable[[int], int]) -> int:
3207
+ """post: _ != 1234"""
3208
+ return f1(4)
3209
+
3210
+ messages = run_checkables(analyze_function(f))
3211
+ assert len(messages) == 1
3212
+ assert re.compile(
3213
+ r"false when calling f\(.*lambda.*\(which returns 1234\)"
3214
+ ).fullmatch(messages[0].message)
3215
+
3216
+
3217
+ def test_callable_with_typevar_in_args() -> None:
3218
+ T = TypeVar("T")
3219
+
3220
+ def f(a: Callable[[T], int], x: T) -> int:
3221
+ """post: _ != 42"""
3222
+ return a(x)
3223
+
3224
+ check_states(f, POST_FAIL)
3225
+
3226
+
3227
+ def test_callable_with_typevar_in_return() -> None:
3228
+ T = TypeVar("T")
3229
+
3230
+ def f(a: Callable[[int], T], x: int) -> T:
3231
+ """post: _"""
3232
+ return a(x)
3233
+
3234
+ check_states(f, POST_FAIL)
3235
+
3236
+
3237
+ def TODO_test_callable_with_typevars() -> None:
3238
+ # Right now, this incorrectly reports a counterexample like:
3239
+ # a=`lambda x : 42` and k=''
3240
+ # (the type vars preclude such a counterexample)
3241
+ # Note also a related issue: https://github.com/pschanely/CrossHair/issues/85
3242
+ T = TypeVar("T")
3243
+
3244
+ def f(a: Callable[[T], T], k: T) -> T:
3245
+ """post: _ != 42"""
3246
+ if isinstance(k, int):
3247
+ return 0 # type: ignore
3248
+ return a(k)
3249
+
3250
+ check_states(f, CANNOT_CONFIRM) # or maybe CONFIRMED?
3251
+
3252
+
3253
+ def test_all():
3254
+ class ExplodingBool(BaseException):
3255
+ def __bool__(self):
3256
+ raise self
3257
+
3258
+ with standalone_statespace as space:
3259
+ true_bool = proxy_for_type(bool, "true_bool")
3260
+ space.add(true_bool)
3261
+ nonempty_list = proxy_for_type(List[object], "nonempty_list")
3262
+ space.add(len(nonempty_list) == 1)
3263
+ arbitrary_bool = proxy_for_type(bool, "arbitrary_bool")
3264
+ boom = ExplodingBool()
3265
+
3266
+ assert all([true_bool, True, nonempty_list, 42]) is True
3267
+
3268
+ sym_false = all([True, true_bool, nonempty_list, arbitrary_bool])
3269
+ with NoTracing():
3270
+ assert isinstance(sym_false, SymbolicBool)
3271
+ assert space.is_possible(z3.Not(sym_false.var))
3272
+ assert space.is_possible(sym_false)
3273
+
3274
+ with pytest.raises(ExplodingBool):
3275
+ all([true_bool, True, nonempty_list, boom])
3276
+
3277
+ assert all([true_bool, nonempty_list, False, boom]) is False
3278
+
3279
+
3280
+ def test_any():
3281
+ class ExplodingBool(BaseException):
3282
+ def __bool__(self):
3283
+ raise self
3284
+
3285
+ with standalone_statespace as space:
3286
+ false_bool = proxy_for_type(bool, "false_bool")
3287
+ space.add(z3.Not(false_bool.var))
3288
+ empty_list = proxy_for_type(List[object], "empty_list")
3289
+ space.add(len(empty_list) == 0)
3290
+ arbitrary_bool = proxy_for_type(bool, "arbitrary_bool")
3291
+ # arbitrary_list = proxy_for_type(List[object], "arbitrary_list")
3292
+ # TODO: in theory, we should be able to keep `bool(arbitrary_list)`
3293
+ # symbolic. However, not enough of the system is resilient to symbolic
3294
+ # booleans. (at the very least, the UNARY_NOT opcode)
3295
+ boom = ExplodingBool()
3296
+
3297
+ assert any([false_bool, False, empty_list, 0]) is False
3298
+
3299
+ sym_false = any([False, false_bool, empty_list, arbitrary_bool])
3300
+ with NoTracing():
3301
+ assert isinstance(sym_false, SymbolicBool)
3302
+ assert space.is_possible(z3.Not(sym_false.var))
3303
+ assert space.is_possible(sym_false)
3304
+
3305
+ with pytest.raises(ExplodingBool):
3306
+ any([false_bool, False, empty_list, boom])
3307
+
3308
+ assert any([false_bool, empty_list, True, boom]) is True
3309
+
3310
+
3311
+ def test_hash() -> None:
3312
+ def f(s: int) -> int:
3313
+ """post: True"""
3314
+ return hash(s)
3315
+
3316
+ check_states(f, CONFIRMED)
3317
+
3318
+
3319
+ @pytest.mark.demo
3320
+ def test_getattr() -> None:
3321
+ def f(s: str) -> int:
3322
+ """
3323
+ Can this function return 42?
3324
+
3325
+ post: _ != 42
3326
+ """
3327
+
3328
+ class Otter:
3329
+ def do_things(self) -> int:
3330
+ return 42
3331
+
3332
+ try:
3333
+ return getattr(Otter(), s)()
3334
+ except Exception:
3335
+ return 0
3336
+
3337
+ check_states(f, POST_FAIL)
3338
+
3339
+
3340
+ def TODO_test_print_ok() -> None:
3341
+ def f(x: int) -> bool:
3342
+ """
3343
+ post: _ == True
3344
+ """
3345
+ print(x)
3346
+ return True
3347
+
3348
+ check_states(f, CONFIRMED)
3349
+
3350
+
3351
+ def test_repr_ok():
3352
+ def f(x: int) -> str:
3353
+ """post: len(_) == 0 or len(_) > 0"""
3354
+ return repr(x)
3355
+
3356
+ check_states(f, CONFIRMED)
3357
+
3358
+
3359
+ @pytest.mark.demo
3360
+ def test_map() -> None:
3361
+ def f(ls: List[int]) -> List[int]:
3362
+ """
3363
+ Can an incremented list equal [4, 9, 0]?
3364
+
3365
+ post: _ != [4, 9, 0]
3366
+ """
3367
+ return list(map(lambda x: x + 1, ls))
3368
+
3369
+ check_states(f, POST_FAIL)
3370
+
3371
+
3372
+ def test_max_fail() -> None:
3373
+ def f(ls: List[int]) -> int:
3374
+ """
3375
+ post: _ in ls
3376
+ """
3377
+ return max(ls)
3378
+
3379
+ check_states(f, EXEC_ERR)
3380
+
3381
+
3382
+ def test_max_ok() -> None:
3383
+ def f(ls: List[int]) -> int:
3384
+ """
3385
+ pre: bool(ls)
3386
+ post[]: _ in ls
3387
+ """
3388
+ return max(ls)
3389
+
3390
+ check_states(f, CANNOT_CONFIRM)
3391
+
3392
+
3393
+ def test_min_ok() -> None:
3394
+ def f(ls: List[float]) -> float:
3395
+ """
3396
+ pre: bool(ls)
3397
+ post[]: _ in ls
3398
+ """
3399
+ return min(ls)
3400
+
3401
+ check_states(f, CANNOT_CONFIRM)
3402
+
3403
+
3404
+ def test_list_index_on_concrete() -> None:
3405
+ def f(i: int) -> int:
3406
+ """post: True"""
3407
+ return [0, 1, 2].index(i)
3408
+
3409
+ (actual, expected) = check_exec_err(f, "ValueError:")
3410
+ assert actual == expected
3411
+
3412
+
3413
+ def test_eval_namespaces() -> None:
3414
+ def f(i: int) -> int:
3415
+ """post: _ == i + 1"""
3416
+ return eval("i + Color.BLUE.value")
3417
+
3418
+ check_states(f, CONFIRMED)
3419
+
3420
+
3421
+ def test_bytes_specific_length() -> None:
3422
+ def f(b: bytes) -> int:
3423
+ """post: _ != 5"""
3424
+ return len(b)
3425
+
3426
+ check_states(f, POST_FAIL)
3427
+
3428
+
3429
+ def test_bytes_out_of_range_byte() -> None:
3430
+ def f(b: bytes) -> bytes:
3431
+ """
3432
+ pre: len(b) == 1
3433
+ post: _[0] != 256
3434
+ """
3435
+ return b
3436
+
3437
+ check_states(f, CONFIRMED)
3438
+
3439
+
3440
+ def test_bytes_roundtrip_array_as_symbolic():
3441
+ with standalone_statespace as space:
3442
+ orig_bytes = proxy_for_type(bytes, "origbytes")
3443
+ as_array = bytearray(orig_bytes)
3444
+ new_bytes = bytes(as_array)
3445
+ with NoTracing():
3446
+ assert type(as_array) is SymbolicByteArray
3447
+ assert type(new_bytes) is SymbolicBytes
3448
+ assert new_bytes.inner is orig_bytes.inner
3449
+
3450
+
3451
+ @pytest.mark.skipif(
3452
+ sys.version_info >= (3, 13),
3453
+ reason="Need to intercept UnicodeDecodeError.str in 3.13+",
3454
+ )
3455
+ @pytest.mark.demo
3456
+ def test_bytes_decode_method():
3457
+ def f(b: bytes) -> str:
3458
+ """
3459
+ Does any 2-byte sequence represent the character "ε" in UTF-8?
3460
+
3461
+ NOTE: The process of decoding involves a lot of branching;
3462
+ most problems will require minutes of processing or more.
3463
+
3464
+ pre: len(b) == 2
3465
+ post: _ != "ε"
3466
+ raises: UnicodeDecodeError
3467
+ """
3468
+ return b.decode("utf8")
3469
+
3470
+ check_states(f, POST_FAIL)
3471
+
3472
+
3473
+ @pytest.mark.demo("red")
3474
+ def test_bytes___str___method():
3475
+ def f(b: bytes):
3476
+ """
3477
+ Is the string form of any byte array equal to b''?
3478
+
3479
+ NOTE: This conversion does not have symbolic support (yet). We are able to find
3480
+ the enpty string, but nearly any other bytes string cannot be found.
3481
+
3482
+ post: _ != "b''"
3483
+ """
3484
+ return str(b)
3485
+
3486
+ check_states(f, POST_FAIL)
3487
+
3488
+
3489
+ @pytest.mark.demo
3490
+ def test_bytearray___add___method():
3491
+ def f(a: bytes, count: int) -> bytearray:
3492
+ """
3493
+ Can some repetitions of a 3-character byte string produce "abcabc"?
3494
+ pre: len(a) == 3
3495
+ post: _ != b"abcabc"
3496
+ """
3497
+ ba = bytearray()
3498
+ for _ in range(count):
3499
+ ba += a
3500
+ return ba
3501
+
3502
+ check_states(f, POST_FAIL)
3503
+
3504
+
3505
+ def test_extend_concrete_bytearray():
3506
+ with standalone_statespace as space:
3507
+ b = bytearray(b"abc")
3508
+ xyz = proxy_for_type(bytearray, "xyz")
3509
+ b.extend(xyz)
3510
+ assert not space.is_possible(b[0] != ord("a"))
3511
+ assert space.is_possible(len(b) > 3)
3512
+
3513
+
3514
+ def test_bytearray_slice():
3515
+ with standalone_statespace as space:
3516
+ xyz = proxy_for_type(bytearray, "xyz")
3517
+ space.add(xyz.__len__() == 3)
3518
+ assert type(xyz[1:]) is bytearray
3519
+
3520
+
3521
+ def test_memoryview_compare():
3522
+ with standalone_statespace as space:
3523
+ mv1 = proxy_for_type(memoryview, "mv1")
3524
+ mv2 = proxy_for_type(memoryview, "mv2")
3525
+ len1, len2 = len(mv1), len(mv2)
3526
+ space.add(len1 == 0)
3527
+ space.add(len2 == 0)
3528
+ views_equal = mv1 == mv2
3529
+ with NoTracing():
3530
+ assert views_equal is True
3531
+
3532
+
3533
+ def test_memoryview_cast():
3534
+ """post: _"""
3535
+ with standalone_statespace as space:
3536
+ val = proxy_for_type(int, "val")
3537
+ space.add(val == 254)
3538
+ mv = memoryview(bytearray([val]))
3539
+ assert mv.cast("b")[0] == -2
3540
+
3541
+
3542
+ def test_memoryview_toreadonly():
3543
+ """post: _"""
3544
+ with standalone_statespace as space:
3545
+ mv = proxy_for_type(memoryview, "mv")
3546
+ space.add(mv.__len__() == 1)
3547
+ mv2 = mv.toreadonly()
3548
+ mv[0] = 12
3549
+ assert mv2[0] == 12
3550
+ with pytest.raises(TypeError):
3551
+ mv2[0] = 24
3552
+
3553
+
3554
+ def test_memoryview_properties():
3555
+ """post: _"""
3556
+ with standalone_statespace as space:
3557
+ symbolic_mv = proxy_for_type(memoryview, "symbolic_mv")
3558
+ space.add(symbolic_mv.__len__() == 1)
3559
+ concrete_mv = memoryview(bytearray(b"a"))
3560
+ assert symbolic_mv.contiguous == concrete_mv.contiguous
3561
+ assert symbolic_mv.c_contiguous == concrete_mv.c_contiguous
3562
+ assert symbolic_mv.f_contiguous == concrete_mv.f_contiguous
3563
+ assert symbolic_mv.readonly == concrete_mv.readonly
3564
+ assert symbolic_mv.format == concrete_mv.format
3565
+ assert symbolic_mv.itemsize == concrete_mv.itemsize
3566
+ assert symbolic_mv.nbytes == concrete_mv.nbytes
3567
+ assert symbolic_mv.ndim == concrete_mv.ndim
3568
+ assert symbolic_mv.shape == concrete_mv.shape
3569
+ assert symbolic_mv.strides == concrete_mv.strides
3570
+ assert symbolic_mv.suboffsets == concrete_mv.suboffsets
3571
+
3572
+
3573
+ def test_chr(space):
3574
+ i = proxy_for_type(int, "i")
3575
+ with ResumedTracing():
3576
+ space.add(10 <= i)
3577
+ space.add(i < 256)
3578
+ c = chr(i)
3579
+ assert space.is_possible(c._codepoints[0] == ord("a"))
3580
+ assert not space.is_possible(c._codepoints[0] == 0)
3581
+
3582
+
3583
+ def test_ord(space):
3584
+ s = proxy_for_type(str, "s")
3585
+ with ResumedTracing():
3586
+ space.add(len(s) == 1)
3587
+ i = ord(s)
3588
+ assert space.is_possible(i == 42)
3589
+ assert not space.is_possible(i > sys.maxunicode)
3590
+
3591
+
3592
+ def test_unicode_support(space):
3593
+ s = proxy_for_type(str, "s")
3594
+ with ResumedTracing():
3595
+ space.add(len(s) == 1)
3596
+ assert space.is_possible(s == "a")
3597
+ assert space.is_possible(s == "\u1234")
3598
+
3599
+
3600
+ @pytest.mark.parametrize("concrete_x", (25, 15, 6, -4, -15, -25))
3601
+ def test_int_round(concrete_x, space):
3602
+ concrete_ret = round(concrete_x, -1)
3603
+ x = proxy_for_type(int, "x")
3604
+ d = proxy_for_type(int, "d")
3605
+ with ResumedTracing():
3606
+ space.add(x == concrete_x)
3607
+ space.add(d == -1)
3608
+ assert not space.is_possible((round(x, d) != concrete_ret))
3609
+
3610
+
3611
+ class ExplodingValue:
3612
+ def __getattribute__(self, name):
3613
+ raise CrossHairInternal
3614
+
3615
+
3616
+ @dataclasses.dataclass
3617
+ class ClassWithSpecialMemberNames:
3618
+ typ: str
3619
+ var: int
3620
+
3621
+
3622
+ def test_class_with_special_member_names():
3623
+ exploding_value = ExplodingValue()
3624
+
3625
+ def f(obj1: ClassWithSpecialMemberNames):
3626
+ """post: True"""
3627
+ assert isinstance(obj1.typ, str)
3628
+ assert isinstance(obj1.var, int)
3629
+ # Ensure run-time creation doesn't explode either:
3630
+ ClassWithSpecialMemberNames(typ=exploding_value, var=exploding_value) # type: ignore
3631
+
3632
+ check_states(f, CONFIRMED)
3633
+
3634
+
3635
+ @pytest.mark.parametrize("type2", (list, set, frozenset, tuple))
3636
+ @pytest.mark.parametrize(
3637
+ "type1",
3638
+ (
3639
+ list,
3640
+ set,
3641
+ frozenset,
3642
+ tuple,
3643
+ array,
3644
+ ),
3645
+ )
3646
+ def test_container_crosstype_equality(space, type1, type2):
3647
+ sym_obj1 = proxy_for_type(type1, "obj1")
3648
+ obj2 = type2()
3649
+ with ResumedTracing():
3650
+ space.add(len(sym_obj1) == 0)
3651
+ obj1 = deep_realize(sym_obj1)
3652
+ sym_eq_on_left = realize(sym_obj1 == obj2)
3653
+ sym_eq_on_right = realize(obj2 == sym_obj1)
3654
+ actually_equal = obj1 == obj2
3655
+ assert actually_equal == sym_eq_on_left
3656
+ assert actually_equal == sym_eq_on_right
3657
+
3658
+
3659
+ @pytest.mark.parametrize("type2", (str, bytes, tuple))
3660
+ @pytest.mark.parametrize("type1", (str, bytes, tuple, memoryview))
3661
+ def test_stringlike_crosstype_equality(space, type1, type2):
3662
+ sym_obj1 = proxy_for_type(type1, "obj1")
3663
+ obj2 = type2()
3664
+ with ResumedTracing():
3665
+ space.add(len(sym_obj1) == 0)
3666
+ obj1 = deep_realize(sym_obj1)
3667
+ sym_eq_on_left = realize(sym_obj1 == obj2)
3668
+ sym_eq_on_right = realize(obj2 == sym_obj1)
3669
+ actually_equal = obj1 == obj2
3670
+ assert actually_equal == sym_eq_on_left
3671
+ assert actually_equal == sym_eq_on_right
3672
+
3673
+
3674
+ @pytest.mark.parametrize("typ", (List[int],))
3675
+ def test_deep_realization(space, typ):
3676
+ symbolic = proxy_for_type(typ, "symbolic")
3677
+ with ResumedTracing():
3678
+ space.add(len(symbolic) == 3)
3679
+ origin = origin_of(typ)
3680
+ assert type(symbolic) != origin
3681
+ concrete = deep_realize(symbolic)
3682
+ assert type(concrete) == origin
3683
+ with ResumedTracing():
3684
+ assert concrete == symbolic
3685
+
3686
+
3687
+ def test_float_round_to_zero(space):
3688
+ space.extra(ModelingDirector).global_representations[
3689
+ float
3690
+ ] = PreciseIeeeSymbolicFloat
3691
+ n = proxy_for_type(float, "n")
3692
+ d = proxy_for_type(float, "d")
3693
+ with ResumedTracing():
3694
+ space.add(d != 0.0)
3695
+ # This is possible for floats, but not reals:
3696
+ assert space.is_possible(n / d != 0.0)
3697
+ assert space.is_possible(n / d == 0.0)
3698
+
3699
+
3700
+ def test_float_neg_zero_is_falsey(space):
3701
+ space.extra(ModelingDirector).global_representations[
3702
+ float
3703
+ ] = PreciseIeeeSymbolicFloat
3704
+ x = proxy_for_type(float, "x")
3705
+ with ResumedTracing():
3706
+ space.add(x == -10.0)
3707
+ negzero = x / math.inf
3708
+ assert not space.is_possible(negzero != 0.0)
3709
+ assert bool(negzero) is False
3710
+
3711
+
3712
+ def TODO_test_int_mod_float():
3713
+ # (errors at the Z3 level presently)
3714
+ with standalone_statespace as space:
3715
+ x = proxy_for_type(int, "x")
3716
+ y = proxy_for_type(float, "y")
3717
+ modval = x % y
3718
+ with NoTracing():
3719
+ assert type(modval) == RealBasedSymbolicFloat
3720
+ assert space.is_possible(modval == 12.12)
3721
+
3722
+
3723
+ def TODO_test_can_generate_integral():
3724
+ with standalone_statespace as space:
3725
+ proxy_for_type(Integral, "x")
3726
+
3727
+
3728
+ def TODO_test_deepcopy_independence():
3729
+ # (snapshotting should take care of this for us(?), but it doesn't seem to)
3730
+ with standalone_statespace as space:
3731
+ ls = proxy_for_type(List[List[int]], "ls")
3732
+ lscopy = copy.deepcopy(ls)
3733
+ with NoTracing():
3734
+ assert ls[0] is not lscopy[0]
3735
+ # Next try mutation on one and test the other...