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.
Files changed (123) hide show
  1. _crosshair_tracers.cpython-39-darwin.so +0 -0
  2. crosshair/__init__.py +1 -1
  3. crosshair/_mark_stacks.h +51 -24
  4. crosshair/_tracers.h +9 -5
  5. crosshair/_tracers_test.py +19 -9
  6. crosshair/auditwall.py +9 -8
  7. crosshair/auditwall_test.py +31 -19
  8. crosshair/codeconfig.py +3 -2
  9. crosshair/condition_parser.py +17 -133
  10. crosshair/condition_parser_test.py +54 -96
  11. crosshair/conftest.py +1 -1
  12. crosshair/copyext.py +91 -22
  13. crosshair/copyext_test.py +33 -0
  14. crosshair/core.py +259 -203
  15. crosshair/core_and_libs.py +20 -0
  16. crosshair/core_regestered_types_test.py +82 -0
  17. crosshair/core_test.py +693 -664
  18. crosshair/diff_behavior.py +76 -21
  19. crosshair/diff_behavior_test.py +132 -23
  20. crosshair/dynamic_typing.py +128 -18
  21. crosshair/dynamic_typing_test.py +91 -4
  22. crosshair/enforce.py +1 -6
  23. crosshair/enforce_test.py +15 -23
  24. crosshair/examples/check_examples_test.py +2 -1
  25. crosshair/fnutil.py +2 -3
  26. crosshair/fnutil_test.py +0 -7
  27. crosshair/fuzz_core_test.py +70 -83
  28. crosshair/libimpl/arraylib.py +10 -7
  29. crosshair/libimpl/binascii_ch_test.py +30 -0
  30. crosshair/libimpl/binascii_test.py +67 -0
  31. crosshair/libimpl/binasciilib.py +150 -0
  32. crosshair/libimpl/bisectlib_test.py +5 -5
  33. crosshair/libimpl/builtinslib.py +1002 -682
  34. crosshair/libimpl/builtinslib_ch_test.py +108 -30
  35. crosshair/libimpl/builtinslib_test.py +431 -143
  36. crosshair/libimpl/codecslib.py +22 -2
  37. crosshair/libimpl/codecslib_test.py +41 -9
  38. crosshair/libimpl/collectionslib.py +44 -8
  39. crosshair/libimpl/collectionslib_test.py +108 -20
  40. crosshair/libimpl/copylib.py +1 -1
  41. crosshair/libimpl/copylib_test.py +18 -0
  42. crosshair/libimpl/datetimelib.py +84 -67
  43. crosshair/libimpl/datetimelib_ch_test.py +12 -7
  44. crosshair/libimpl/datetimelib_test.py +5 -6
  45. crosshair/libimpl/decimallib.py +5257 -0
  46. crosshair/libimpl/decimallib_ch_test.py +78 -0
  47. crosshair/libimpl/decimallib_test.py +76 -0
  48. crosshair/libimpl/encodings/_encutil.py +21 -11
  49. crosshair/libimpl/fractionlib.py +16 -0
  50. crosshair/libimpl/fractionlib_test.py +80 -0
  51. crosshair/libimpl/functoolslib.py +19 -7
  52. crosshair/libimpl/functoolslib_test.py +22 -6
  53. crosshair/libimpl/hashliblib.py +30 -0
  54. crosshair/libimpl/hashliblib_test.py +18 -0
  55. crosshair/libimpl/heapqlib.py +32 -5
  56. crosshair/libimpl/heapqlib_test.py +15 -12
  57. crosshair/libimpl/iolib.py +7 -4
  58. crosshair/libimpl/ipaddresslib.py +8 -0
  59. crosshair/libimpl/itertoolslib_test.py +1 -1
  60. crosshair/libimpl/mathlib.py +165 -2
  61. crosshair/libimpl/mathlib_ch_test.py +44 -0
  62. crosshair/libimpl/mathlib_test.py +59 -16
  63. crosshair/libimpl/oslib.py +7 -0
  64. crosshair/libimpl/pathliblib_test.py +10 -0
  65. crosshair/libimpl/randomlib.py +1 -0
  66. crosshair/libimpl/randomlib_test.py +6 -4
  67. crosshair/libimpl/relib.py +180 -59
  68. crosshair/libimpl/relib_ch_test.py +26 -2
  69. crosshair/libimpl/relib_test.py +77 -14
  70. crosshair/libimpl/timelib.py +35 -13
  71. crosshair/libimpl/timelib_test.py +13 -3
  72. crosshair/libimpl/typeslib.py +15 -0
  73. crosshair/libimpl/typeslib_test.py +36 -0
  74. crosshair/libimpl/unicodedatalib_test.py +3 -3
  75. crosshair/libimpl/weakreflib.py +13 -0
  76. crosshair/libimpl/weakreflib_test.py +69 -0
  77. crosshair/libimpl/zliblib.py +15 -0
  78. crosshair/libimpl/zliblib_test.py +13 -0
  79. crosshair/lsp_server.py +21 -10
  80. crosshair/main.py +48 -28
  81. crosshair/main_test.py +59 -14
  82. crosshair/objectproxy.py +39 -14
  83. crosshair/objectproxy_test.py +27 -13
  84. crosshair/opcode_intercept.py +212 -24
  85. crosshair/opcode_intercept_test.py +172 -18
  86. crosshair/options.py +0 -1
  87. crosshair/patch_equivalence_test.py +5 -21
  88. crosshair/path_cover.py +7 -5
  89. crosshair/path_search.py +6 -4
  90. crosshair/path_search_test.py +1 -2
  91. crosshair/pathing_oracle.py +53 -10
  92. crosshair/pathing_oracle_test.py +21 -0
  93. crosshair/pure_importer_test.py +5 -21
  94. crosshair/register_contract.py +16 -6
  95. crosshair/register_contract_test.py +2 -14
  96. crosshair/simplestructs.py +154 -85
  97. crosshair/simplestructs_test.py +16 -2
  98. crosshair/smtlib.py +24 -0
  99. crosshair/smtlib_test.py +14 -0
  100. crosshair/statespace.py +319 -196
  101. crosshair/statespace_test.py +45 -0
  102. crosshair/stubs_parser.py +0 -2
  103. crosshair/test_util.py +87 -25
  104. crosshair/test_util_test.py +26 -0
  105. crosshair/tools/check_init_and_setup_coincide.py +0 -3
  106. crosshair/tools/generate_demo_table.py +2 -2
  107. crosshair/tracers.py +141 -49
  108. crosshair/type_repo.py +11 -4
  109. crosshair/unicode_categories.py +1 -0
  110. crosshair/util.py +158 -76
  111. crosshair/util_test.py +13 -20
  112. crosshair/watcher.py +4 -4
  113. crosshair/z3util.py +1 -1
  114. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/METADATA +45 -36
  115. crosshair_tool-0.0.100.dist-info/RECORD +176 -0
  116. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/WHEEL +2 -1
  117. crosshair/examples/hypothesis/__init__.py +0 -2
  118. crosshair/examples/hypothesis/bugs_detected/simple_strategies.py +0 -74
  119. crosshair_tool-0.0.56.dist-info/RECORD +0 -152
  120. /crosshair/{examples/hypothesis/bugs_detected/__init__.py → py.typed} +0 -0
  121. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/entry_points.txt +0 -0
  122. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info/licenses}/LICENSE +0 -0
  123. {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 CrosshairInternal, IgnoreAttempt, set_debug
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 = SymbolicFloat("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 = SymbolicFloat("f")
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 test_number_promotion_compare_unknown() -> None:
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, CANNOT_CONFIRM)
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, CANNOT_CONFIRM)
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, CANNOT_CONFIRM)
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 test_int_from_bytes() -> None:
513
- def f(byt: bytes) -> int:
514
- """
515
- pre: len(byt) == 2
516
- post: _ != 5
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
- check_states(f, POST_FAIL)
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
- space.add(a.var)
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("op", (operator.lt, operator.eq, operator.add, operator.mul))
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 = SymbolicFloat("a")
581
- space.add(a.var < 0)
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.var <= 999)
627
- assert not space.is_possible(asfloat.var > 999)
628
- assert space.is_possible(asfloat.var == 0) # (because "000" is a valid float)
629
- assert not space.is_possible(asfloat.var == 500.5)
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.var == val)
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.var == val)
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
- space.add(x.var == 42)
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
- space.add(sz6.var == 6)
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
- with NoTracing():
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
- # Can we retain our symbolic state after forcing a positive truthiness?:
1208
- assert space.is_possible((a == "this").var)
1209
- assert space.is_possible((a == "that").var)
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
- space.add(s.__len__().var == 1)
1272
- assert space.is_possible((s == "z").var)
1273
- assert space.is_possible((ord("a{0}c".format(s)[1]) == ord("b")).var)
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
- space.add(s.__len__().var == 1)
1281
- assert space.is_possible((s == "z").var)
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 = "\u01C9" # "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.var == 4) # type: ignore
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.var == 4) # type: ignore
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
- assert space.is_possible(prefixlen.var == 1)
1992
- assert space.is_possible(prefixlen.var == 3)
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 test_dict___iter__() -> None:
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).var == 0)
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.var == 4) # type: ignore
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
- # TODO: profile / optimize
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.var == 4) # type: ignore
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.var == 4) # type: ignore
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
- space.add(x.__len__().var == 2)
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
- class ProtocolsTest(unittest.TestCase):
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
- """post: _.real <= 0"""
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.var)
3260
+ space.add(true_bool)
3034
3261
  nonempty_list = proxy_for_type(List[object], "nonempty_list")
3035
- space.add(len(nonempty_list).var == 1)
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).var == 0)
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).var > 3)
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__().var == 3)
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
- with NoTracing():
3296
- space.add(len1.var == 0)
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.var == 254)
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__().var == 1)
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__().var == 1)
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
- with standalone_statespace as space:
3345
- i = proxy_for_type(int, "i")
3346
- space.add(z3.And(10 <= i.var, i.var < 256))
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].var == ord("a"))
3349
- assert not space.is_possible(c._codepoints[0].var == 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
- with standalone_statespace as space:
3354
- s = proxy_for_type(str, "s")
3355
- space.add(len(s).var == 1)
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.var == 42)
3358
- assert not space.is_possible(i.var > sys.maxunicode)
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
- with standalone_statespace as space:
3363
- s = proxy_for_type(str, "s")
3364
- space.add(len(s).var == 1)
3365
- assert space.is_possible((s == "a").var)
3366
- assert space.is_possible((s == "\u1234").var)
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
- with standalone_statespace as space:
3372
- concrete_ret = round(concrete_x, -1)
3373
- x = proxy_for_type(int, "x")
3374
- d = proxy_for_type(int, "d")
3375
- space.add(x.var == concrete_x)
3376
- space.add(d.var == -1)
3377
- assert not space.is_possible((round(x, d) != concrete_ret).var)
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 CrosshairInternal
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
- def TODO_test_float_precision_issues(a, b):
3405
- # Does not yet work: floating point is modeled with infinite precision at the moment
3406
- def f(a: int, b: int):
3407
- """
3408
- pre: b !=0
3409
- post: a == b * _
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
- return a / b
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) == SymbolicFloat
3426
- assert space.is_possible(modval.var == 12.12)
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()