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.
- _crosshair_tracers.cpython-312-darwin.so +0 -0
- crosshair/__init__.py +42 -0
- crosshair/__main__.py +8 -0
- crosshair/_mark_stacks.h +790 -0
- crosshair/_preliminaries_test.py +18 -0
- crosshair/_tracers.h +94 -0
- crosshair/_tracers_pycompat.h +522 -0
- crosshair/_tracers_test.py +138 -0
- crosshair/abcstring.py +245 -0
- crosshair/auditwall.py +190 -0
- crosshair/auditwall_test.py +77 -0
- crosshair/codeconfig.py +113 -0
- crosshair/codeconfig_test.py +117 -0
- crosshair/condition_parser.py +1237 -0
- crosshair/condition_parser_test.py +497 -0
- crosshair/conftest.py +30 -0
- crosshair/copyext.py +155 -0
- crosshair/copyext_test.py +84 -0
- crosshair/core.py +1763 -0
- crosshair/core_and_libs.py +149 -0
- crosshair/core_regestered_types_test.py +82 -0
- crosshair/core_test.py +1316 -0
- crosshair/diff_behavior.py +314 -0
- crosshair/diff_behavior_test.py +261 -0
- crosshair/dynamic_typing.py +346 -0
- crosshair/dynamic_typing_test.py +210 -0
- crosshair/enforce.py +282 -0
- crosshair/enforce_test.py +182 -0
- crosshair/examples/PEP316/__init__.py +1 -0
- crosshair/examples/PEP316/bugs_detected/__init__.py +0 -0
- crosshair/examples/PEP316/bugs_detected/getattr_magic.py +16 -0
- crosshair/examples/PEP316/bugs_detected/hash_consistent_with_equals.py +31 -0
- crosshair/examples/PEP316/bugs_detected/shopping_cart.py +24 -0
- crosshair/examples/PEP316/bugs_detected/showcase.py +39 -0
- crosshair/examples/PEP316/correct_code/__init__.py +0 -0
- crosshair/examples/PEP316/correct_code/arith.py +60 -0
- crosshair/examples/PEP316/correct_code/chess.py +77 -0
- crosshair/examples/PEP316/correct_code/nesting_inference.py +17 -0
- crosshair/examples/PEP316/correct_code/numpy_examples.py +132 -0
- crosshair/examples/PEP316/correct_code/rolling_average.py +35 -0
- crosshair/examples/PEP316/correct_code/showcase.py +104 -0
- crosshair/examples/__init__.py +0 -0
- crosshair/examples/check_examples_test.py +146 -0
- crosshair/examples/deal/__init__.py +1 -0
- crosshair/examples/icontract/__init__.py +1 -0
- crosshair/examples/icontract/bugs_detected/__init__.py +0 -0
- crosshair/examples/icontract/bugs_detected/showcase.py +41 -0
- crosshair/examples/icontract/bugs_detected/wrong_sign.py +8 -0
- crosshair/examples/icontract/correct_code/__init__.py +0 -0
- crosshair/examples/icontract/correct_code/arith.py +51 -0
- crosshair/examples/icontract/correct_code/showcase.py +94 -0
- crosshair/fnutil.py +391 -0
- crosshair/fnutil_test.py +75 -0
- crosshair/fuzz_core_test.py +516 -0
- crosshair/libimpl/__init__.py +0 -0
- crosshair/libimpl/arraylib.py +161 -0
- crosshair/libimpl/binascii_ch_test.py +30 -0
- crosshair/libimpl/binascii_test.py +67 -0
- crosshair/libimpl/binasciilib.py +150 -0
- crosshair/libimpl/bisectlib_test.py +23 -0
- crosshair/libimpl/builtinslib.py +5228 -0
- crosshair/libimpl/builtinslib_ch_test.py +1191 -0
- crosshair/libimpl/builtinslib_test.py +3735 -0
- crosshair/libimpl/codecslib.py +86 -0
- crosshair/libimpl/codecslib_test.py +86 -0
- crosshair/libimpl/collectionslib.py +264 -0
- crosshair/libimpl/collectionslib_ch_test.py +252 -0
- crosshair/libimpl/collectionslib_test.py +332 -0
- crosshair/libimpl/copylib.py +23 -0
- crosshair/libimpl/copylib_test.py +18 -0
- crosshair/libimpl/datetimelib.py +2559 -0
- crosshair/libimpl/datetimelib_ch_test.py +354 -0
- crosshair/libimpl/datetimelib_test.py +112 -0
- crosshair/libimpl/decimallib.py +5257 -0
- crosshair/libimpl/decimallib_ch_test.py +78 -0
- crosshair/libimpl/decimallib_test.py +76 -0
- crosshair/libimpl/encodings/__init__.py +23 -0
- crosshair/libimpl/encodings/_encutil.py +187 -0
- crosshair/libimpl/encodings/ascii.py +44 -0
- crosshair/libimpl/encodings/latin_1.py +40 -0
- crosshair/libimpl/encodings/utf_8.py +93 -0
- crosshair/libimpl/encodings_ch_test.py +83 -0
- crosshair/libimpl/fractionlib.py +16 -0
- crosshair/libimpl/fractionlib_test.py +80 -0
- crosshair/libimpl/functoolslib.py +34 -0
- crosshair/libimpl/functoolslib_test.py +56 -0
- crosshair/libimpl/hashliblib.py +30 -0
- crosshair/libimpl/hashliblib_test.py +18 -0
- crosshair/libimpl/heapqlib.py +47 -0
- crosshair/libimpl/heapqlib_test.py +21 -0
- crosshair/libimpl/importliblib.py +18 -0
- crosshair/libimpl/importliblib_test.py +38 -0
- crosshair/libimpl/iolib.py +216 -0
- crosshair/libimpl/iolib_ch_test.py +128 -0
- crosshair/libimpl/iolib_test.py +19 -0
- crosshair/libimpl/ipaddresslib.py +8 -0
- crosshair/libimpl/itertoolslib.py +44 -0
- crosshair/libimpl/itertoolslib_test.py +44 -0
- crosshair/libimpl/jsonlib.py +984 -0
- crosshair/libimpl/jsonlib_ch_test.py +42 -0
- crosshair/libimpl/jsonlib_test.py +51 -0
- crosshair/libimpl/mathlib.py +179 -0
- crosshair/libimpl/mathlib_ch_test.py +44 -0
- crosshair/libimpl/mathlib_test.py +67 -0
- crosshair/libimpl/oslib.py +7 -0
- crosshair/libimpl/pathliblib_test.py +10 -0
- crosshair/libimpl/randomlib.py +178 -0
- crosshair/libimpl/randomlib_test.py +120 -0
- crosshair/libimpl/relib.py +846 -0
- crosshair/libimpl/relib_ch_test.py +169 -0
- crosshair/libimpl/relib_test.py +493 -0
- crosshair/libimpl/timelib.py +72 -0
- crosshair/libimpl/timelib_test.py +82 -0
- crosshair/libimpl/typeslib.py +15 -0
- crosshair/libimpl/typeslib_test.py +36 -0
- crosshair/libimpl/unicodedatalib.py +75 -0
- crosshair/libimpl/unicodedatalib_test.py +42 -0
- crosshair/libimpl/urlliblib.py +23 -0
- crosshair/libimpl/urlliblib_test.py +19 -0
- crosshair/libimpl/weakreflib.py +13 -0
- crosshair/libimpl/weakreflib_test.py +69 -0
- crosshair/libimpl/zliblib.py +15 -0
- crosshair/libimpl/zliblib_test.py +13 -0
- crosshair/lsp_server.py +261 -0
- crosshair/lsp_server_test.py +30 -0
- crosshair/main.py +973 -0
- crosshair/main_test.py +543 -0
- crosshair/objectproxy.py +376 -0
- crosshair/objectproxy_test.py +41 -0
- crosshair/opcode_intercept.py +601 -0
- crosshair/opcode_intercept_test.py +304 -0
- crosshair/options.py +218 -0
- crosshair/options_test.py +10 -0
- crosshair/patch_equivalence_test.py +75 -0
- crosshair/path_cover.py +209 -0
- crosshair/path_cover_test.py +138 -0
- crosshair/path_search.py +161 -0
- crosshair/path_search_test.py +52 -0
- crosshair/pathing_oracle.py +271 -0
- crosshair/pathing_oracle_test.py +21 -0
- crosshair/pure_importer.py +27 -0
- crosshair/pure_importer_test.py +16 -0
- crosshair/py.typed +0 -0
- crosshair/register_contract.py +273 -0
- crosshair/register_contract_test.py +190 -0
- crosshair/simplestructs.py +1165 -0
- crosshair/simplestructs_test.py +283 -0
- crosshair/smtlib.py +24 -0
- crosshair/smtlib_test.py +14 -0
- crosshair/statespace.py +1199 -0
- crosshair/statespace_test.py +108 -0
- crosshair/stubs_parser.py +352 -0
- crosshair/stubs_parser_test.py +43 -0
- crosshair/test_util.py +329 -0
- crosshair/test_util_test.py +26 -0
- crosshair/tools/__init__.py +0 -0
- crosshair/tools/check_help_in_doc.py +264 -0
- crosshair/tools/check_init_and_setup_coincide.py +119 -0
- crosshair/tools/generate_demo_table.py +127 -0
- crosshair/tracers.py +544 -0
- crosshair/tracers_test.py +154 -0
- crosshair/type_repo.py +151 -0
- crosshair/unicode_categories.py +589 -0
- crosshair/unicode_categories_test.py +27 -0
- crosshair/util.py +741 -0
- crosshair/util_test.py +173 -0
- crosshair/watcher.py +307 -0
- crosshair/watcher_test.py +107 -0
- crosshair/z3util.py +76 -0
- crosshair/z3util_test.py +11 -0
- crosshair_tool-0.0.99.dist-info/METADATA +144 -0
- crosshair_tool-0.0.99.dist-info/RECORD +176 -0
- crosshair_tool-0.0.99.dist-info/WHEEL +6 -0
- crosshair_tool-0.0.99.dist-info/entry_points.txt +3 -0
- crosshair_tool-0.0.99.dist-info/licenses/LICENSE +93 -0
- 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...
|