intrepyd 0.13.0__py3-none-win_amd64.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.
- intrepyd/LICENSE.intrepid +60 -0
- intrepyd/__init__.py +4 -0
- intrepyd/api.py +236 -0
- intrepyd/atg/__init__.py +3 -0
- intrepyd/atg/circuit.py +33 -0
- intrepyd/atg/mcdc.py +293 -0
- intrepyd/circuit.py +79 -0
- intrepyd/colors.py +55 -0
- intrepyd/components/__init__.py +3 -0
- intrepyd/components/eda.py +276 -0
- intrepyd/components.py +55 -0
- intrepyd/config.py +47 -0
- intrepyd/context.py +496 -0
- intrepyd/engine.py +286 -0
- intrepyd/formula2py/FormulaLexer.py +84 -0
- intrepyd/formula2py/FormulaParser.py +424 -0
- intrepyd/formula2py/FormulaVisitor.py +48 -0
- intrepyd/formula2py/encoder.py +100 -0
- intrepyd/formula2py/formulabuilder.py +51 -0
- intrepyd/formula2py/parser.py +22 -0
- intrepyd/iec611312py/IEC61131ParserLexer.py +1616 -0
- intrepyd/iec611312py/IEC61131ParserParser.py +6735 -0
- intrepyd/iec611312py/IEC61131ParserVisitor.py +513 -0
- intrepyd/iec611312py/__init__.py +3 -0
- intrepyd/iec611312py/datatype.py +103 -0
- intrepyd/iec611312py/expression.py +171 -0
- intrepyd/iec611312py/flatstmt2intrepyd.py +174 -0
- intrepyd/iec611312py/flattener.py +105 -0
- intrepyd/iec611312py/function.py +44 -0
- intrepyd/iec611312py/functionblock.py +32 -0
- intrepyd/iec611312py/inferdatatype.py +168 -0
- intrepyd/iec611312py/parsest.py +20 -0
- intrepyd/iec611312py/plcopen.py +111 -0
- intrepyd/iec611312py/statement.py +94 -0
- intrepyd/iec611312py/stmtbuilder.py +238 -0
- intrepyd/iec611312py/stmtprinter.py +95 -0
- intrepyd/iec611312py/summarizer.py +78 -0
- intrepyd/iec611312py/translator.py +298 -0
- intrepyd/iec611312py/utils.py +6 -0
- intrepyd/iec611312py/variable.py +34 -0
- intrepyd/iec611312py/visitor.py +88 -0
- intrepyd/intrepid.dll +0 -0
- intrepyd/lustre2py/LustreLexer.py +309 -0
- intrepyd/lustre2py/LustreParser.py +2811 -0
- intrepyd/lustre2py/LustreVisitor.py +243 -0
- intrepyd/lustre2py/__init__.py +3 -0
- intrepyd/lustre2py/ast.py +17 -0
- intrepyd/lustre2py/ast2intrepyd.py +332 -0
- intrepyd/lustre2py/astbuilder.py +180 -0
- intrepyd/lustre2py/astprinter.py +140 -0
- intrepyd/lustre2py/datatype.py +53 -0
- intrepyd/lustre2py/expression.py +219 -0
- intrepyd/lustre2py/instruction.py +115 -0
- intrepyd/lustre2py/node.py +247 -0
- intrepyd/lustre2py/parser.py +22 -0
- intrepyd/lustre2py/translator.py +142 -0
- intrepyd/lustre2py/variable.py +27 -0
- intrepyd/lustre2py/visitor.py +107 -0
- intrepyd/parser.py +209 -0
- intrepyd/plots.py +114 -0
- intrepyd/pseudoboolean.py +101 -0
- intrepyd/scr.py +118 -0
- intrepyd/simulator.py +27 -0
- intrepyd/tools.py +61 -0
- intrepyd/trace.py +125 -0
- intrepyd/visitable.py +14 -0
- intrepyd-0.13.0.dist-info/METADATA +76 -0
- intrepyd-0.13.0.dist-info/RECORD +73 -0
- intrepyd-0.13.0.dist-info/WHEEL +5 -0
- intrepyd-0.13.0.dist-info/licenses/CREDITS.md +17 -0
- intrepyd-0.13.0.dist-info/licenses/LICENSE.md +35 -0
- intrepyd-0.13.0.dist-info/licenses/intrepyd/LICENSE.intrepid +60 -0
- intrepyd-0.13.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Intrepid Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2016-2026 Roberto Bruttomesso
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
This license applies to the contents of the Intrepid repository and all of
|
|
7
|
+
its subdirectories (the "Software"), including source code, build scripts,
|
|
8
|
+
interface definitions, files generated from them, and any binary obtained by
|
|
9
|
+
compiling them.
|
|
10
|
+
|
|
11
|
+
1. No rights granted. The Software is the exclusive property of Roberto
|
|
12
|
+
Bruttomesso (the "Author"). Except as stated in section 7, or as expressly
|
|
13
|
+
authorized in advance and in writing by the Author, no permission is
|
|
14
|
+
granted to any person to use, run, copy, compile, modify, translate,
|
|
15
|
+
adapt, merge, publish, distribute, sublicense, sell, rent, lend, host, or
|
|
16
|
+
otherwise make available the Software, in whole or in part, nor to create
|
|
17
|
+
derivative works based on it.
|
|
18
|
+
|
|
19
|
+
2. Explicit consent required. Any authorization must be given explicitly and
|
|
20
|
+
in writing by the Author, and is limited to the scope, duration, and
|
|
21
|
+
purpose stated in it. Authorization cannot be inferred from conduct, from
|
|
22
|
+
access to the Software, or from the Software being publicly visible.
|
|
23
|
+
|
|
24
|
+
3. No reverse engineering. Decompiling, disassembling, or reverse engineering
|
|
25
|
+
any binary form of the Software is forbidden, except to the extent that
|
|
26
|
+
applicable law expressly permits it notwithstanding this restriction.
|
|
27
|
+
|
|
28
|
+
4. Notices. Copyright and license notices in the Software must not be
|
|
29
|
+
removed or altered.
|
|
30
|
+
|
|
31
|
+
5. Third party software. The Software is built against third party
|
|
32
|
+
components (such as Z3, googletest and nlohmann/json) that are downloaded
|
|
33
|
+
at build time and are not part of the Software. Those components remain
|
|
34
|
+
under their own licenses.
|
|
35
|
+
|
|
36
|
+
6. No warranty. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
|
37
|
+
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
38
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
39
|
+
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
40
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
41
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
42
|
+
DEALINGS IN THE SOFTWARE.
|
|
43
|
+
|
|
44
|
+
7. Permission for intrepyd. As an exception to sections 1 and 2, the Author
|
|
45
|
+
grants everyone a free, worldwide, non-exclusive, royalty-free permission
|
|
46
|
+
to use, run, copy and redistribute the compiled libraries of the Software
|
|
47
|
+
(libintrepid.so, libintrepid.dylib, intrepid.dll, and their static
|
|
48
|
+
counterparts), for any purpose, including commercial ones, provided that:
|
|
49
|
+
|
|
50
|
+
a. they are used as part of the intrepyd project
|
|
51
|
+
(https://github.com/formalmethods/intrepyd, and the "intrepyd"
|
|
52
|
+
package on PyPI), or of a modified version of it, through the
|
|
53
|
+
interfaces intrepyd provides;
|
|
54
|
+
|
|
55
|
+
b. they are redistributed unmodified, and together with this license.
|
|
56
|
+
|
|
57
|
+
This permission does not cover the source code of the Software, nor using
|
|
58
|
+
the libraries on their own, outside of intrepyd. Section 3 still applies.
|
|
59
|
+
|
|
60
|
+
Requests for authorization: roberto.bruttomesso@gmail.com
|
intrepyd/__init__.py
ADDED
intrepyd/api.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Python binding of the intrepid backend.
|
|
3
|
+
|
|
4
|
+
intrepid is a C library (libintrepid.so on Linux, intrepid.dll on Windows),
|
|
5
|
+
built and released from its own repository. This module loads it with ctypes
|
|
6
|
+
and exposes each function of its C API, src/api/Intrepid.h, under the same
|
|
7
|
+
name, so that
|
|
8
|
+
|
|
9
|
+
from intrepyd.api import mk_ctx, mk_boolean_type
|
|
10
|
+
|
|
11
|
+
behaves as it did with the SWIG extension this module replaces:
|
|
12
|
+
|
|
13
|
+
- strings are passed and returned as str;
|
|
14
|
+
- handles (contexts, types, engines, traces, simulators) are opaque values,
|
|
15
|
+
None when the library returns NULL; nets are ints;
|
|
16
|
+
- an error reported by the library raises RuntimeError with its message.
|
|
17
|
+
|
|
18
|
+
The library is looked up first in the INTREPID_LIBRARY environment variable,
|
|
19
|
+
which must be the path of the library file, and then next to this module,
|
|
20
|
+
which is where the packaged intrepyd ships it.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import ctypes
|
|
24
|
+
import os
|
|
25
|
+
import sys
|
|
26
|
+
|
|
27
|
+
__all__ = ['INT_ENGINE_RESULT_UNKNOWN', 'INT_ENGINE_RESULT_REACHABLE',
|
|
28
|
+
'INT_ENGINE_RESULT_UNREACHABLE', 'FUNCTIONS', 'LIBRARY_PATH']
|
|
29
|
+
|
|
30
|
+
# intrepid is built for Linux and Windows only
|
|
31
|
+
if sys.platform == 'win32':
|
|
32
|
+
_LIBRARY_NAME = 'intrepid.dll'
|
|
33
|
+
else:
|
|
34
|
+
_LIBRARY_NAME = 'libintrepid.so'
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _find_library():
|
|
38
|
+
path = os.environ.get('INTREPID_LIBRARY')
|
|
39
|
+
if path:
|
|
40
|
+
if not os.path.isfile(path):
|
|
41
|
+
raise ImportError('INTREPID_LIBRARY is set to %s, which is not a file' % path)
|
|
42
|
+
return path
|
|
43
|
+
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), _LIBRARY_NAME)
|
|
44
|
+
if not os.path.isfile(path):
|
|
45
|
+
raise ImportError(
|
|
46
|
+
'Cannot find the intrepid library %s. In a source checkout, run '
|
|
47
|
+
'"make fetch_intrepid", or point INTREPID_LIBRARY at a local build '
|
|
48
|
+
'of intrepid.' % path)
|
|
49
|
+
return path
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
LIBRARY_PATH = _find_library()
|
|
53
|
+
_lib = ctypes.CDLL(LIBRARY_PATH)
|
|
54
|
+
|
|
55
|
+
# Values of the Int_engine_result enum
|
|
56
|
+
INT_ENGINE_RESULT_UNKNOWN = 0
|
|
57
|
+
INT_ENGINE_RESULT_REACHABLE = 1
|
|
58
|
+
INT_ENGINE_RESULT_UNREACHABLE = 2
|
|
59
|
+
|
|
60
|
+
# The C types used by the API
|
|
61
|
+
HANDLE = ctypes.c_void_p # Int_ctx, Int_type, Int_engine_*, Int_trace, ...
|
|
62
|
+
UINT = ctypes.c_uint # Int_net, unsigned
|
|
63
|
+
INT = ctypes.c_int # Int_engine_result
|
|
64
|
+
STR = ctypes.c_char_p # const char* argument, char* result
|
|
65
|
+
CHAR = ctypes.c_char # char result
|
|
66
|
+
VOID = None
|
|
67
|
+
|
|
68
|
+
# Every function of Intrepid.h, as name: (result type, argument types). It is
|
|
69
|
+
# filled in by _bind, below, and checked against the header by
|
|
70
|
+
# intrepyd/tests/test_api.py.
|
|
71
|
+
FUNCTIONS = {}
|
|
72
|
+
|
|
73
|
+
_check_exception = _lib.check_exception
|
|
74
|
+
_check_exception.restype = STR
|
|
75
|
+
_check_exception.argtypes = ()
|
|
76
|
+
_clear_exception = _lib.clear_exception
|
|
77
|
+
_clear_exception.restype = VOID
|
|
78
|
+
_clear_exception.argtypes = ()
|
|
79
|
+
FUNCTIONS['check_exception'] = (STR, ())
|
|
80
|
+
FUNCTIONS['clear_exception'] = (VOID, ())
|
|
81
|
+
__all__ += ['check_exception', 'clear_exception']
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _encode(value):
|
|
85
|
+
return value.encode() if isinstance(value, str) else value
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _bind(name, restype, argtypes):
|
|
89
|
+
FUNCTIONS[name] = (restype, argtypes)
|
|
90
|
+
__all__.append(name)
|
|
91
|
+
function = getattr(_lib, name)
|
|
92
|
+
function.restype = restype
|
|
93
|
+
function.argtypes = argtypes
|
|
94
|
+
string_args = [i for i, argtype in enumerate(argtypes) if argtype is STR]
|
|
95
|
+
decode = restype in (STR, CHAR)
|
|
96
|
+
|
|
97
|
+
# The library records an error rather than throwing, and the record stays
|
|
98
|
+
# until cleared: it is checked after each call, and cleared before raising
|
|
99
|
+
# so that the next call starts afresh.
|
|
100
|
+
def wrapper(*args):
|
|
101
|
+
if string_args:
|
|
102
|
+
args = list(args)
|
|
103
|
+
for i in string_args:
|
|
104
|
+
if i < len(args):
|
|
105
|
+
args[i] = _encode(args[i])
|
|
106
|
+
result = function(*args)
|
|
107
|
+
error = _check_exception()
|
|
108
|
+
if error is not None:
|
|
109
|
+
_clear_exception()
|
|
110
|
+
raise RuntimeError(error.decode(errors='replace'))
|
|
111
|
+
if decode and result is not None:
|
|
112
|
+
return result.decode(errors='replace')
|
|
113
|
+
return result
|
|
114
|
+
|
|
115
|
+
wrapper.__name__ = name
|
|
116
|
+
wrapper.__qualname__ = name
|
|
117
|
+
return wrapper
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# check_exception and clear_exception are exposed as they are: wrapping them
|
|
121
|
+
# would clear the very error they are asked about.
|
|
122
|
+
def check_exception():
|
|
123
|
+
"""Returns the message of the last error, or None if there was none"""
|
|
124
|
+
error = _check_exception()
|
|
125
|
+
return None if error is None else error.decode(errors='replace')
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def clear_exception():
|
|
129
|
+
"""Forgets the last error"""
|
|
130
|
+
_clear_exception()
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
mk_ctx = _bind('mk_ctx', HANDLE, ())
|
|
134
|
+
del_ctx = _bind('del_ctx', VOID, (HANDLE,))
|
|
135
|
+
throw_exception = _bind('throw_exception', VOID, (STR,))
|
|
136
|
+
push_namespace = _bind('push_namespace', VOID, (HANDLE, STR))
|
|
137
|
+
pop_namespace = _bind('pop_namespace', VOID, (HANDLE,))
|
|
138
|
+
mk_engine_bmc = _bind('mk_engine_bmc', HANDLE, (HANDLE,))
|
|
139
|
+
set_bmc_current_depth = _bind('set_bmc_current_depth', VOID, (HANDLE, UINT))
|
|
140
|
+
set_bmc_optimize = _bind('set_bmc_optimize', VOID, (HANDLE,))
|
|
141
|
+
set_bmc_allow_targets_at_any_depth = _bind('set_bmc_allow_targets_at_any_depth', VOID, (HANDLE,))
|
|
142
|
+
set_bmc_use_induction = _bind('set_bmc_use_induction', VOID, (HANDLE,))
|
|
143
|
+
set_bmc_use_attack_path_axioms = _bind('set_bmc_use_attack_path_axioms', VOID, (HANDLE, HANDLE, UINT, UINT))
|
|
144
|
+
bmc_add_target = _bind('bmc_add_target', VOID, (HANDLE, HANDLE, UINT))
|
|
145
|
+
bmc_add_watch = _bind('bmc_add_watch', VOID, (HANDLE, HANDLE, UINT))
|
|
146
|
+
bmc_reach_targets = _bind('bmc_reach_targets', INT, (HANDLE,))
|
|
147
|
+
bmc_remove_last_reached_targets = _bind('bmc_remove_last_reached_targets', VOID, (HANDLE,))
|
|
148
|
+
bmc_last_reached_targets_number = _bind('bmc_last_reached_targets_number', UINT, (HANDLE,))
|
|
149
|
+
bmc_last_reached_target = _bind('bmc_last_reached_target', UINT, (HANDLE, UINT))
|
|
150
|
+
bmc_get_trace = _bind('bmc_get_trace', HANDLE, (HANDLE, HANDLE, UINT))
|
|
151
|
+
mk_engine_br = _bind('mk_engine_br', HANDLE, (HANDLE,))
|
|
152
|
+
br_add_target = _bind('br_add_target', VOID, (HANDLE, HANDLE, UINT))
|
|
153
|
+
br_add_watch = _bind('br_add_watch', VOID, (HANDLE, HANDLE, UINT))
|
|
154
|
+
br_reach_targets = _bind('br_reach_targets', INT, (HANDLE,))
|
|
155
|
+
br_remove_last_reached_targets = _bind('br_remove_last_reached_targets', VOID, (HANDLE,))
|
|
156
|
+
br_last_reached_targets_number = _bind('br_last_reached_targets_number', UINT, (HANDLE,))
|
|
157
|
+
br_last_reached_target = _bind('br_last_reached_target', UINT, (HANDLE, UINT))
|
|
158
|
+
br_get_trace = _bind('br_get_trace', HANDLE, (HANDLE, HANDLE, UINT))
|
|
159
|
+
mk_engine_pdr = _bind('mk_engine_pdr', HANDLE, (HANDLE,))
|
|
160
|
+
pdr_add_target = _bind('pdr_add_target', VOID, (HANDLE, HANDLE, UINT))
|
|
161
|
+
pdr_add_watch = _bind('pdr_add_watch', VOID, (HANDLE, HANDLE, UINT))
|
|
162
|
+
pdr_reach_targets = _bind('pdr_reach_targets', INT, (HANDLE,))
|
|
163
|
+
pdr_remove_last_reached_targets = _bind('pdr_remove_last_reached_targets', VOID, (HANDLE,))
|
|
164
|
+
pdr_last_reached_targets_number = _bind('pdr_last_reached_targets_number', UINT, (HANDLE,))
|
|
165
|
+
pdr_last_reached_target = _bind('pdr_last_reached_target', UINT, (HANDLE, UINT))
|
|
166
|
+
pdr_get_trace = _bind('pdr_get_trace', HANDLE, (HANDLE, HANDLE, UINT))
|
|
167
|
+
trace_prepare_value_for_net = _bind('trace_prepare_value_for_net', UINT, (HANDLE, HANDLE, UINT, UINT))
|
|
168
|
+
prepare_value_for_net = _bind('prepare_value_for_net', UINT, (HANDLE, UINT))
|
|
169
|
+
value_at = _bind('value_at', CHAR, (UINT,))
|
|
170
|
+
trace_get_max_depth = _bind('trace_get_max_depth', UINT, (HANDLE,))
|
|
171
|
+
mk_trace = _bind('mk_trace', HANDLE, (HANDLE,))
|
|
172
|
+
trace_set_value = _bind('trace_set_value', VOID, (HANDLE, HANDLE, UINT, UINT, STR))
|
|
173
|
+
trace_get_watched_nets_number = _bind('trace_get_watched_nets_number', UINT, (HANDLE,))
|
|
174
|
+
trace_get_watched_net = _bind('trace_get_watched_net', UINT, (HANDLE, UINT))
|
|
175
|
+
mk_simulator = _bind('mk_simulator', HANDLE, (HANDLE,))
|
|
176
|
+
simulator_add_watch = _bind('simulator_add_watch', VOID, (HANDLE, HANDLE, UINT))
|
|
177
|
+
simulator_simulate = _bind('simulator_simulate', VOID, (HANDLE, HANDLE, UINT))
|
|
178
|
+
mk_boolean_type = _bind('mk_boolean_type', HANDLE, (HANDLE,))
|
|
179
|
+
mk_int8_type = _bind('mk_int8_type', HANDLE, (HANDLE,))
|
|
180
|
+
mk_int16_type = _bind('mk_int16_type', HANDLE, (HANDLE,))
|
|
181
|
+
mk_int32_type = _bind('mk_int32_type', HANDLE, (HANDLE,))
|
|
182
|
+
mk_int64_type = _bind('mk_int64_type', HANDLE, (HANDLE,))
|
|
183
|
+
mk_uint8_type = _bind('mk_uint8_type', HANDLE, (HANDLE,))
|
|
184
|
+
mk_uint16_type = _bind('mk_uint16_type', HANDLE, (HANDLE,))
|
|
185
|
+
mk_uint32_type = _bind('mk_uint32_type', HANDLE, (HANDLE,))
|
|
186
|
+
mk_uint64_type = _bind('mk_uint64_type', HANDLE, (HANDLE,))
|
|
187
|
+
mk_int_type = _bind('mk_int_type', HANDLE, (HANDLE,))
|
|
188
|
+
mk_real_type = _bind('mk_real_type', HANDLE, (HANDLE,))
|
|
189
|
+
mk_float16_type = _bind('mk_float16_type', HANDLE, (HANDLE,))
|
|
190
|
+
mk_float32_type = _bind('mk_float32_type', HANDLE, (HANDLE,))
|
|
191
|
+
mk_float64_type = _bind('mk_float64_type', HANDLE, (HANDLE,))
|
|
192
|
+
mk_undef = _bind('mk_undef', UINT, (HANDLE,))
|
|
193
|
+
mk_true = _bind('mk_true', UINT, (HANDLE,))
|
|
194
|
+
mk_false = _bind('mk_false', UINT, (HANDLE,))
|
|
195
|
+
mk_number = _bind('mk_number', UINT, (HANDLE, STR, HANDLE))
|
|
196
|
+
mk_not = _bind('mk_not', UINT, (HANDLE, UINT))
|
|
197
|
+
mk_and = _bind('mk_and', UINT, (HANDLE, UINT, UINT))
|
|
198
|
+
mk_or = _bind('mk_or', UINT, (HANDLE, UINT, UINT))
|
|
199
|
+
mk_xor = _bind('mk_xor', UINT, (HANDLE, UINT, UINT))
|
|
200
|
+
mk_ite = _bind('mk_ite', UINT, (HANDLE, UINT, UINT, UINT))
|
|
201
|
+
mk_iff = _bind('mk_iff', UINT, (HANDLE, UINT, UINT))
|
|
202
|
+
mk_minus = _bind('mk_minus', UINT, (HANDLE, UINT))
|
|
203
|
+
mk_add = _bind('mk_add', UINT, (HANDLE, UINT, UINT))
|
|
204
|
+
mk_sub = _bind('mk_sub', UINT, (HANDLE, UINT, UINT))
|
|
205
|
+
mk_mul = _bind('mk_mul', UINT, (HANDLE, UINT, UINT))
|
|
206
|
+
mk_div = _bind('mk_div', UINT, (HANDLE, UINT, UINT))
|
|
207
|
+
mk_mod = _bind('mk_mod', UINT, (HANDLE, UINT, UINT))
|
|
208
|
+
mk_eq = _bind('mk_eq', UINT, (HANDLE, UINT, UINT))
|
|
209
|
+
mk_leq = _bind('mk_leq', UINT, (HANDLE, UINT, UINT))
|
|
210
|
+
mk_lt = _bind('mk_lt', UINT, (HANDLE, UINT, UINT))
|
|
211
|
+
mk_geq = _bind('mk_geq', UINT, (HANDLE, UINT, UINT))
|
|
212
|
+
mk_gt = _bind('mk_gt', UINT, (HANDLE, UINT, UINT))
|
|
213
|
+
mk_neq = _bind('mk_neq', UINT, (HANDLE, UINT, UINT))
|
|
214
|
+
mk_input = _bind('mk_input', UINT, (HANDLE, STR, HANDLE))
|
|
215
|
+
mk_output = _bind('mk_output', VOID, (HANDLE, UINT))
|
|
216
|
+
get_bit = _bind('get_bit', UINT, (HANDLE, UINT, UINT))
|
|
217
|
+
set_bit = _bind('set_bit', UINT, (HANDLE, UINT, UINT, UINT))
|
|
218
|
+
mk_assumption = _bind('mk_assumption', VOID, (HANDLE, UINT))
|
|
219
|
+
push_assumption = _bind('push_assumption', VOID, (HANDLE, UINT))
|
|
220
|
+
pop_assumption = _bind('pop_assumption', VOID, (HANDLE,))
|
|
221
|
+
mk_latch = _bind('mk_latch', UINT, (HANDLE, STR, HANDLE))
|
|
222
|
+
set_latch_init_next = _bind('set_latch_init_next', VOID, (HANDLE, UINT, UINT, UINT))
|
|
223
|
+
mk_substitute = _bind('mk_substitute', UINT, (HANDLE, UINT, UINT, UINT))
|
|
224
|
+
mk_cast_to_int8 = _bind('mk_cast_to_int8', UINT, (HANDLE, UINT))
|
|
225
|
+
mk_cast_to_int16 = _bind('mk_cast_to_int16', UINT, (HANDLE, UINT))
|
|
226
|
+
mk_cast_to_int32 = _bind('mk_cast_to_int32', UINT, (HANDLE, UINT))
|
|
227
|
+
mk_cast_to_int64 = _bind('mk_cast_to_int64', UINT, (HANDLE, UINT))
|
|
228
|
+
mk_cast_to_uint8 = _bind('mk_cast_to_uint8', UINT, (HANDLE, UINT))
|
|
229
|
+
mk_cast_to_uint16 = _bind('mk_cast_to_uint16', UINT, (HANDLE, UINT))
|
|
230
|
+
mk_cast_to_uint32 = _bind('mk_cast_to_uint32', UINT, (HANDLE, UINT))
|
|
231
|
+
mk_cast_to_uint64 = _bind('mk_cast_to_uint64', UINT, (HANDLE, UINT))
|
|
232
|
+
apitrace_dump_to_file = _bind('apitrace_dump_to_file', VOID, (STR,))
|
|
233
|
+
apitrace_print_to_stdout = _bind('apitrace_print_to_stdout', VOID, ())
|
|
234
|
+
apitrace_print_to_stderr = _bind('apitrace_print_to_stderr', VOID, ())
|
|
235
|
+
|
|
236
|
+
_clear_exception()
|
intrepyd/atg/__init__.py
ADDED
intrepyd/atg/circuit.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Implementation of Circuit class for ATG
|
|
3
|
+
"""
|
|
4
|
+
class Circuit:
|
|
5
|
+
"""
|
|
6
|
+
Abstract interface for a circuit to use for ATG
|
|
7
|
+
"""
|
|
8
|
+
def __init__(self, ctx, name):
|
|
9
|
+
self._ctx = ctx
|
|
10
|
+
self._name = name
|
|
11
|
+
|
|
12
|
+
def mk_circuit(self):
|
|
13
|
+
"""
|
|
14
|
+
Makes the circuit
|
|
15
|
+
"""
|
|
16
|
+
self._ctx.push_namespace(self._name)
|
|
17
|
+
self._mk_circuit_impl()
|
|
18
|
+
self._ctx.pop_namespace()
|
|
19
|
+
|
|
20
|
+
def _mk_circuit_impl(self):
|
|
21
|
+
raise NotImplementedError('Should have implemented this')
|
|
22
|
+
|
|
23
|
+
def inputs(self):
|
|
24
|
+
"""
|
|
25
|
+
Returns the dictionary of inputs
|
|
26
|
+
"""
|
|
27
|
+
raise NotImplementedError('Should have implemented this')
|
|
28
|
+
|
|
29
|
+
def nets(self):
|
|
30
|
+
"""
|
|
31
|
+
Returns the dictionary of nets
|
|
32
|
+
"""
|
|
33
|
+
raise NotImplementedError('Should have implemented this')
|
intrepyd/atg/mcdc.py
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module implements a toolbox for Automated Test Generation
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from intrepyd.engine import EngineResult
|
|
7
|
+
|
|
8
|
+
def compute_mcdc(context, class_, decisions, max_depth):
|
|
9
|
+
"""
|
|
10
|
+
Computes MC/DC tests in form of a table.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
context: the intrepyd context to use
|
|
14
|
+
class_ (class): a python class that defines the circuit
|
|
15
|
+
decisions (Dictionary): each key of the dictionary is the
|
|
16
|
+
name of a decision, and the values are the
|
|
17
|
+
corresponding conditions
|
|
18
|
+
|
|
19
|
+
{ netName : [netName, ...] }
|
|
20
|
+
^ ^
|
|
21
|
+
the decision the list of conditions
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
(Dictionary) the MC/DC tables, one per each decision
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# Fetches and duplicates the circuit
|
|
28
|
+
inst_a = class_(context, 'InstA')
|
|
29
|
+
inst_b = class_(context, 'InstB')
|
|
30
|
+
inst_a.mk_circuit()
|
|
31
|
+
inst_b.mk_circuit()
|
|
32
|
+
|
|
33
|
+
# Creates test objectives
|
|
34
|
+
decision2testobjectives = {decision :\
|
|
35
|
+
compute_mcdc_targets(context, inst_a, inst_b, decision, conditions)\
|
|
36
|
+
for decision, conditions in decisions.items()}
|
|
37
|
+
|
|
38
|
+
# Compute MC/DC traces: this is the computationally
|
|
39
|
+
# expensive part that calls model checking routines
|
|
40
|
+
decision2traces, trace2condition, decision2unreachable =\
|
|
41
|
+
solve_mcdc_targets(context, decision2testobjectives, decisions, max_depth)
|
|
42
|
+
|
|
43
|
+
# Compute MC/DC tables from traces
|
|
44
|
+
decision2table, decision2independencepairs =\
|
|
45
|
+
compute_mcdc_tables(context, inst_a, inst_b,\
|
|
46
|
+
decision2traces, trace2condition, decisions)
|
|
47
|
+
|
|
48
|
+
return decision2table, decision2independencepairs, decision2unreachable
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def compute_mcdc_targets(context, inst_a, inst_b, decision, conditions):
|
|
52
|
+
"""
|
|
53
|
+
Computes the MC/DC reachability target for the
|
|
54
|
+
two copies of the circuit.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
context: the intrepyd context to use
|
|
58
|
+
inst_a: an instance of the circuit
|
|
59
|
+
inst_b: an instance of the circuit
|
|
60
|
+
decision: the name of a decision
|
|
61
|
+
conditions: the list of names of the conditions
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
a map from targets to conditions for which target is an independence pair
|
|
65
|
+
"""
|
|
66
|
+
decision_a = inst_a.nets()[decision]
|
|
67
|
+
decision_b = inst_b.nets()[decision]
|
|
68
|
+
decisiona_diff_decisionb = context.mk_neq(decision_a, decision_b)
|
|
69
|
+
|
|
70
|
+
conditiona_neq_conditionb = []
|
|
71
|
+
conditions_a = []
|
|
72
|
+
conditions_b = []
|
|
73
|
+
for condition in conditions:
|
|
74
|
+
condition_a = inst_a.nets()[condition]
|
|
75
|
+
conditions_a.append(condition_a)
|
|
76
|
+
condition_b = inst_b.nets()[condition]
|
|
77
|
+
conditions_b.append(condition_b)
|
|
78
|
+
conditiona_neq_conditionb.append(context.mk_neq(condition_a, condition_b))
|
|
79
|
+
|
|
80
|
+
targets = {}
|
|
81
|
+
for i in range(len(conditions)):
|
|
82
|
+
# Building test objective for the i-th condition
|
|
83
|
+
condition_a = conditions_a[i]
|
|
84
|
+
condition_b = conditions_b[i]
|
|
85
|
+
# i-th condition must differ in A and B
|
|
86
|
+
conj1 = conditiona_neq_conditionb[i]
|
|
87
|
+
# test objective must differ in A and B
|
|
88
|
+
conj2 = decisiona_diff_decisionb
|
|
89
|
+
# decisionA[not(cA)/cA] != decisionA
|
|
90
|
+
not_condition_a = context.mk_not(condition_a)
|
|
91
|
+
decisiona_not_ca = context.mk_substitute(decision_a, not_condition_a, condition_a)
|
|
92
|
+
conj3 = context.mk_neq(decisiona_not_ca, decision_a)
|
|
93
|
+
# decisionB[not(cB)/cB] != decisionB
|
|
94
|
+
not_condition_b = context.mk_not(condition_b)
|
|
95
|
+
decisiona_not_cb = context.mk_substitute(decision_b, not_condition_b, condition_b)
|
|
96
|
+
conj4 = context.mk_neq(decisiona_not_cb, decision_b)
|
|
97
|
+
# Creates final conjunction
|
|
98
|
+
tmp1 = context.mk_and(conj1, conj2)
|
|
99
|
+
tmp2 = context.mk_and(tmp1, conj3)
|
|
100
|
+
target = context.mk_and(tmp2, conj4)
|
|
101
|
+
targets[target] = i
|
|
102
|
+
|
|
103
|
+
return targets
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def solve_mcdc_targets(context, decision2testobjectives, decisions, max_depth):
|
|
107
|
+
"""
|
|
108
|
+
Solves the MC/DC targets, maximizing the number of solved
|
|
109
|
+
test objectives per each call.
|
|
110
|
+
"""
|
|
111
|
+
# Bmc engine will be used to compute counterexamples
|
|
112
|
+
bmc = context.mk_optimizing_bmc()
|
|
113
|
+
|
|
114
|
+
testobjectives2decision = {}
|
|
115
|
+
testobjectives2condition = {}
|
|
116
|
+
decision2traces = {}
|
|
117
|
+
decision2unreachable = {}
|
|
118
|
+
targets = []
|
|
119
|
+
for decision, tos in decision2testobjectives.items():
|
|
120
|
+
decision2traces[decision] = []
|
|
121
|
+
decision2unreachable[decision] = []
|
|
122
|
+
for test_objective, condition in tos.items():
|
|
123
|
+
testobjectives2decision[test_objective] = decision
|
|
124
|
+
testobjectives2condition[test_objective] = condition
|
|
125
|
+
targets.append(test_objective)
|
|
126
|
+
assert len(testobjectives2decision) == len(testobjectives2condition)
|
|
127
|
+
total_targets = len(targets)
|
|
128
|
+
|
|
129
|
+
# Compute unreachable targets first
|
|
130
|
+
total_unreached = 0
|
|
131
|
+
total_reached = 0
|
|
132
|
+
for target in targets:
|
|
133
|
+
breach = context.mk_backward_reach()
|
|
134
|
+
breach.add_target(target)
|
|
135
|
+
result = breach.reach_targets()
|
|
136
|
+
if result == EngineResult.UNREACHABLE:
|
|
137
|
+
decision = testobjectives2decision[target]
|
|
138
|
+
condition = decision2testobjectives[decision][target]
|
|
139
|
+
condition = decisions[decision][condition]
|
|
140
|
+
decision2unreachable[decision].append(condition)
|
|
141
|
+
|
|
142
|
+
total_unreached += 1
|
|
143
|
+
elif result == EngineResult.REACHABLE:
|
|
144
|
+
bmc.add_target(target)
|
|
145
|
+
total_reached += 1
|
|
146
|
+
|
|
147
|
+
# print('There are', totalTargets, 'test objectives:')
|
|
148
|
+
# print('-', totalUnreached, 'unreachable test objectives')
|
|
149
|
+
# print('-', totalReached, 'reachable test objectives')
|
|
150
|
+
trace2condition = {}
|
|
151
|
+
|
|
152
|
+
if total_unreached == total_targets:
|
|
153
|
+
return decision2traces, trace2condition, decision2unreachable
|
|
154
|
+
|
|
155
|
+
# Compute counterexamples for reachable targets
|
|
156
|
+
done = False
|
|
157
|
+
depth = 0
|
|
158
|
+
bmc_total_reached = 0
|
|
159
|
+
while not done:
|
|
160
|
+
bmc.set_current_depth(depth)
|
|
161
|
+
result = bmc.reach_targets()
|
|
162
|
+
if result != EngineResult.REACHABLE:
|
|
163
|
+
if depth == max_depth:
|
|
164
|
+
done = True
|
|
165
|
+
depth += 1
|
|
166
|
+
continue
|
|
167
|
+
reached = bmc.get_last_reached_targets()
|
|
168
|
+
trace = bmc.get_last_trace()
|
|
169
|
+
for reached_target in reached:
|
|
170
|
+
decision = testobjectives2decision[reached_target]
|
|
171
|
+
decision2traces[decision].append(trace)
|
|
172
|
+
trace2condition[trace] = testobjectives2condition[reached_target]
|
|
173
|
+
bmc_total_reached += 1
|
|
174
|
+
bmc.remove_last_reached_targets()
|
|
175
|
+
if bmc_total_reached == total_reached:
|
|
176
|
+
done = True
|
|
177
|
+
|
|
178
|
+
total_undecided = total_reached - bmc_total_reached
|
|
179
|
+
if total_undecided != 0:
|
|
180
|
+
print('There are', total_undecided, 'undecided test objectives within depth', max_depth)
|
|
181
|
+
|
|
182
|
+
return decision2traces, trace2condition, decision2unreachable
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def compute_mcdc_tables(context, inst_a, inst_b, decision2traces, trace2condition, decisions):
|
|
186
|
+
"""
|
|
187
|
+
Computes MC/DC tables out of counterexamples.
|
|
188
|
+
"""
|
|
189
|
+
decision2table = {}
|
|
190
|
+
decision2independencepairs = {}
|
|
191
|
+
seen = {}
|
|
192
|
+
for decision, traces in decision2traces.items():
|
|
193
|
+
inst_a_conds_dec =\
|
|
194
|
+
[inst_a.nets()[cond] for cond in decisions[decision]] + [inst_a.nets()[decision]]
|
|
195
|
+
inst_a_testnets =\
|
|
196
|
+
list(inst_a.inputs().values()) + inst_a_conds_dec
|
|
197
|
+
inst_b_conds_dec =\
|
|
198
|
+
[inst_b.nets()[cond] for cond in decisions[decision]] + [inst_b.nets()[decision]]
|
|
199
|
+
inst_b_testnets = list(inst_b.inputs().values()) + inst_b_conds_dec
|
|
200
|
+
header = decisions[decision]
|
|
201
|
+
header.append(decision)
|
|
202
|
+
header_nets_a = set(inst_a_conds_dec)
|
|
203
|
+
# Enable this to include inputs in the tests (but you need to add also names in headers)
|
|
204
|
+
# header_nets_a = set(inst_a_testnets)
|
|
205
|
+
header_nets_b = set(inst_b_conds_dec)
|
|
206
|
+
# Enable this to include inputs in the tests (but you need to add also names in headers)
|
|
207
|
+
# header_nets_b = set(inst_b_testnets)
|
|
208
|
+
decision2table[decision] = [header]
|
|
209
|
+
decision2independencepairs[decision] = {}
|
|
210
|
+
test_number = 0
|
|
211
|
+
for trace in traces:
|
|
212
|
+
test_a_number = None
|
|
213
|
+
test_b_number = None
|
|
214
|
+
# Enrich the traces with the value of the output
|
|
215
|
+
# by performing a simulation
|
|
216
|
+
simulator = context.mk_simulator()
|
|
217
|
+
for net in inst_a_testnets:
|
|
218
|
+
simulator.add_watch(net)
|
|
219
|
+
for net in inst_b_testnets:
|
|
220
|
+
simulator.add_watch(net)
|
|
221
|
+
simulator.simulate(trace, trace.get_max_depth())
|
|
222
|
+
full_test_a = trace.get_as_net_dictionary()
|
|
223
|
+
full_test_b = trace.get_as_net_dictionary()
|
|
224
|
+
test_a_candidate = [v[0] for k, v in full_test_a.items()\
|
|
225
|
+
if (k in inst_a_testnets and k in header_nets_a)]
|
|
226
|
+
test_b_candidate = [v[0] for k, v in full_test_b.items()\
|
|
227
|
+
if (k in inst_b_testnets and k in header_nets_b)]
|
|
228
|
+
test_a_hash = compute_hash(test_a_candidate)
|
|
229
|
+
test_b_hash = compute_hash(test_b_candidate)
|
|
230
|
+
|
|
231
|
+
if test_a_hash in seen:
|
|
232
|
+
_, test_a_number = seen[test_a_hash]
|
|
233
|
+
else:
|
|
234
|
+
seen[test_a_hash] = (test_a_candidate, test_number)
|
|
235
|
+
decision2table[decision].append(test_a_candidate)
|
|
236
|
+
test_a_number = test_number
|
|
237
|
+
test_number += 1
|
|
238
|
+
|
|
239
|
+
if test_b_hash in seen:
|
|
240
|
+
_, test_b_number = seen[test_b_hash]
|
|
241
|
+
else:
|
|
242
|
+
seen[test_b_hash] = (test_b_candidate, test_number)
|
|
243
|
+
decision2table[decision].append(test_b_candidate)
|
|
244
|
+
test_b_number = test_number
|
|
245
|
+
test_number += 1
|
|
246
|
+
|
|
247
|
+
condition = trace2condition[trace]
|
|
248
|
+
condition = decisions[decision][condition]
|
|
249
|
+
decision2independencepairs[decision][condition] = (test_a_number, test_b_number)
|
|
250
|
+
return decision2table, decision2independencepairs
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def compute_hash(test):
|
|
254
|
+
"""
|
|
255
|
+
Computes a unique hash for a test
|
|
256
|
+
"""
|
|
257
|
+
result = ""
|
|
258
|
+
for val in test:
|
|
259
|
+
result += val
|
|
260
|
+
return result
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def compute_pretty_tables(decision2table):
|
|
264
|
+
"""
|
|
265
|
+
Postprocess raw MC/DC tables to get something presentation ready.
|
|
266
|
+
"""
|
|
267
|
+
decision2prettytable = {}
|
|
268
|
+
first = True
|
|
269
|
+
for decision, table in decision2table.items():
|
|
270
|
+
pretty_table = []
|
|
271
|
+
for row in table:
|
|
272
|
+
if first:
|
|
273
|
+
first = False
|
|
274
|
+
pretty_table.append(row)
|
|
275
|
+
else:
|
|
276
|
+
pretty_table.append(row[0])
|
|
277
|
+
pretty_table.append(row[1])
|
|
278
|
+
decision2prettytable[decision] = pretty_table
|
|
279
|
+
return decision2prettytable
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_tables_as_dataframe(decision2table):
|
|
283
|
+
"""
|
|
284
|
+
Postprocess table to turn into a dataframe
|
|
285
|
+
"""
|
|
286
|
+
result = {}
|
|
287
|
+
for decision, table in decision2table.items():
|
|
288
|
+
if len(table) == 1:
|
|
289
|
+
continue
|
|
290
|
+
dataframe = pd.DataFrame(table[1:], columns=table[0])
|
|
291
|
+
dataframe = dataframe.drop_duplicates()
|
|
292
|
+
result[decision] = dataframe
|
|
293
|
+
return result
|