crosshair-tool 0.0.56__cp39-cp39-macosx_11_0_arm64.whl → 0.0.100__cp39-cp39-macosx_11_0_arm64.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-39-darwin.so +0 -0
- crosshair/__init__.py +1 -1
- crosshair/_mark_stacks.h +51 -24
- crosshair/_tracers.h +9 -5
- crosshair/_tracers_test.py +19 -9
- crosshair/auditwall.py +9 -8
- crosshair/auditwall_test.py +31 -19
- crosshair/codeconfig.py +3 -2
- crosshair/condition_parser.py +17 -133
- crosshair/condition_parser_test.py +54 -96
- crosshair/conftest.py +1 -1
- crosshair/copyext.py +91 -22
- crosshair/copyext_test.py +33 -0
- crosshair/core.py +259 -203
- crosshair/core_and_libs.py +20 -0
- crosshair/core_regestered_types_test.py +82 -0
- crosshair/core_test.py +693 -664
- crosshair/diff_behavior.py +76 -21
- crosshair/diff_behavior_test.py +132 -23
- crosshair/dynamic_typing.py +128 -18
- crosshair/dynamic_typing_test.py +91 -4
- crosshair/enforce.py +1 -6
- crosshair/enforce_test.py +15 -23
- crosshair/examples/check_examples_test.py +2 -1
- crosshair/fnutil.py +2 -3
- crosshair/fnutil_test.py +0 -7
- crosshair/fuzz_core_test.py +70 -83
- crosshair/libimpl/arraylib.py +10 -7
- 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 +5 -5
- crosshair/libimpl/builtinslib.py +1002 -682
- crosshair/libimpl/builtinslib_ch_test.py +108 -30
- crosshair/libimpl/builtinslib_test.py +431 -143
- crosshair/libimpl/codecslib.py +22 -2
- crosshair/libimpl/codecslib_test.py +41 -9
- crosshair/libimpl/collectionslib.py +44 -8
- crosshair/libimpl/collectionslib_test.py +108 -20
- crosshair/libimpl/copylib.py +1 -1
- crosshair/libimpl/copylib_test.py +18 -0
- crosshair/libimpl/datetimelib.py +84 -67
- crosshair/libimpl/datetimelib_ch_test.py +12 -7
- crosshair/libimpl/datetimelib_test.py +5 -6
- crosshair/libimpl/decimallib.py +5257 -0
- crosshair/libimpl/decimallib_ch_test.py +78 -0
- crosshair/libimpl/decimallib_test.py +76 -0
- crosshair/libimpl/encodings/_encutil.py +21 -11
- crosshair/libimpl/fractionlib.py +16 -0
- crosshair/libimpl/fractionlib_test.py +80 -0
- crosshair/libimpl/functoolslib.py +19 -7
- crosshair/libimpl/functoolslib_test.py +22 -6
- crosshair/libimpl/hashliblib.py +30 -0
- crosshair/libimpl/hashliblib_test.py +18 -0
- crosshair/libimpl/heapqlib.py +32 -5
- crosshair/libimpl/heapqlib_test.py +15 -12
- crosshair/libimpl/iolib.py +7 -4
- crosshair/libimpl/ipaddresslib.py +8 -0
- crosshair/libimpl/itertoolslib_test.py +1 -1
- crosshair/libimpl/mathlib.py +165 -2
- crosshair/libimpl/mathlib_ch_test.py +44 -0
- crosshair/libimpl/mathlib_test.py +59 -16
- crosshair/libimpl/oslib.py +7 -0
- crosshair/libimpl/pathliblib_test.py +10 -0
- crosshair/libimpl/randomlib.py +1 -0
- crosshair/libimpl/randomlib_test.py +6 -4
- crosshair/libimpl/relib.py +180 -59
- crosshair/libimpl/relib_ch_test.py +26 -2
- crosshair/libimpl/relib_test.py +77 -14
- crosshair/libimpl/timelib.py +35 -13
- crosshair/libimpl/timelib_test.py +13 -3
- crosshair/libimpl/typeslib.py +15 -0
- crosshair/libimpl/typeslib_test.py +36 -0
- crosshair/libimpl/unicodedatalib_test.py +3 -3
- 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 +21 -10
- crosshair/main.py +48 -28
- crosshair/main_test.py +59 -14
- crosshair/objectproxy.py +39 -14
- crosshair/objectproxy_test.py +27 -13
- crosshair/opcode_intercept.py +212 -24
- crosshair/opcode_intercept_test.py +172 -18
- crosshair/options.py +0 -1
- crosshair/patch_equivalence_test.py +5 -21
- crosshair/path_cover.py +7 -5
- crosshair/path_search.py +6 -4
- crosshair/path_search_test.py +1 -2
- crosshair/pathing_oracle.py +53 -10
- crosshair/pathing_oracle_test.py +21 -0
- crosshair/pure_importer_test.py +5 -21
- crosshair/register_contract.py +16 -6
- crosshair/register_contract_test.py +2 -14
- crosshair/simplestructs.py +154 -85
- crosshair/simplestructs_test.py +16 -2
- crosshair/smtlib.py +24 -0
- crosshair/smtlib_test.py +14 -0
- crosshair/statespace.py +319 -196
- crosshair/statespace_test.py +45 -0
- crosshair/stubs_parser.py +0 -2
- crosshair/test_util.py +87 -25
- crosshair/test_util_test.py +26 -0
- crosshair/tools/check_init_and_setup_coincide.py +0 -3
- crosshair/tools/generate_demo_table.py +2 -2
- crosshair/tracers.py +141 -49
- crosshair/type_repo.py +11 -4
- crosshair/unicode_categories.py +1 -0
- crosshair/util.py +158 -76
- crosshair/util_test.py +13 -20
- crosshair/watcher.py +4 -4
- crosshair/z3util.py +1 -1
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/METADATA +45 -36
- crosshair_tool-0.0.100.dist-info/RECORD +176 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/WHEEL +2 -1
- crosshair/examples/hypothesis/__init__.py +0 -2
- crosshair/examples/hypothesis/bugs_detected/simple_strategies.py +0 -74
- crosshair_tool-0.0.56.dist-info/RECORD +0 -152
- /crosshair/{examples/hypothesis/bugs_detected/__init__.py → py.typed} +0 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/entry_points.txt +0 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info/licenses}/LICENSE +0 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/top_level.txt +0 -0
|
@@ -8,8 +8,8 @@ import math
|
|
|
8
8
|
import operator
|
|
9
9
|
import re
|
|
10
10
|
import sys
|
|
11
|
-
import unittest
|
|
12
11
|
from abc import ABC, abstractmethod
|
|
12
|
+
from array import array
|
|
13
13
|
from numbers import Integral
|
|
14
14
|
from typing import (
|
|
15
15
|
Callable,
|
|
@@ -32,14 +32,15 @@ from typing import (
|
|
|
32
32
|
Type,
|
|
33
33
|
TypeVar,
|
|
34
34
|
Union,
|
|
35
|
+
get_type_hints,
|
|
35
36
|
)
|
|
37
|
+
from unittest.mock import patch
|
|
36
38
|
|
|
37
39
|
import pytest
|
|
38
40
|
import z3 # type: ignore
|
|
39
41
|
|
|
40
42
|
from crosshair import type_repo
|
|
41
43
|
from crosshair.core import (
|
|
42
|
-
CrossHairValue,
|
|
43
44
|
analyze_function,
|
|
44
45
|
deep_realize,
|
|
45
46
|
proxy_for_type,
|
|
@@ -47,13 +48,16 @@ from crosshair.core import (
|
|
|
47
48
|
standalone_statespace,
|
|
48
49
|
)
|
|
49
50
|
from crosshair.core_and_libs import run_checkables
|
|
51
|
+
from crosshair.dynamic_typing import origin_of
|
|
50
52
|
from crosshair.libimpl.builtinslib import (
|
|
51
53
|
LazyIntSymbolicStr,
|
|
54
|
+
ModelingDirector,
|
|
55
|
+
PreciseIeeeSymbolicFloat,
|
|
56
|
+
RealBasedSymbolicFloat,
|
|
52
57
|
SymbolicArrayBasedUniformTuple,
|
|
53
58
|
SymbolicBool,
|
|
54
59
|
SymbolicByteArray,
|
|
55
60
|
SymbolicBytes,
|
|
56
|
-
SymbolicFloat,
|
|
57
61
|
SymbolicInt,
|
|
58
62
|
SymbolicList,
|
|
59
63
|
SymbolicObject,
|
|
@@ -66,9 +70,9 @@ from crosshair.statespace import (
|
|
|
66
70
|
CANNOT_CONFIRM,
|
|
67
71
|
CONFIRMED,
|
|
68
72
|
EXEC_ERR,
|
|
69
|
-
POST_ERR,
|
|
70
73
|
POST_FAIL,
|
|
71
74
|
MessageType,
|
|
75
|
+
StateSpace,
|
|
72
76
|
)
|
|
73
77
|
from crosshair.test_util import (
|
|
74
78
|
check_exec_err,
|
|
@@ -77,7 +81,12 @@ from crosshair.test_util import (
|
|
|
77
81
|
summarize_execution,
|
|
78
82
|
)
|
|
79
83
|
from crosshair.tracers import NoTracing, ResumedTracing
|
|
80
|
-
from crosshair.util import
|
|
84
|
+
from crosshair.util import (
|
|
85
|
+
CrossHairInternal,
|
|
86
|
+
CrossHairValue,
|
|
87
|
+
IgnoreAttempt,
|
|
88
|
+
UnknownSatisfiability,
|
|
89
|
+
)
|
|
81
90
|
|
|
82
91
|
|
|
83
92
|
class Cat:
|
|
@@ -140,14 +149,14 @@ NAN = float("nan")
|
|
|
140
149
|
|
|
141
150
|
|
|
142
151
|
def test_crosshair_types_for_python_type() -> None:
|
|
143
|
-
assert crosshair_types_for_python_type(int) == (SymbolicInt,)
|
|
152
|
+
assert crosshair_types_for_python_type(int) == ((SymbolicInt, 1.0),)
|
|
144
153
|
assert crosshair_types_for_python_type(SmokeDetector) == ()
|
|
145
154
|
|
|
146
155
|
|
|
147
156
|
def test_isinstance():
|
|
148
157
|
with standalone_statespace:
|
|
149
158
|
with NoTracing():
|
|
150
|
-
f =
|
|
159
|
+
f = RealBasedSymbolicFloat("f")
|
|
151
160
|
assert isinstance(f, float)
|
|
152
161
|
assert not isinstance(f, int)
|
|
153
162
|
|
|
@@ -155,7 +164,7 @@ def test_isinstance():
|
|
|
155
164
|
def test_smtfloat_like_a_float():
|
|
156
165
|
with standalone_statespace:
|
|
157
166
|
with NoTracing():
|
|
158
|
-
f1 =
|
|
167
|
+
f1 = RealBasedSymbolicFloat("f")
|
|
159
168
|
f2 = type(f1)(12)
|
|
160
169
|
with NoTracing():
|
|
161
170
|
assert isinstance(f2, float)
|
|
@@ -228,7 +237,7 @@ def test_number_simple_compare_ok() -> None:
|
|
|
228
237
|
check_states(f, CONFIRMED)
|
|
229
238
|
|
|
230
239
|
|
|
231
|
-
def
|
|
240
|
+
def test_number_promotion_compare_ok() -> None:
|
|
232
241
|
def f(i: int, f: float) -> bool:
|
|
233
242
|
"""
|
|
234
243
|
pre: i == 7
|
|
@@ -237,7 +246,7 @@ def test_number_promotion_compare_unknown() -> None:
|
|
|
237
246
|
"""
|
|
238
247
|
return i == f and f >= i and i >= f
|
|
239
248
|
|
|
240
|
-
check_states(f,
|
|
249
|
+
check_states(f, CONFIRMED)
|
|
241
250
|
|
|
242
251
|
|
|
243
252
|
def test_numeric_promotions() -> None:
|
|
@@ -258,7 +267,7 @@ def test_float_as_bool() -> None:
|
|
|
258
267
|
"""
|
|
259
268
|
return x or y
|
|
260
269
|
|
|
261
|
-
check_states(f,
|
|
270
|
+
check_states(f, CONFIRMED)
|
|
262
271
|
|
|
263
272
|
|
|
264
273
|
def test_int_reverse_operators() -> None:
|
|
@@ -272,6 +281,35 @@ def test_int_reverse_operators() -> None:
|
|
|
272
281
|
check_states(f, POST_FAIL)
|
|
273
282
|
|
|
274
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
|
+
|
|
275
313
|
@pytest.mark.demo
|
|
276
314
|
def test_int___add___method():
|
|
277
315
|
def f(a: int, b: int) -> int:
|
|
@@ -335,6 +373,29 @@ def test_int___pow___method():
|
|
|
335
373
|
check_states(f, POST_FAIL)
|
|
336
374
|
|
|
337
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
|
+
|
|
338
399
|
@pytest.mark.demo
|
|
339
400
|
def test_int___sub___method():
|
|
340
401
|
def f(a: int) -> int:
|
|
@@ -424,6 +485,7 @@ def test_int___truediv___method() -> None:
|
|
|
424
485
|
def test_trunc_fail() -> None:
|
|
425
486
|
def f(n: float) -> int:
|
|
426
487
|
"""
|
|
488
|
+
pre: math.isfinite(n)
|
|
427
489
|
pre: n > 100
|
|
428
490
|
post: _ < n
|
|
429
491
|
"""
|
|
@@ -459,11 +521,11 @@ def test_round_fail() -> None:
|
|
|
459
521
|
def test_round_unknown() -> None:
|
|
460
522
|
def f(num: float, ndigits: Optional[int]) -> float:
|
|
461
523
|
"""
|
|
524
|
+
pre: math.isfinite(num)
|
|
462
525
|
post: isinstance(_, int) == (ndigits is None)
|
|
463
526
|
"""
|
|
464
527
|
return round(num, ndigits)
|
|
465
528
|
|
|
466
|
-
# TODO: this is unknown (rounding reals is hard)
|
|
467
529
|
check_states(f, CANNOT_CONFIRM)
|
|
468
530
|
|
|
469
531
|
|
|
@@ -472,7 +534,7 @@ def test_float_isinstance() -> None:
|
|
|
472
534
|
"""post: isinstance(_, float)"""
|
|
473
535
|
return x
|
|
474
536
|
|
|
475
|
-
check_states(f,
|
|
537
|
+
check_states(f, CONFIRMED)
|
|
476
538
|
|
|
477
539
|
|
|
478
540
|
def test_mismatched_types() -> None:
|
|
@@ -509,15 +571,20 @@ def test_float_from_hex() -> None:
|
|
|
509
571
|
check_states(f, CONFIRMED)
|
|
510
572
|
|
|
511
573
|
|
|
512
|
-
def
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
return int.from_bytes(byt, byteorder="little")
|
|
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)
|
|
519
580
|
|
|
520
|
-
|
|
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)
|
|
521
588
|
|
|
522
589
|
|
|
523
590
|
def test_int_nonlinear() -> None:
|
|
@@ -566,19 +633,22 @@ def test_bool_ops(b, op):
|
|
|
566
633
|
with standalone_statespace as space:
|
|
567
634
|
with NoTracing():
|
|
568
635
|
a = SymbolicBool("a")
|
|
569
|
-
|
|
636
|
+
space.add(a)
|
|
570
637
|
symbolic_ret = summarize_execution(lambda: op(a, b))
|
|
571
638
|
concrete_ret = summarize_execution(lambda: op(realize(a), b), detach_path=False)
|
|
572
639
|
assert symbolic_ret == concrete_ret
|
|
573
640
|
|
|
574
641
|
|
|
575
642
|
@pytest.mark.parametrize("b", (False, 1, -2.0, NAN, INF, -INF))
|
|
576
|
-
@pytest.mark.parametrize(
|
|
643
|
+
@pytest.mark.parametrize(
|
|
644
|
+
"op",
|
|
645
|
+
(operator.lt, operator.eq, operator.add, operator.mul, operator.eq, operator.ne),
|
|
646
|
+
)
|
|
577
647
|
def test_float_ops(b, op):
|
|
578
648
|
with standalone_statespace as space:
|
|
579
649
|
with NoTracing():
|
|
580
|
-
a =
|
|
581
|
-
|
|
650
|
+
a = space.extra(ModelingDirector).choose(float)("a")
|
|
651
|
+
space.add(a < 0)
|
|
582
652
|
symbolic_ret = summarize_execution(lambda: op(a, b))
|
|
583
653
|
concrete_ret = summarize_execution(lambda: op(realize(a), b), detach_path=False)
|
|
584
654
|
assert symbolic_ret == concrete_ret
|
|
@@ -595,6 +665,26 @@ def test_int_from_str():
|
|
|
595
665
|
check_states(f, POST_FAIL)
|
|
596
666
|
|
|
597
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
|
+
|
|
598
688
|
def test_easy_float_from_str():
|
|
599
689
|
def f(a: str) -> float:
|
|
600
690
|
"""
|
|
@@ -618,15 +708,31 @@ def test_float_from_three_digit_str():
|
|
|
618
708
|
proxy_for_type(int, "xat1"),
|
|
619
709
|
proxy_for_type(int, "xat2"),
|
|
620
710
|
]
|
|
621
|
-
for point in codepoints:
|
|
622
|
-
space.add(point.var >= ord("0"))
|
|
623
|
-
space.add(point.var <= ord("9"))
|
|
624
711
|
x = LazyIntSymbolicStr(codepoints)
|
|
712
|
+
for point in codepoints:
|
|
713
|
+
space.add(point >= ord("0"))
|
|
714
|
+
space.add(point <= ord("9"))
|
|
625
715
|
asfloat = float(x)
|
|
626
|
-
assert space.is_possible(asfloat
|
|
627
|
-
assert not space.is_possible(asfloat
|
|
628
|
-
assert space.is_possible(asfloat
|
|
629
|
-
assert not space.is_possible(asfloat
|
|
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)
|
|
630
736
|
|
|
631
737
|
|
|
632
738
|
def test_int_bitwise_find_negative_input():
|
|
@@ -644,7 +750,7 @@ def test_int_bitwise_find_negative_input():
|
|
|
644
750
|
def test_int_bit_length(val):
|
|
645
751
|
with standalone_statespace as space:
|
|
646
752
|
x = proxy_for_type(int, "x")
|
|
647
|
-
space.add(x
|
|
753
|
+
space.add(x == val)
|
|
648
754
|
assert realize(x.bit_length()) == val.bit_length()
|
|
649
755
|
|
|
650
756
|
|
|
@@ -654,7 +760,7 @@ def test_int_bit_length(val):
|
|
|
654
760
|
def test_int_to_bytes(val):
|
|
655
761
|
with standalone_statespace as space:
|
|
656
762
|
x = proxy_for_type(int, "x")
|
|
657
|
-
space.add(x
|
|
763
|
+
space.add(x == val)
|
|
658
764
|
assert realize(x.to_bytes(2, "big", signed=True)) == val.to_bytes(
|
|
659
765
|
2, "big", signed=True
|
|
660
766
|
)
|
|
@@ -664,7 +770,7 @@ def test_int_format():
|
|
|
664
770
|
with standalone_statespace as space:
|
|
665
771
|
with NoTracing():
|
|
666
772
|
x = SymbolicInt("x")
|
|
667
|
-
|
|
773
|
+
space.add(x == 42)
|
|
668
774
|
assert x.__format__("") == "42"
|
|
669
775
|
# TODO this fails:
|
|
670
776
|
# assert x.__format__("f") == "42.000000"
|
|
@@ -863,6 +969,31 @@ def test_str_replace_method() -> None:
|
|
|
863
969
|
check_states(f, POST_FAIL)
|
|
864
970
|
|
|
865
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
|
+
|
|
866
997
|
@pytest.mark.demo
|
|
867
998
|
def test_str_index_method() -> None:
|
|
868
999
|
def f(a: str) -> int:
|
|
@@ -1164,18 +1295,17 @@ def test_str_center():
|
|
|
1164
1295
|
with standalone_statespace as space:
|
|
1165
1296
|
with NoTracing():
|
|
1166
1297
|
string = LazyIntSymbolicStr("string")
|
|
1167
|
-
space.add(string.__len__().var == 3)
|
|
1168
1298
|
fillch = LazyIntSymbolicStr("fillch")
|
|
1169
|
-
space.add(fillch.__len__().var == 1)
|
|
1170
1299
|
sz = SymbolicInt("sz")
|
|
1171
|
-
space.add(sz.var > 5)
|
|
1172
1300
|
sz6 = SymbolicInt("sz6")
|
|
1173
|
-
|
|
1301
|
+
space.add(string.__len__() == 3)
|
|
1302
|
+
space.add(fillch.__len__() == 1)
|
|
1303
|
+
space.add(sz > 5)
|
|
1304
|
+
space.add(sz6 == 6)
|
|
1174
1305
|
assert "boo".center(sz6) == " boo "
|
|
1175
1306
|
symbolic_centered = "boo".center(sz, fillch)
|
|
1176
1307
|
starts_with_nonfill = ord(symbolic_centered[0]) != ord(fillch)
|
|
1177
|
-
|
|
1178
|
-
assert not space.is_possible(starts_with_nonfill.var)
|
|
1308
|
+
assert not space.is_possible(starts_with_nonfill)
|
|
1179
1309
|
|
|
1180
1310
|
|
|
1181
1311
|
def test_str_map_chars() -> None:
|
|
@@ -1201,12 +1331,12 @@ def test_str___add___method() -> None:
|
|
|
1201
1331
|
def test_str_bool():
|
|
1202
1332
|
with standalone_statespace as space, NoTracing():
|
|
1203
1333
|
a = LazyIntSymbolicStr("a")
|
|
1204
|
-
space.add(a.__len__().var > 0)
|
|
1205
1334
|
with ResumedTracing():
|
|
1335
|
+
space.add(a.__len__() > 0)
|
|
1206
1336
|
assert bool(a)
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
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")
|
|
1210
1340
|
|
|
1211
1341
|
|
|
1212
1342
|
def test_str_eq():
|
|
@@ -1234,9 +1364,9 @@ def test_str_filter_with_none():
|
|
|
1234
1364
|
string = LazyIntSymbolicStr([ord("a")])
|
|
1235
1365
|
truthyint = proxy_for_type(int, "truthyint")
|
|
1236
1366
|
falseyint = proxy_for_type(int, "falseyint")
|
|
1237
|
-
space.add(truthyint.var == 10)
|
|
1238
|
-
space.add(falseyint.var == 0)
|
|
1239
1367
|
with ResumedTracing():
|
|
1368
|
+
space.add(truthyint == 10)
|
|
1369
|
+
space.add(falseyint == 0)
|
|
1240
1370
|
ret = deep_realize(list(filter(None, [falseyint, 42, 0, truthyint])))
|
|
1241
1371
|
assert ret == [42, 10]
|
|
1242
1372
|
|
|
@@ -1268,20 +1398,18 @@ def test_str_format_basic():
|
|
|
1268
1398
|
with standalone_statespace as space:
|
|
1269
1399
|
with NoTracing():
|
|
1270
1400
|
s = LazyIntSymbolicStr("s")
|
|
1271
|
-
|
|
1272
|
-
assert space.is_possible(
|
|
1273
|
-
assert space.is_possible(
|
|
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"))
|
|
1274
1404
|
|
|
1275
1405
|
|
|
1276
1406
|
def test_str_format_map():
|
|
1277
1407
|
with standalone_statespace as space:
|
|
1278
1408
|
with NoTracing():
|
|
1279
1409
|
s = LazyIntSymbolicStr("s")
|
|
1280
|
-
|
|
1281
|
-
assert space.is_possible(
|
|
1282
|
-
assert space.is_possible(
|
|
1283
|
-
(ord("a{foo}c".format_map({"foo": s})[1]) == ord("b")).var
|
|
1284
|
-
)
|
|
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"))
|
|
1285
1413
|
|
|
1286
1414
|
|
|
1287
1415
|
def test_str_rfind() -> None:
|
|
@@ -1352,7 +1480,7 @@ def test_str_lower():
|
|
|
1352
1480
|
|
|
1353
1481
|
|
|
1354
1482
|
def test_str_title():
|
|
1355
|
-
chr_lj = "\
|
|
1483
|
+
chr_lj = "\u01c9" # "lj"
|
|
1356
1484
|
chr_Lj = "\u01c8" # "Lj" (different from "LJ", "\u01c7")
|
|
1357
1485
|
with standalone_statespace:
|
|
1358
1486
|
with NoTracing():
|
|
@@ -1427,11 +1555,20 @@ def test_tuple___len___method():
|
|
|
1427
1555
|
def test_tuple___repr__symbolic_in_concrete(space) -> None:
|
|
1428
1556
|
x = proxy_for_type(int, "x")
|
|
1429
1557
|
with ResumedTracing():
|
|
1430
|
-
space.add(x
|
|
1558
|
+
space.add(x == 4) # type: ignore
|
|
1431
1559
|
container = (x, x)
|
|
1432
1560
|
assert repr(container) == "(4, 4)"
|
|
1433
1561
|
|
|
1434
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
|
+
|
|
1435
1572
|
def test_tuple_range_intersection_fail() -> None:
|
|
1436
1573
|
def f(a: Tuple[int, int], b: Tuple[int, int]) -> Optional[Tuple[int, int]]:
|
|
1437
1574
|
"""
|
|
@@ -1486,6 +1623,13 @@ def test_tuple_runtime_type() -> None:
|
|
|
1486
1623
|
check_states(f, POST_FAIL)
|
|
1487
1624
|
|
|
1488
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
|
+
|
|
1489
1633
|
def test_tuple_isinstance_check() -> None:
|
|
1490
1634
|
def f(uniform_tuple: Tuple[List, ...], basic_tuple: tuple) -> Tuple[bool, bool]:
|
|
1491
1635
|
"""post: _ == (True, True)"""
|
|
@@ -1523,11 +1667,11 @@ def test_range_slicing(space) -> None:
|
|
|
1523
1667
|
# Repeat this with a symbolic range:
|
|
1524
1668
|
rng = proxy_for_type(range, "rng")
|
|
1525
1669
|
newstart = proxy_for_type(int, "newstart")
|
|
1526
|
-
space.add(rng.start.var == 3) # type: ignore
|
|
1527
|
-
space.add(rng.stop.var == 40) # type: ignore
|
|
1528
|
-
space.add(rng.step.var == 2) # type: ignore
|
|
1529
|
-
space.add(newstart.var == 5) # type: ignore
|
|
1530
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
|
|
1531
1675
|
assert list(rng[newstart::-1]) == [13, 11, 9, 7, 5, 3]
|
|
1532
1676
|
|
|
1533
1677
|
|
|
@@ -1583,7 +1727,7 @@ def test_list___add___method() -> None:
|
|
|
1583
1727
|
def test_list___repr___symbolic_in_concrete(space) -> None:
|
|
1584
1728
|
x = proxy_for_type(int, "x")
|
|
1585
1729
|
with ResumedTracing():
|
|
1586
|
-
space.add(x
|
|
1730
|
+
space.add(x == 4) # type: ignore
|
|
1587
1731
|
continer = [x]
|
|
1588
1732
|
assert f"{continer=}" == "continer=[4]"
|
|
1589
1733
|
|
|
@@ -1971,8 +2115,8 @@ def test_list_shallow_realization():
|
|
|
1971
2115
|
with standalone_statespace as space:
|
|
1972
2116
|
nums = proxy_for_type(List[int], "nums")
|
|
1973
2117
|
numslen = len(nums)
|
|
2118
|
+
space.add(numslen == 1)
|
|
1974
2119
|
with NoTracing():
|
|
1975
|
-
space.add(numslen.var == 1)
|
|
1976
2120
|
realized = realize(nums)
|
|
1977
2121
|
assert type(realized) is list
|
|
1978
2122
|
assert len(realized) == 1
|
|
@@ -1981,15 +2125,16 @@ def test_list_shallow_realization():
|
|
|
1981
2125
|
|
|
1982
2126
|
def test_list_concrete_with_symbolic_slice(space):
|
|
1983
2127
|
idx = proxy_for_type(int, "i")
|
|
1984
|
-
space.add(1 <= idx.var)
|
|
1985
|
-
space.add(idx.var <= 3)
|
|
1986
2128
|
with ResumedTracing():
|
|
2129
|
+
space.add(1 <= idx)
|
|
2130
|
+
space.add(idx <= 3)
|
|
1987
2131
|
prefix = [0, 1, 2, 3][:idx]
|
|
1988
2132
|
prefixlen = len(prefix)
|
|
1989
2133
|
assert isinstance(prefix, CrossHairValue)
|
|
1990
2134
|
assert isinstance(prefixlen, CrossHairValue)
|
|
1991
|
-
|
|
1992
|
-
|
|
2135
|
+
with ResumedTracing():
|
|
2136
|
+
assert space.is_possible(prefixlen == 1)
|
|
2137
|
+
assert space.is_possible(prefixlen == 3)
|
|
1993
2138
|
|
|
1994
2139
|
|
|
1995
2140
|
def test_list_copy(space):
|
|
@@ -2006,6 +2151,7 @@ def test_list_copy_compare_without_forking(space):
|
|
|
2006
2151
|
lst = proxy_for_type(List[int], "lst")
|
|
2007
2152
|
with ResumedTracing():
|
|
2008
2153
|
lst2 = copy.deepcopy(lst)
|
|
2154
|
+
assert lst.inner is not lst2.inner
|
|
2009
2155
|
assert type(lst2) is SymbolicList
|
|
2010
2156
|
assert lst.inner.var is lst2.inner.var
|
|
2011
2157
|
with ResumedTracing():
|
|
@@ -2025,7 +2171,7 @@ def test_dict___bool___ok() -> None:
|
|
|
2025
2171
|
check_states(f, CONFIRMED)
|
|
2026
2172
|
|
|
2027
2173
|
|
|
2028
|
-
def
|
|
2174
|
+
def test_dict___iter___fail() -> None:
|
|
2029
2175
|
def f(a: Dict[int, str]) -> List[int]:
|
|
2030
2176
|
"""
|
|
2031
2177
|
post[a]: 5 in _
|
|
@@ -2051,7 +2197,7 @@ def test_dict___iter___ok() -> None:
|
|
|
2051
2197
|
def test_dict___or___method():
|
|
2052
2198
|
with standalone_statespace as space:
|
|
2053
2199
|
d = proxy_for_type(Dict[int, int], "d")
|
|
2054
|
-
space.add(len(d)
|
|
2200
|
+
space.add(len(d) == 0)
|
|
2055
2201
|
with pytest.raises(TypeError):
|
|
2056
2202
|
d | set()
|
|
2057
2203
|
if sys.version_info >= (3, 9):
|
|
@@ -2148,10 +2294,17 @@ def test_dict___getitem___implicit_conversion_for_keys_fail() -> None:
|
|
|
2148
2294
|
check_states(f, POST_FAIL)
|
|
2149
2295
|
|
|
2150
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
|
+
|
|
2151
2304
|
def test_dict___repr___symbolic_in_concrete(space) -> None:
|
|
2152
2305
|
x = proxy_for_type(int, "x")
|
|
2153
2306
|
with ResumedTracing():
|
|
2154
|
-
space.add(x
|
|
2307
|
+
space.add(x == 4) # type: ignore
|
|
2155
2308
|
container = {x: x}
|
|
2156
2309
|
assert repr(container) == "{4: 4}"
|
|
2157
2310
|
|
|
@@ -2276,6 +2429,14 @@ def test_dict_construction_from_generator(space):
|
|
|
2276
2429
|
assert d == {1: 2, 3: 4}
|
|
2277
2430
|
|
|
2278
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
|
+
|
|
2279
2440
|
def test_dict_construction_with_duplicate_keys(space):
|
|
2280
2441
|
with ResumedTracing():
|
|
2281
2442
|
d = dict([(1, 2), (3, 4), (1, 10), (5, 6), (3, 10)])
|
|
@@ -2485,6 +2646,7 @@ def test_dict_alternate_mapping_types() -> None:
|
|
|
2485
2646
|
def test_dict_untyped_access():
|
|
2486
2647
|
def f(d: dict, k: int) -> dict:
|
|
2487
2648
|
"""
|
|
2649
|
+
pre: len(d) == 2 # (just to bound the search space a little)
|
|
2488
2650
|
pre: 42 in d
|
|
2489
2651
|
post: 42 in __return__
|
|
2490
2652
|
raises: KeyError
|
|
@@ -2492,11 +2654,7 @@ def test_dict_untyped_access():
|
|
|
2492
2654
|
del d[k]
|
|
2493
2655
|
return d
|
|
2494
2656
|
|
|
2495
|
-
|
|
2496
|
-
check_states(
|
|
2497
|
-
f,
|
|
2498
|
-
MessageType.POST_FAIL,
|
|
2499
|
-
)
|
|
2657
|
+
check_states(f, MessageType.POST_FAIL)
|
|
2500
2658
|
|
|
2501
2659
|
|
|
2502
2660
|
# NOTE: TypedDict appeared earlier than 3.9, but was not runtime-detectable until then
|
|
@@ -2521,6 +2679,7 @@ if sys.version_info >= (3, 9):
|
|
|
2521
2679
|
def test_set_basic_fail() -> None:
|
|
2522
2680
|
def f(a: Set[int], k: int) -> None:
|
|
2523
2681
|
"""
|
|
2682
|
+
pre: len(a) <= 2
|
|
2524
2683
|
post[a]: k+1 in a
|
|
2525
2684
|
"""
|
|
2526
2685
|
a.add(k)
|
|
@@ -2621,6 +2780,16 @@ def test_set_isinstance_check() -> None:
|
|
|
2621
2780
|
check_states(f, CONFIRMED)
|
|
2622
2781
|
|
|
2623
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
|
+
|
|
2624
2793
|
def test_set___eq__() -> None:
|
|
2625
2794
|
def f(a: Set[FrozenSet[int]]) -> object:
|
|
2626
2795
|
"""
|
|
@@ -2639,7 +2808,7 @@ def test_set___eq__() -> None:
|
|
|
2639
2808
|
def test_frozenset___repr__symbolic_in_concrete(space) -> None:
|
|
2640
2809
|
x = proxy_for_type(int, "x")
|
|
2641
2810
|
with ResumedTracing():
|
|
2642
|
-
space.add(x
|
|
2811
|
+
space.add(x == 4) # type: ignore
|
|
2643
2812
|
container = frozenset([x])
|
|
2644
2813
|
assert repr(container) == "frozenset({4})"
|
|
2645
2814
|
|
|
@@ -2647,11 +2816,20 @@ def test_frozenset___repr__symbolic_in_concrete(space) -> None:
|
|
|
2647
2816
|
def test_set___repr__symbolic_in_concrete(space) -> None:
|
|
2648
2817
|
x = proxy_for_type(int, "x")
|
|
2649
2818
|
with ResumedTracing():
|
|
2650
|
-
space.add(x
|
|
2819
|
+
space.add(x == 4) # type: ignore
|
|
2651
2820
|
container = {x}
|
|
2652
2821
|
assert repr(container) == "{4}"
|
|
2653
2822
|
|
|
2654
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
|
+
|
|
2655
2833
|
def test_set_no_duplicates() -> None:
|
|
2656
2834
|
def f(s: Set[int]) -> int:
|
|
2657
2835
|
"""
|
|
@@ -2690,9 +2868,9 @@ def test_frozenset_realize():
|
|
|
2690
2868
|
|
|
2691
2869
|
def test_frozenset_covariance():
|
|
2692
2870
|
with standalone_statespace as space:
|
|
2871
|
+
frozen_set = proxy_for_type(FrozenSet[AbstractBase], "x")
|
|
2872
|
+
space.add(frozen_set.__len__() == 1)
|
|
2693
2873
|
with NoTracing():
|
|
2694
|
-
frozen_set = proxy_for_type(FrozenSet[AbstractBase], "x")
|
|
2695
|
-
space.add(frozen_set.__len__().var == 1)
|
|
2696
2874
|
assert isinstance(next(iter(frozen_set)), ConcreteSubclass)
|
|
2697
2875
|
|
|
2698
2876
|
|
|
@@ -2702,9 +2880,9 @@ def test_set_invariance():
|
|
|
2702
2880
|
# assigned to a superclass container.
|
|
2703
2881
|
# Therefore, CrossHair should happily create set contents using subclasses:
|
|
2704
2882
|
with standalone_statespace as space:
|
|
2883
|
+
mutable_set = proxy_for_type(Set[AbstractBase], "x")
|
|
2884
|
+
space.add(mutable_set.__len__() == 1)
|
|
2705
2885
|
with NoTracing():
|
|
2706
|
-
mutable_set = proxy_for_type(Set[AbstractBase], "x")
|
|
2707
|
-
space.add(mutable_set.__len__().var == 1)
|
|
2708
2886
|
assert isinstance(next(iter(mutable_set)), ConcreteSubclass)
|
|
2709
2887
|
|
|
2710
2888
|
|
|
@@ -2722,14 +2900,43 @@ def test_set_iter_partial():
|
|
|
2722
2900
|
with standalone_statespace as space:
|
|
2723
2901
|
with NoTracing():
|
|
2724
2902
|
x = proxy_for_type(Set[int], "x")
|
|
2725
|
-
|
|
2903
|
+
space.add(x.__len__() == 2)
|
|
2726
2904
|
itr = iter(x)
|
|
2727
2905
|
first = next(itr)
|
|
2728
2906
|
# leave the iterator incomplete; looking for generator + context mgr problems
|
|
2729
|
-
return
|
|
2730
2907
|
|
|
2731
2908
|
|
|
2732
|
-
|
|
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:
|
|
2733
2940
|
# TODO: move most of this into a collectionslib_test.py file
|
|
2734
2941
|
def test_hashable_values_fail(self) -> None:
|
|
2735
2942
|
def f(b: bool, i: int, t: Tuple[str, ...]) -> int:
|
|
@@ -2761,7 +2968,10 @@ class ProtocolsTest(unittest.TestCase):
|
|
|
2761
2968
|
# c: SupportsComplex, # TODO: symbolic complex not yet really working
|
|
2762
2969
|
b: SupportsBytes,
|
|
2763
2970
|
) -> float:
|
|
2764
|
-
"""
|
|
2971
|
+
"""
|
|
2972
|
+
pre: math.isfinite(f) and math.isfinite(r)
|
|
2973
|
+
post: _.real <= 0
|
|
2974
|
+
"""
|
|
2765
2975
|
return abs(a) + float(f) + int(i) + round(r) + len(bytes(b))
|
|
2766
2976
|
# + complex(c)
|
|
2767
2977
|
|
|
@@ -2885,6 +3095,12 @@ def test_object___eq__() -> None:
|
|
|
2885
3095
|
check_states(f, POST_FAIL)
|
|
2886
3096
|
|
|
2887
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
|
+
|
|
2888
3104
|
def test_issubclass_abc():
|
|
2889
3105
|
with standalone_statespace as space:
|
|
2890
3106
|
with NoTracing():
|
|
@@ -2974,6 +3190,17 @@ def test_callable_as_bool() -> None:
|
|
|
2974
3190
|
check_states(f, CONFIRMED)
|
|
2975
3191
|
|
|
2976
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
|
+
|
|
2977
3204
|
@pytest.mark.smoke
|
|
2978
3205
|
def test_callable_repr() -> None:
|
|
2979
3206
|
def f(f1: Callable[[int], int]) -> int:
|
|
@@ -3030,9 +3257,9 @@ def test_all():
|
|
|
3030
3257
|
|
|
3031
3258
|
with standalone_statespace as space:
|
|
3032
3259
|
true_bool = proxy_for_type(bool, "true_bool")
|
|
3033
|
-
space.add(true_bool
|
|
3260
|
+
space.add(true_bool)
|
|
3034
3261
|
nonempty_list = proxy_for_type(List[object], "nonempty_list")
|
|
3035
|
-
space.add(len(nonempty_list)
|
|
3262
|
+
space.add(len(nonempty_list) == 1)
|
|
3036
3263
|
arbitrary_bool = proxy_for_type(bool, "arbitrary_bool")
|
|
3037
3264
|
boom = ExplodingBool()
|
|
3038
3265
|
|
|
@@ -3041,8 +3268,8 @@ def test_all():
|
|
|
3041
3268
|
sym_false = all([True, true_bool, nonempty_list, arbitrary_bool])
|
|
3042
3269
|
with NoTracing():
|
|
3043
3270
|
assert isinstance(sym_false, SymbolicBool)
|
|
3044
|
-
assert space.is_possible(sym_false.var)
|
|
3045
3271
|
assert space.is_possible(z3.Not(sym_false.var))
|
|
3272
|
+
assert space.is_possible(sym_false)
|
|
3046
3273
|
|
|
3047
3274
|
with pytest.raises(ExplodingBool):
|
|
3048
3275
|
all([true_bool, True, nonempty_list, boom])
|
|
@@ -3059,7 +3286,7 @@ def test_any():
|
|
|
3059
3286
|
false_bool = proxy_for_type(bool, "false_bool")
|
|
3060
3287
|
space.add(z3.Not(false_bool.var))
|
|
3061
3288
|
empty_list = proxy_for_type(List[object], "empty_list")
|
|
3062
|
-
space.add(len(empty_list)
|
|
3289
|
+
space.add(len(empty_list) == 0)
|
|
3063
3290
|
arbitrary_bool = proxy_for_type(bool, "arbitrary_bool")
|
|
3064
3291
|
# arbitrary_list = proxy_for_type(List[object], "arbitrary_list")
|
|
3065
3292
|
# TODO: in theory, we should be able to keep `bool(arbitrary_list)`
|
|
@@ -3072,8 +3299,8 @@ def test_any():
|
|
|
3072
3299
|
sym_false = any([False, false_bool, empty_list, arbitrary_bool])
|
|
3073
3300
|
with NoTracing():
|
|
3074
3301
|
assert isinstance(sym_false, SymbolicBool)
|
|
3075
|
-
assert space.is_possible(sym_false.var)
|
|
3076
3302
|
assert space.is_possible(z3.Not(sym_false.var))
|
|
3303
|
+
assert space.is_possible(sym_false)
|
|
3077
3304
|
|
|
3078
3305
|
with pytest.raises(ExplodingBool):
|
|
3079
3306
|
any([false_bool, False, empty_list, boom])
|
|
@@ -3221,6 +3448,10 @@ def test_bytes_roundtrip_array_as_symbolic():
|
|
|
3221
3448
|
assert new_bytes.inner is orig_bytes.inner
|
|
3222
3449
|
|
|
3223
3450
|
|
|
3451
|
+
@pytest.mark.skipif(
|
|
3452
|
+
sys.version_info >= (3, 13),
|
|
3453
|
+
reason="Need to intercept UnicodeDecodeError.str in 3.13+",
|
|
3454
|
+
)
|
|
3224
3455
|
@pytest.mark.demo
|
|
3225
3456
|
def test_bytes_decode_method():
|
|
3226
3457
|
def f(b: bytes) -> str:
|
|
@@ -3277,13 +3508,13 @@ def test_extend_concrete_bytearray():
|
|
|
3277
3508
|
xyz = proxy_for_type(bytearray, "xyz")
|
|
3278
3509
|
b.extend(xyz)
|
|
3279
3510
|
assert not space.is_possible(b[0] != ord("a"))
|
|
3280
|
-
assert space.is_possible(len(b)
|
|
3511
|
+
assert space.is_possible(len(b) > 3)
|
|
3281
3512
|
|
|
3282
3513
|
|
|
3283
3514
|
def test_bytearray_slice():
|
|
3284
3515
|
with standalone_statespace as space:
|
|
3285
3516
|
xyz = proxy_for_type(bytearray, "xyz")
|
|
3286
|
-
space.add(xyz.__len__()
|
|
3517
|
+
space.add(xyz.__len__() == 3)
|
|
3287
3518
|
assert type(xyz[1:]) is bytearray
|
|
3288
3519
|
|
|
3289
3520
|
|
|
@@ -3292,9 +3523,8 @@ def test_memoryview_compare():
|
|
|
3292
3523
|
mv1 = proxy_for_type(memoryview, "mv1")
|
|
3293
3524
|
mv2 = proxy_for_type(memoryview, "mv2")
|
|
3294
3525
|
len1, len2 = len(mv1), len(mv2)
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
space.add(len2.var == 0)
|
|
3526
|
+
space.add(len1 == 0)
|
|
3527
|
+
space.add(len2 == 0)
|
|
3298
3528
|
views_equal = mv1 == mv2
|
|
3299
3529
|
with NoTracing():
|
|
3300
3530
|
assert views_equal is True
|
|
@@ -3304,7 +3534,7 @@ def test_memoryview_cast():
|
|
|
3304
3534
|
"""post: _"""
|
|
3305
3535
|
with standalone_statespace as space:
|
|
3306
3536
|
val = proxy_for_type(int, "val")
|
|
3307
|
-
space.add(val
|
|
3537
|
+
space.add(val == 254)
|
|
3308
3538
|
mv = memoryview(bytearray([val]))
|
|
3309
3539
|
assert mv.cast("b")[0] == -2
|
|
3310
3540
|
|
|
@@ -3313,7 +3543,7 @@ def test_memoryview_toreadonly():
|
|
|
3313
3543
|
"""post: _"""
|
|
3314
3544
|
with standalone_statespace as space:
|
|
3315
3545
|
mv = proxy_for_type(memoryview, "mv")
|
|
3316
|
-
space.add(mv.__len__()
|
|
3546
|
+
space.add(mv.__len__() == 1)
|
|
3317
3547
|
mv2 = mv.toreadonly()
|
|
3318
3548
|
mv[0] = 12
|
|
3319
3549
|
assert mv2[0] == 12
|
|
@@ -3325,7 +3555,7 @@ def test_memoryview_properties():
|
|
|
3325
3555
|
"""post: _"""
|
|
3326
3556
|
with standalone_statespace as space:
|
|
3327
3557
|
symbolic_mv = proxy_for_type(memoryview, "symbolic_mv")
|
|
3328
|
-
space.add(symbolic_mv.__len__()
|
|
3558
|
+
space.add(symbolic_mv.__len__() == 1)
|
|
3329
3559
|
concrete_mv = memoryview(bytearray(b"a"))
|
|
3330
3560
|
assert symbolic_mv.contiguous == concrete_mv.contiguous
|
|
3331
3561
|
assert symbolic_mv.c_contiguous == concrete_mv.c_contiguous
|
|
@@ -3340,46 +3570,47 @@ def test_memoryview_properties():
|
|
|
3340
3570
|
assert symbolic_mv.suboffsets == concrete_mv.suboffsets
|
|
3341
3571
|
|
|
3342
3572
|
|
|
3343
|
-
def test_chr():
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
space.add(
|
|
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)
|
|
3347
3578
|
c = chr(i)
|
|
3348
|
-
assert space.is_possible(c._codepoints[0]
|
|
3349
|
-
assert not space.is_possible(c._codepoints[0]
|
|
3579
|
+
assert space.is_possible(c._codepoints[0] == ord("a"))
|
|
3580
|
+
assert not space.is_possible(c._codepoints[0] == 0)
|
|
3350
3581
|
|
|
3351
3582
|
|
|
3352
|
-
def test_ord():
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
space.add(len(s)
|
|
3583
|
+
def test_ord(space):
|
|
3584
|
+
s = proxy_for_type(str, "s")
|
|
3585
|
+
with ResumedTracing():
|
|
3586
|
+
space.add(len(s) == 1)
|
|
3356
3587
|
i = ord(s)
|
|
3357
|
-
assert space.is_possible(i
|
|
3358
|
-
assert not space.is_possible(i
|
|
3588
|
+
assert space.is_possible(i == 42)
|
|
3589
|
+
assert not space.is_possible(i > sys.maxunicode)
|
|
3359
3590
|
|
|
3360
3591
|
|
|
3361
|
-
def test_unicode_support():
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
space.add(len(s)
|
|
3365
|
-
assert space.is_possible(
|
|
3366
|
-
assert space.is_possible(
|
|
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")
|
|
3367
3598
|
|
|
3368
3599
|
|
|
3369
3600
|
@pytest.mark.parametrize("concrete_x", (25, 15, 6, -4, -15, -25))
|
|
3370
|
-
def test_int_round(concrete_x):
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
space.add(x
|
|
3376
|
-
space.add(d
|
|
3377
|
-
assert not space.is_possible((round(x, d) != concrete_ret)
|
|
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))
|
|
3378
3609
|
|
|
3379
3610
|
|
|
3380
3611
|
class ExplodingValue:
|
|
3381
3612
|
def __getattribute__(self, name):
|
|
3382
|
-
raise
|
|
3613
|
+
raise CrossHairInternal
|
|
3383
3614
|
|
|
3384
3615
|
|
|
3385
3616
|
@dataclasses.dataclass
|
|
@@ -3401,18 +3632,81 @@ def test_class_with_special_member_names():
|
|
|
3401
3632
|
check_states(f, CONFIRMED)
|
|
3402
3633
|
|
|
3403
3634
|
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
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
|
|
3410
3685
|
|
|
3411
|
-
Postcondition holds in a math-sense, but not when floating point precision
|
|
3412
|
-
comes into play (e.g. it's false for 13 / 99)
|
|
3413
|
-
"""
|
|
3414
3686
|
|
|
3415
|
-
|
|
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
|
|
3416
3710
|
|
|
3417
3711
|
|
|
3418
3712
|
def TODO_test_int_mod_float():
|
|
@@ -3422,8 +3716,8 @@ def TODO_test_int_mod_float():
|
|
|
3422
3716
|
y = proxy_for_type(float, "y")
|
|
3423
3717
|
modval = x % y
|
|
3424
3718
|
with NoTracing():
|
|
3425
|
-
assert type(modval) ==
|
|
3426
|
-
|
|
3719
|
+
assert type(modval) == RealBasedSymbolicFloat
|
|
3720
|
+
assert space.is_possible(modval == 12.12)
|
|
3427
3721
|
|
|
3428
3722
|
|
|
3429
3723
|
def TODO_test_can_generate_integral():
|
|
@@ -3439,9 +3733,3 @@ def TODO_test_deepcopy_independence():
|
|
|
3439
3733
|
with NoTracing():
|
|
3440
3734
|
assert ls[0] is not lscopy[0]
|
|
3441
3735
|
# Next try mutation on one and test the other...
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
if __name__ == "__main__":
|
|
3445
|
-
if ("-v" in sys.argv) or ("--verbose" in sys.argv):
|
|
3446
|
-
set_debug(True)
|
|
3447
|
-
unittest.main()
|