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.
Files changed (73) hide show
  1. intrepyd/LICENSE.intrepid +60 -0
  2. intrepyd/__init__.py +4 -0
  3. intrepyd/api.py +236 -0
  4. intrepyd/atg/__init__.py +3 -0
  5. intrepyd/atg/circuit.py +33 -0
  6. intrepyd/atg/mcdc.py +293 -0
  7. intrepyd/circuit.py +79 -0
  8. intrepyd/colors.py +55 -0
  9. intrepyd/components/__init__.py +3 -0
  10. intrepyd/components/eda.py +276 -0
  11. intrepyd/components.py +55 -0
  12. intrepyd/config.py +47 -0
  13. intrepyd/context.py +496 -0
  14. intrepyd/engine.py +286 -0
  15. intrepyd/formula2py/FormulaLexer.py +84 -0
  16. intrepyd/formula2py/FormulaParser.py +424 -0
  17. intrepyd/formula2py/FormulaVisitor.py +48 -0
  18. intrepyd/formula2py/encoder.py +100 -0
  19. intrepyd/formula2py/formulabuilder.py +51 -0
  20. intrepyd/formula2py/parser.py +22 -0
  21. intrepyd/iec611312py/IEC61131ParserLexer.py +1616 -0
  22. intrepyd/iec611312py/IEC61131ParserParser.py +6735 -0
  23. intrepyd/iec611312py/IEC61131ParserVisitor.py +513 -0
  24. intrepyd/iec611312py/__init__.py +3 -0
  25. intrepyd/iec611312py/datatype.py +103 -0
  26. intrepyd/iec611312py/expression.py +171 -0
  27. intrepyd/iec611312py/flatstmt2intrepyd.py +174 -0
  28. intrepyd/iec611312py/flattener.py +105 -0
  29. intrepyd/iec611312py/function.py +44 -0
  30. intrepyd/iec611312py/functionblock.py +32 -0
  31. intrepyd/iec611312py/inferdatatype.py +168 -0
  32. intrepyd/iec611312py/parsest.py +20 -0
  33. intrepyd/iec611312py/plcopen.py +111 -0
  34. intrepyd/iec611312py/statement.py +94 -0
  35. intrepyd/iec611312py/stmtbuilder.py +238 -0
  36. intrepyd/iec611312py/stmtprinter.py +95 -0
  37. intrepyd/iec611312py/summarizer.py +78 -0
  38. intrepyd/iec611312py/translator.py +298 -0
  39. intrepyd/iec611312py/utils.py +6 -0
  40. intrepyd/iec611312py/variable.py +34 -0
  41. intrepyd/iec611312py/visitor.py +88 -0
  42. intrepyd/intrepid.dll +0 -0
  43. intrepyd/lustre2py/LustreLexer.py +309 -0
  44. intrepyd/lustre2py/LustreParser.py +2811 -0
  45. intrepyd/lustre2py/LustreVisitor.py +243 -0
  46. intrepyd/lustre2py/__init__.py +3 -0
  47. intrepyd/lustre2py/ast.py +17 -0
  48. intrepyd/lustre2py/ast2intrepyd.py +332 -0
  49. intrepyd/lustre2py/astbuilder.py +180 -0
  50. intrepyd/lustre2py/astprinter.py +140 -0
  51. intrepyd/lustre2py/datatype.py +53 -0
  52. intrepyd/lustre2py/expression.py +219 -0
  53. intrepyd/lustre2py/instruction.py +115 -0
  54. intrepyd/lustre2py/node.py +247 -0
  55. intrepyd/lustre2py/parser.py +22 -0
  56. intrepyd/lustre2py/translator.py +142 -0
  57. intrepyd/lustre2py/variable.py +27 -0
  58. intrepyd/lustre2py/visitor.py +107 -0
  59. intrepyd/parser.py +209 -0
  60. intrepyd/plots.py +114 -0
  61. intrepyd/pseudoboolean.py +101 -0
  62. intrepyd/scr.py +118 -0
  63. intrepyd/simulator.py +27 -0
  64. intrepyd/tools.py +61 -0
  65. intrepyd/trace.py +125 -0
  66. intrepyd/visitable.py +14 -0
  67. intrepyd-0.13.0.dist-info/METADATA +76 -0
  68. intrepyd-0.13.0.dist-info/RECORD +73 -0
  69. intrepyd-0.13.0.dist-info/WHEEL +5 -0
  70. intrepyd-0.13.0.dist-info/licenses/CREDITS.md +17 -0
  71. intrepyd-0.13.0.dist-info/licenses/LICENSE.md +35 -0
  72. intrepyd-0.13.0.dist-info/licenses/intrepyd/LICENSE.intrepid +60 -0
  73. 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
@@ -0,0 +1,4 @@
1
+ """
2
+ Main package for Intrepyd
3
+ """
4
+ from .context import Context
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()
@@ -0,0 +1,3 @@
1
+ """
2
+ Atg
3
+ """
@@ -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