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
@@ -1,4 +1,6 @@
1
1
  import collections
2
+ import sys
3
+ from inspect import Parameter, Signature, signature
2
4
  from typing import (
3
5
  Callable,
4
6
  Dict,
@@ -6,15 +8,25 @@ from typing import (
6
8
  Iterable,
7
9
  List,
8
10
  Mapping,
11
+ Optional,
9
12
  Sequence,
10
13
  Tuple,
11
14
  TypeVar,
12
15
  Union,
13
16
  )
14
17
 
18
+ import pytest
15
19
  from typing_extensions import TypedDict
16
20
 
17
- from crosshair.dynamic_typing import get_bindings_from_type_arguments, realize, unify
21
+ from crosshair.dynamic_typing import (
22
+ get_bindings_from_type_arguments,
23
+ intersect_signatures,
24
+ realize,
25
+ unify,
26
+ )
27
+ from crosshair.options import AnalysisOptionSet
28
+ from crosshair.statespace import CANNOT_CONFIRM
29
+ from crosshair.test_util import check_states
18
30
 
19
31
  _T = TypeVar("_T")
20
32
  _U = TypeVar("_U")
@@ -49,7 +61,12 @@ def test_typedicts():
49
61
  def test_typevars():
50
62
  bindings = collections.ChainMap()
51
63
  assert unify(Tuple[int, str, List[int]], Tuple[int, _T, _U], bindings)
52
- assert realize(Mapping[_U, _T], bindings) == Mapping[List[int], str]
64
+
65
+ ret = realize(Mapping[_U, _T], bindings)
66
+ if sys.version_info >= (3, 9):
67
+ assert ret == collections.abc.Mapping[List[int], str]
68
+ else:
69
+ assert ret == Mapping[List[int], str]
53
70
 
54
71
 
55
72
  def test_bound_vtypears():
@@ -68,7 +85,13 @@ def test_callable():
68
85
 
69
86
  assert not unify(Callable[[List], bool], Callable[[Iterable], bool], bindings)
70
87
  assert unify(Callable[[int, _T], List[int]], Callable[[int, str], _U], bindings)
71
- assert realize(Callable[[_U], _T], bindings) == Callable[[List[int]], str]
88
+ if sys.version_info >= (3, 9):
89
+ assert (
90
+ realize(Callable[[_U], _T], bindings)
91
+ == collections.abc.Callable[[List[int]], str]
92
+ )
93
+ else:
94
+ assert realize(Callable[[_U], _T], bindings) == Callable[[List[int]], str]
72
95
 
73
96
 
74
97
  def test_plain_callable():
@@ -120,4 +143,68 @@ class Pair(Generic[_U, _T]):
120
143
  def test_bindings_from_type_arguments():
121
144
  var_mapping = get_bindings_from_type_arguments(Pair[int, str])
122
145
  assert var_mapping == {_U: int, _T: str}
123
- assert realize(List[_U], var_mapping) == List[int]
146
+ if sys.version_info >= (3, 9):
147
+ assert realize(List[_U], var_mapping) == list[int]
148
+ else:
149
+ assert realize(List[_U], var_mapping) == List[int]
150
+
151
+
152
+ def test_intersect_signatures_basic():
153
+ def f1(x: int, y: str, **kw) -> List[bool]:
154
+ return []
155
+
156
+ def f2(x: bool, *extra: str, **kw) -> List[int]:
157
+ return []
158
+
159
+ intersection = intersect_signatures(signature(f1), signature(f2))
160
+ assert intersection is not None
161
+ assert intersection.parameters == {
162
+ "x": Parameter("x", kind=Parameter.KEYWORD_ONLY, annotation=bool),
163
+ "y": Parameter("y", kind=Parameter.KEYWORD_ONLY, annotation=str),
164
+ "kw": Parameter("kw", kind=Parameter.VAR_KEYWORD),
165
+ }
166
+ assert intersection.return_annotation == List[bool]
167
+
168
+
169
+ def test_intersect_signatures_typevars():
170
+ _T = TypeVar("_T")
171
+
172
+ def f1(cc, *args, **kwds):
173
+ pass
174
+
175
+ def f2(dd, left: Optional[_T], right: Optional[_T]):
176
+ pass
177
+
178
+ intersection = intersect_signatures(signature(f1), signature(f2))
179
+ assert intersection is not None
180
+ expected = {
181
+ "dd": Parameter("dd", kind=Parameter.KEYWORD_ONLY),
182
+ "left": Parameter("left", kind=Parameter.KEYWORD_ONLY, annotation=Optional[_T]),
183
+ "right": Parameter(
184
+ "right", kind=Parameter.KEYWORD_ONLY, annotation=Optional[_T]
185
+ ),
186
+ }
187
+ assert intersection.parameters == expected
188
+
189
+
190
+ @pytest.mark.skip(
191
+ reason="The inspect module doesn't expose runtime type information yet"
192
+ )
193
+ def test_intersect_signature_with_crosshair():
194
+ def check_intersect_signatures(
195
+ sig1: Signature, sig2: Signature, pos_args: List, kw_args: Mapping[str, object]
196
+ ) -> None:
197
+ """post: True"""
198
+
199
+ def _sig_bindable(sig: Signature) -> bool:
200
+ try:
201
+ sig.bind(*pos_args, **kw_args)
202
+ return True
203
+ except TypeError:
204
+ return False
205
+
206
+ if _sig_bindable(sig1) or _sig_bindable(sig2):
207
+ intersection = intersect_signatures(sig1, sig2)
208
+ assert _sig_bindable(intersection)
209
+
210
+ check_states(check_intersect_signatures, CANNOT_CONFIRM)
crosshair/enforce.py CHANGED
@@ -36,6 +36,7 @@ def WithEnforcement(fn: Callable) -> Callable:
36
36
  Enforcement is normally disabled when calling from some internal files, for
37
37
  performance reasons. Use WithEnforcement to ensure it is enabled anywhere.
38
38
  """
39
+
39
40
  # This local function has a special name that we look for while tracing
40
41
  # (see the wants_codeobj method below):
41
42
  def _crosshair_with_enforcement(*a, **kw):
@@ -191,12 +192,6 @@ class EnforcedConditions(TracingModule):
191
192
  self.fns_enforcing: Optional[Set[Callable]] = None
192
193
  self.codeobj_cache: Dict[object, bool] = {}
193
194
 
194
- def __enter__(self): # TODO: no longer used as a context manager; remove
195
- return self
196
-
197
- def __exit__(self, exc_type, exc_val, exc_tb):
198
- return False
199
-
200
195
  @contextlib.contextmanager
201
196
  def currently_enforcing(self, fn: Callable):
202
197
  if self.fns_enforcing is None:
crosshair/enforce_test.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import abc
2
2
  import sys
3
- import unittest
4
3
  from contextlib import ExitStack
5
4
 
6
5
  import pytest
@@ -53,26 +52,25 @@ class Enforcement(ExitStack):
53
52
  super().__enter__()
54
53
  enforced_conditions = EnforcedConditions(Pep316Parser())
55
54
  self.enter_context(COMPOSITE_TRACER)
56
- self.enter_context(enforced_conditions)
57
55
  self.enter_context(enforced_conditions.enabled_enforcement())
58
56
  COMPOSITE_TRACER.trace_caller()
59
57
 
60
58
 
61
- class CoreTest(unittest.TestCase):
59
+ class TestCore:
62
60
  def test_enforce_conditions(self) -> None:
63
- self.assertEqual(foo(-1), -2) # unchecked
61
+ assert foo(-1) == -2 # unchecked
64
62
  with Enforcement():
65
- self.assertEqual(foo(50), 100)
66
- with self.assertRaises(PreconditionFailed):
63
+ assert foo(50) == 100
64
+ with pytest.raises(PreconditionFailed):
67
65
  foo(-1)
68
- with self.assertRaises(PostconditionFailed):
66
+ with pytest.raises(PostconditionFailed):
69
67
  foo(0)
70
68
 
71
69
  def test_class_enforce(self) -> None:
72
70
  Pokeable().pokeby(-1) # no exception (yet!)
73
71
  with Enforcement():
74
72
  Pokeable().poke()
75
- with self.assertRaises(PreconditionFailed):
73
+ with pytest.raises(PreconditionFailed):
76
74
  Pokeable().pokeby(-1)
77
75
 
78
76
  def test_enforce_on_uncopyable_value(self) -> None:
@@ -82,7 +80,7 @@ class CoreTest(unittest.TestCase):
82
80
 
83
81
  not_copyable = NotCopyable()
84
82
  with Enforcement():
85
- with self.assertRaises(AttributeError):
83
+ with pytest.raises(AttributeError):
86
84
  same_thing(not_copyable)
87
85
 
88
86
 
@@ -115,19 +113,19 @@ class DerivedFooable(BaseFooable):
115
113
  """pre: x > 0"""
116
114
 
117
115
 
118
- class TrickyCasesTest(unittest.TestCase):
116
+ class TestTrickyCases:
119
117
  def test_attrs_restored_properly(self) -> None:
120
118
  orig_class_dict = DerivedFooable.__dict__.copy()
121
119
  with Enforcement():
122
120
  pass
123
121
  for k, v in orig_class_dict.items():
124
- self.assertIs(
125
- DerivedFooable.__dict__[k], v, f'member "{k}" changed afer encforcement'
126
- )
122
+ assert (
123
+ DerivedFooable.__dict__[k] is v
124
+ ), f'member "{k}" changed afer encforcement'
127
125
 
128
126
  def test_enforcement_of_class_methods(self) -> None:
129
127
  with Enforcement():
130
- with self.assertRaises(PreconditionFailed):
128
+ with pytest.raises(PreconditionFailed):
131
129
  BaseFooable.class_foo(50)
132
130
  with Enforcement():
133
131
  DerivedFooable.class_foo(50)
@@ -135,14 +133,14 @@ class TrickyCasesTest(unittest.TestCase):
135
133
  def test_enforcement_of_static_methods(self) -> None:
136
134
  with Enforcement():
137
135
  DerivedFooable.static_foo(50)
138
- with self.assertRaises(PreconditionFailed):
136
+ with pytest.raises(PreconditionFailed):
139
137
  BaseFooable.static_foo(50)
140
138
 
141
139
  def test_super_method_enforced(self) -> None:
142
140
  with Enforcement():
143
- with self.assertRaises(PreconditionFailed):
141
+ with pytest.raises(PreconditionFailed):
144
142
  DerivedFooable().foo_only_in_super(50)
145
- with self.assertRaises(PreconditionFailed):
143
+ with pytest.raises(PreconditionFailed):
146
144
  DerivedFooable().foo(-1)
147
145
  # Derived class has a weaker precondition, so this is OK:
148
146
  DerivedFooable().foo(50)
@@ -182,9 +180,3 @@ def test_enforcement_init_on_abcmeta() -> None:
182
180
  with pytest.raises(PostconditionFailed):
183
181
  WithMetaclass(55)
184
182
  WithMetaclass(99)
185
-
186
-
187
- if __name__ == "__main__":
188
- if ("-v" in sys.argv) or ("--verbose" in sys.argv):
189
- set_debug(True)
190
- unittest.main()
@@ -1,4 +1,5 @@
1
1
  """Run functional tests of the tool on all the examples."""
2
+
2
3
  import argparse
3
4
  import os
4
5
  import pathlib
@@ -24,7 +25,7 @@ def extract_linenums(text: str) -> List[int]:
24
25
 
25
26
  def find_examples() -> Iterable[Path]:
26
27
  examples_dir = pathlib.Path(os.path.realpath(__file__)).parent
27
- for path in sorted(examples_dir.glob("**/*.py")):
28
+ for path in sorted(examples_dir.glob("*/**/*.py")):
28
29
  if path.stem != "__init__":
29
30
  yield path
30
31
 
crosshair/fnutil.py CHANGED
@@ -41,8 +41,7 @@ if sys.version_info >= (3, 8):
41
41
  from typing import Protocol
42
42
 
43
43
  class Descriptor(Protocol):
44
- def __get__(self, instance: object, cls: type) -> Any:
45
- ...
44
+ def __get__(self, instance: object, cls: type) -> Any: ...
46
45
 
47
46
  else:
48
47
  Descriptor = Any
@@ -344,7 +343,7 @@ def walk_paths(paths: Iterable[Path], ignore_missing=False) -> Iterable[Path]:
344
343
  else:
345
344
  raise FileNotFoundError(str(path))
346
345
  if path.is_dir():
347
- for (dirpath, _dirs, files) in os.walk(str(path)):
346
+ for dirpath, _dirs, files in os.walk(str(path)):
348
347
  for curfile in files:
349
348
  if analyzable_filename(curfile):
350
349
  yield Path(dirpath) / curfile
crosshair/fnutil_test.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import builtins
2
2
  import inspect
3
3
  import sys
4
- import unittest
5
4
  from dataclasses import dataclass
6
5
  from typing import Generic
7
6
 
@@ -74,9 +73,3 @@ def test_load_function_at_line():
74
73
 
75
74
  def test_FunctionInfo_get_callable_on_generic():
76
75
  assert FunctionInfo.from_class(Generic, "__class_getitem__").get_callable() is None
77
-
78
-
79
- if __name__ == "__main__":
80
- if ("-v" in sys.argv) or ("--verbose" in sys.argv):
81
- set_debug(True)
82
- unittest.main()
@@ -38,15 +38,16 @@ from crosshair.fnutil import resolve_signature
38
38
  from crosshair.libimpl.builtinslib import origin_of
39
39
  from crosshair.statespace import (
40
40
  CallAnalysis,
41
- CrosshairInternal,
41
+ CrossHairInternal,
42
42
  IgnoreAttempt,
43
43
  RootNode,
44
44
  StateSpace,
45
45
  StateSpaceContext,
46
46
  )
47
47
  from crosshair.stubs_parser import signature_from_stubs
48
+ from crosshair.test_util import flexible_equal
48
49
  from crosshair.tracers import COMPOSITE_TRACER, NoTracing, ResumedTracing
49
- from crosshair.util import CrosshairUnsupported, debug, type_args_of
50
+ from crosshair.util import CrosshairUnsupported, debug, is_iterable, type_args_of
50
51
 
51
52
  FUZZ_SEED = 1348
52
53
 
@@ -176,8 +177,8 @@ class TrialStatus(enum.Enum):
176
177
  class FuzzTester:
177
178
  r: random.Random
178
179
 
179
- def __init__(self, seed=None):
180
- self.r = random.Random(FUZZ_SEED if seed is None else seed)
180
+ def __init__(self, seed):
181
+ self.r = random.Random(seed)
181
182
 
182
183
  def symbolic_run(
183
184
  self,
@@ -220,15 +221,10 @@ class FuzzTester:
220
221
  return (
221
222
  None,
222
223
  symbolic_args,
223
- CrosshairInternal(f"exhausted after {itr} iterations"),
224
+ CrossHairInternal(f"exhausted after {itr} iterations"),
224
225
  space,
225
226
  )
226
- return (
227
- None,
228
- None,
229
- CrosshairInternal("Unable to find a successful symbolic execution"),
230
- space,
231
- )
227
+ raise CrossHairInternal("Unable to find a successful symbolic execution")
232
228
 
233
229
  def runexpr(self, expr, bindings):
234
230
  try:
@@ -271,86 +267,77 @@ class FuzzTester:
271
267
  ) -> object:
272
268
  for name in typed_args.keys():
273
269
  literal, symbolic = literal_args[name], symbolic_args[name]
274
- if isinstance(literal, (set, frozenset, dict)):
275
- assert isinstance(symbolic, Sized)
276
- # We need not only equality, but equal ordering, because some operations
277
- # like pop() are order-dependent:
278
- if len(literal) != len(symbolic):
279
- raise IgnoreAttempt(
280
- f'symbolic "{name}" not equal to literal "{name}"'
281
- )
282
- if isinstance(literal, Mapping):
283
- assert isinstance(symbolic, Mapping)
284
- literal, symbolic = list(literal.items()), list(
285
- symbolic.items()
286
- )
287
- else:
288
- assert isinstance(symbolic, Iterable)
289
- literal, symbolic = list(literal), list(symbolic)
270
+ with NoTracing():
271
+ # TODO: transition into general purpose SMT expr extractor
272
+ # for equality with constant
273
+ if hasattr(symbolic, "_smt_promote_literal"):
274
+ symbolic.var = symbolic._smt_promote_literal(literal) # type: ignore
275
+ elif is_iterable(literal) and is_iterable(symbolic):
276
+ with ResumedTracing():
277
+ space.add(len(literal) == len(symbolic)) # type: ignore
290
278
  if literal != symbolic:
291
279
  raise IgnoreAttempt(
292
280
  f'symbolic "{name}" not equal to literal "{name}"'
293
281
  )
282
+ if repr(literal) != repr(symbolic):
283
+ # dict/set ordering, -0.0 vs 0.0, etc
284
+ raise IgnoreAttempt(
285
+ f'symbolic "{name}" not repr-equal to literal "{name}"'
286
+ )
294
287
  return eval(expr, symbolic_args.copy())
295
288
 
296
- try:
297
- debug(f" ===== {expr} with {literal_args} ===== ")
298
- compile(expr, "<string>", "eval")
299
- postexec_literal_args = copy.deepcopy(literal_args)
300
- literal_ret, literal_exc = self.runexpr(expr, postexec_literal_args)
301
- (
302
- symbolic_ret,
303
- postexec_symbolic_args,
304
- symbolic_exc,
305
- space,
306
- ) = self.symbolic_run(symbolic_checker, typed_args)
307
- if isinstance(symbolic_exc, CrosshairUnsupported):
308
- return TrialStatus.UNSUPPORTED
309
- with Patched(), StateSpaceContext(space), COMPOSITE_TRACER, NoTracing():
310
- # compare iterators as the values they produce:
311
- with ResumedTracing():
312
- if isinstance(literal_ret, Iterable) and isinstance(
313
- symbolic_ret, Iterable
314
- ):
315
- literal_ret = list(literal_ret)
316
- symbolic_ret = list(symbolic_ret)
317
- postexec_symbolic_args = deep_realize(postexec_symbolic_args)
318
- symbolic_ret = deep_realize(symbolic_ret)
319
- symbolic_exc = deep_realize(symbolic_exc)
320
- rets_differ = realize(bool(literal_ret != symbolic_ret))
321
- postexec_args_differ = realize(
322
- bool(postexec_literal_args != postexec_symbolic_args)
323
- )
324
- if (
325
- rets_differ
326
- or postexec_args_differ
327
- or type(literal_exc) != type(symbolic_exc)
289
+ debug(f" ===== {expr} with {literal_args} ===== ")
290
+ compile(expr, "<string>", "eval")
291
+ postexec_literal_args = copy.deepcopy(literal_args)
292
+ literal_ret, literal_exc = self.runexpr(expr, postexec_literal_args)
293
+ (
294
+ symbolic_ret,
295
+ postexec_symbolic_args,
296
+ symbolic_exc,
297
+ space,
298
+ ) = self.symbolic_run(symbolic_checker, typed_args)
299
+ if isinstance(symbolic_exc, CrosshairUnsupported):
300
+ return TrialStatus.UNSUPPORTED
301
+ with Patched(), StateSpaceContext(space), COMPOSITE_TRACER, NoTracing():
302
+ # compare iterators as the values they produce:
303
+ with ResumedTracing():
304
+ if isinstance(literal_ret, Iterable) and isinstance(
305
+ symbolic_ret, Iterable
328
306
  ):
329
- debug(
330
- f" ***** BEGIN FAILURE FOR {expr} WITH {literal_args} ***** "
331
- )
332
- debug(
333
- f" ***** Expected return: {literal_ret} (exc: {type(literal_exc)} {literal_exc})"
334
- )
335
- debug(f" ***** {postexec_literal_args}")
336
- debug(
337
- f" ***** Symbolic return: {symbolic_ret} (exc: {type(symbolic_exc)} {symbolic_exc})"
338
- )
339
- debug(f" ***** {postexec_symbolic_args}")
340
- debug(f" ***** END FAILURE FOR {expr} ***** ")
341
- assert (literal_ret, literal_exc) == (symbolic_ret, symbolic_exc)
342
- debug(" OK ret= ", literal_ret, "vs", symbolic_ret)
307
+ literal_ret = list(literal_ret)
308
+ symbolic_ret = list(symbolic_ret)
309
+ postexec_symbolic_args = deep_realize(postexec_symbolic_args)
310
+ symbolic_ret = deep_realize(symbolic_ret)
311
+ symbolic_exc = deep_realize(symbolic_exc)
312
+ rets_differ = not realize(flexible_equal(literal_ret, symbolic_ret))
313
+ postexec_args_differ = realize(
314
+ bool(postexec_literal_args != postexec_symbolic_args)
315
+ )
316
+ if (
317
+ rets_differ
318
+ or postexec_args_differ
319
+ or type(literal_exc) != type(symbolic_exc)
320
+ ):
321
+ debug(f" ***** BEGIN FAILURE FOR {expr} WITH {literal_args} ***** ")
343
322
  debug(
344
- " OK exc= ",
345
- type(literal_exc),
346
- literal_exc,
347
- "vs",
348
- type(symbolic_exc),
349
- symbolic_exc,
323
+ f" ***** Expected return: {literal_ret} (exc: {type(literal_exc)} {literal_exc})"
350
324
  )
351
- except AssertionError as e:
352
- raise AssertionError(
353
- f"Mismatch while evaluating {expr} with {literal_args}: {e}"
325
+ debug(f" ***** postexec args: {postexec_literal_args}")
326
+ debug(
327
+ f" ***** Symbolic return: {symbolic_ret} (exc: {type(symbolic_exc)} {symbolic_exc})"
328
+ )
329
+ debug(f" ***** postexec args: {postexec_symbolic_args}")
330
+ debug(f" ***** END FAILURE FOR {expr} ***** ")
331
+ assert literal_ret == symbolic_ret
332
+ assert False, f"Mismatch while evaluating {expr} with {literal_args}"
333
+ debug(" OK ret= ", literal_ret, "vs", symbolic_ret)
334
+ debug(
335
+ " OK exc= ",
336
+ type(literal_exc),
337
+ literal_exc,
338
+ "vs",
339
+ type(symbolic_exc),
340
+ symbolic_exc,
354
341
  )
355
342
  return TrialStatus.NORMAL
356
343
 
@@ -406,7 +393,7 @@ def test_unary_ops(expr_str, seed) -> None:
406
393
  tester.run_trial(expr_str, arg_type_roots)
407
394
 
408
395
 
409
- @pytest.mark.parametrize("seed", range(25))
396
+ @pytest.mark.parametrize("seed", set(range(25)) - {17})
410
397
  @pytest.mark.parametrize(
411
398
  "expr_str",
412
399
  [
@@ -1,14 +1,16 @@
1
+ import sys
1
2
  from array import array
2
- from typing import BinaryIO, ByteString, Dict, Iterable, List, Sequence, Tuple
3
+ from typing import BinaryIO, Dict, Iterable, List, Sequence, Tuple
3
4
 
4
5
  import z3 # type: ignore
5
6
 
6
7
  from crosshair import SymbolicFactory, realize, register_patch
7
- from crosshair.core import CrossHairValue, register_type
8
+ from crosshair.core import register_type
8
9
  from crosshair.libimpl.builtinslib import SymbolicArrayBasedUniformTuple
9
10
  from crosshair.simplestructs import ShellMutableSequence
10
11
  from crosshair.statespace import StateSpace
11
12
  from crosshair.tracers import NoTracing
13
+ from crosshair.util import CrossHairValue
12
14
 
13
15
  INT_TYPE_BOUNDS: Dict[str, Tuple[int, int]] = {
14
16
  # (min, max) ranges - inclusive on min, exclusive on max.
@@ -28,13 +30,9 @@ INT_TYPE_BOUNDS: Dict[str, Tuple[int, int]] = {
28
30
  INT_TYPE_SIZE = {c: array(c).itemsize for c in INT_TYPE_BOUNDS.keys()}
29
31
 
30
32
 
31
- def is_bytes_like(obj: object) -> bool:
32
- return isinstance(obj, (ByteString, array))
33
-
34
-
35
33
  def pick_code(space: StateSpace) -> Tuple[str, int, int]:
36
34
  last_idx = len(INT_TYPE_BOUNDS) - 1
37
- for (idx, (code, rng)) in enumerate(INT_TYPE_BOUNDS.items()):
35
+ for idx, (code, rng) in enumerate(INT_TYPE_BOUNDS.items()):
38
36
  if idx < last_idx:
39
37
  if space.smt_fork(desc=f"not_{code}_array"):
40
38
  continue
@@ -100,6 +98,11 @@ class SymbolicArray(
100
98
  def __ch_pytype__(self):
101
99
  return array
102
100
 
101
+ def __eq__(self, other):
102
+ if not isinstance(other, array):
103
+ return False
104
+ return ShellMutableSequence.__eq__(self, other)
105
+
103
106
  def _spawn(self, items: Sequence) -> ShellMutableSequence:
104
107
  return SymbolicArray(self.typecode, items)
105
108
 
@@ -0,0 +1,30 @@
1
+ import binascii
2
+ import sys
3
+
4
+ import pytest
5
+
6
+ from crosshair.core import analyze_function, run_checkables
7
+ from crosshair.statespace import MessageType
8
+ from crosshair.test_util import compare_results
9
+
10
+
11
+ def check_b2a_base64(byts: bytes, newline: bool):
12
+ """post: _"""
13
+ return compare_results(binascii.b2a_base64, byts, newline=newline)
14
+
15
+
16
+ def check_a2b_base64(byts: bytes, strict_mode: bool):
17
+ """post: _"""
18
+ kw = {"strict_mode": strict_mode} if sys.version_info >= (3, 11) else {}
19
+ return compare_results(binascii.a2b_base64, byts, **kw)
20
+
21
+
22
+ # This is the only real test definition.
23
+ # It runs crosshair on each of the "check" functions defined above.
24
+ @pytest.mark.parametrize("fn_name", [fn for fn in dir() if fn.startswith("check_")])
25
+ def test_builtin(fn_name: str) -> None:
26
+ this_module = sys.modules[__name__]
27
+ fn = getattr(this_module, fn_name)
28
+ messages = run_checkables(analyze_function(fn))
29
+ errors = [m for m in messages if m.state > MessageType.PRE_UNSAT]
30
+ assert errors == []
@@ -0,0 +1,67 @@
1
+ import binascii
2
+ import sys
3
+ from array import array
4
+
5
+ import pytest
6
+
7
+ from crosshair.test_util import summarize_execution
8
+ from crosshair.tracers import ResumedTracing
9
+
10
+
11
+ @pytest.mark.parametrize(
12
+ "input_bytes",
13
+ [
14
+ b"", # empty
15
+ b"//==", # padding 2
16
+ b"AAA=", # padding 1
17
+ b"9999", # no padding
18
+ b"3=4=", # discontinuous padding
19
+ b"34=&=", # unexpected chars in padding
20
+ b"34=", # wrong padding
21
+ b"333====", # over-padding
22
+ b"This/12+yearOld/Fox=", # valid, long
23
+ "", # empty string
24
+ "9009", # nonempty string
25
+ "\u2165", # unicode string
26
+ ],
27
+ )
28
+ @pytest.mark.parametrize("strict_mode", [True, False])
29
+ def test_base64_decode(space, input_bytes, strict_mode):
30
+ kw = {"strict_mode": strict_mode} if sys.version_info >= (3, 11) else {}
31
+ concrete_result = summarize_execution(
32
+ binascii.a2b_base64, (input_bytes,), kw, detach_path=False
33
+ )
34
+ with ResumedTracing():
35
+ symbolic_result = summarize_execution(
36
+ binascii.a2b_base64, (input_bytes,), kw, detach_path=False
37
+ )
38
+ assert concrete_result == symbolic_result
39
+
40
+
41
+ @pytest.mark.parametrize("newline", [True, False])
42
+ @pytest.mark.parametrize(
43
+ "input_bytes",
44
+ [
45
+ b"", # empty
46
+ b"H",
47
+ b"Ha",
48
+ b"Hai",
49
+ b"Hair",
50
+ "", # empty string
51
+ "Hair", # nonempty string
52
+ "\u2165", # unicode string
53
+ bytearray(b"Hair"), # bytearray
54
+ memoryview(b"Hair"), # memoryview
55
+ array("B", b"Hair"), # array
56
+ ],
57
+ )
58
+ def test_base64_encode(space, input_bytes, newline):
59
+ kw = {"newline": newline}
60
+ concrete_result = summarize_execution(
61
+ binascii.b2a_base64, (input_bytes,), kw, detach_path=False
62
+ )
63
+ with ResumedTracing():
64
+ symbolic_result = summarize_execution(
65
+ binascii.b2a_base64, (input_bytes,), kw, detach_path=False
66
+ )
67
+ assert concrete_result == symbolic_result