oscura 0.5.0__py3-none-any.whl → 0.6.0__py3-none-any.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 (513) hide show
  1. oscura/__init__.py +169 -167
  2. oscura/analyzers/__init__.py +3 -0
  3. oscura/analyzers/classification.py +659 -0
  4. oscura/analyzers/digital/__init__.py +0 -48
  5. oscura/analyzers/digital/edges.py +325 -65
  6. oscura/analyzers/digital/extraction.py +0 -195
  7. oscura/analyzers/digital/quality.py +293 -166
  8. oscura/analyzers/digital/timing.py +260 -115
  9. oscura/analyzers/digital/timing_numba.py +334 -0
  10. oscura/analyzers/entropy.py +605 -0
  11. oscura/analyzers/eye/diagram.py +176 -109
  12. oscura/analyzers/eye/metrics.py +5 -5
  13. oscura/analyzers/jitter/__init__.py +6 -4
  14. oscura/analyzers/jitter/ber.py +52 -52
  15. oscura/analyzers/jitter/classification.py +156 -0
  16. oscura/analyzers/jitter/decomposition.py +163 -113
  17. oscura/analyzers/jitter/spectrum.py +80 -64
  18. oscura/analyzers/ml/__init__.py +39 -0
  19. oscura/analyzers/ml/features.py +600 -0
  20. oscura/analyzers/ml/signal_classifier.py +604 -0
  21. oscura/analyzers/packet/daq.py +246 -158
  22. oscura/analyzers/packet/parser.py +12 -1
  23. oscura/analyzers/packet/payload.py +50 -2110
  24. oscura/analyzers/packet/payload_analysis.py +361 -181
  25. oscura/analyzers/packet/payload_patterns.py +133 -70
  26. oscura/analyzers/packet/stream.py +84 -23
  27. oscura/analyzers/patterns/__init__.py +26 -5
  28. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  29. oscura/analyzers/patterns/clustering.py +169 -108
  30. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  31. oscura/analyzers/patterns/discovery.py +1 -1
  32. oscura/analyzers/patterns/matching.py +581 -197
  33. oscura/analyzers/patterns/pattern_mining.py +778 -0
  34. oscura/analyzers/patterns/periodic.py +121 -38
  35. oscura/analyzers/patterns/sequences.py +175 -78
  36. oscura/analyzers/power/conduction.py +1 -1
  37. oscura/analyzers/power/soa.py +6 -6
  38. oscura/analyzers/power/switching.py +250 -110
  39. oscura/analyzers/protocol/__init__.py +17 -1
  40. oscura/analyzers/protocols/__init__.py +1 -22
  41. oscura/analyzers/protocols/base.py +6 -6
  42. oscura/analyzers/protocols/ble/__init__.py +38 -0
  43. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  44. oscura/analyzers/protocols/ble/uuids.py +288 -0
  45. oscura/analyzers/protocols/can.py +257 -127
  46. oscura/analyzers/protocols/can_fd.py +107 -80
  47. oscura/analyzers/protocols/flexray.py +139 -80
  48. oscura/analyzers/protocols/hdlc.py +93 -58
  49. oscura/analyzers/protocols/i2c.py +247 -106
  50. oscura/analyzers/protocols/i2s.py +138 -86
  51. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  52. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  53. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  54. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  55. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  56. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  57. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  58. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  59. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  60. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  61. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  62. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  63. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  64. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  65. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  66. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  67. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  68. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  69. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  70. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  71. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  72. oscura/analyzers/protocols/jtag.py +180 -98
  73. oscura/analyzers/protocols/lin.py +219 -114
  74. oscura/analyzers/protocols/manchester.py +4 -4
  75. oscura/analyzers/protocols/onewire.py +253 -149
  76. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  77. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  78. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  79. oscura/analyzers/protocols/spi.py +192 -95
  80. oscura/analyzers/protocols/swd.py +321 -167
  81. oscura/analyzers/protocols/uart.py +267 -125
  82. oscura/analyzers/protocols/usb.py +235 -131
  83. oscura/analyzers/side_channel/power.py +17 -12
  84. oscura/analyzers/signal/__init__.py +15 -0
  85. oscura/analyzers/signal/timing_analysis.py +1086 -0
  86. oscura/analyzers/signal_integrity/__init__.py +4 -1
  87. oscura/analyzers/signal_integrity/sparams.py +2 -19
  88. oscura/analyzers/spectral/chunked.py +129 -60
  89. oscura/analyzers/spectral/chunked_fft.py +300 -94
  90. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  91. oscura/analyzers/statistical/checksum.py +376 -217
  92. oscura/analyzers/statistical/classification.py +229 -107
  93. oscura/analyzers/statistical/entropy.py +78 -53
  94. oscura/analyzers/statistics/correlation.py +407 -211
  95. oscura/analyzers/statistics/outliers.py +2 -2
  96. oscura/analyzers/statistics/streaming.py +30 -5
  97. oscura/analyzers/validation.py +216 -101
  98. oscura/analyzers/waveform/measurements.py +9 -0
  99. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  100. oscura/analyzers/waveform/spectral.py +500 -228
  101. oscura/api/__init__.py +31 -5
  102. oscura/api/dsl/__init__.py +582 -0
  103. oscura/{dsl → api/dsl}/commands.py +43 -76
  104. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  105. oscura/{dsl → api/dsl}/parser.py +107 -77
  106. oscura/{dsl → api/dsl}/repl.py +2 -2
  107. oscura/api/dsl.py +1 -1
  108. oscura/{integrations → api/integrations}/__init__.py +1 -1
  109. oscura/{integrations → api/integrations}/llm.py +201 -102
  110. oscura/api/operators.py +3 -3
  111. oscura/api/optimization.py +144 -30
  112. oscura/api/rest_server.py +921 -0
  113. oscura/api/server/__init__.py +17 -0
  114. oscura/api/server/dashboard.py +850 -0
  115. oscura/api/server/static/README.md +34 -0
  116. oscura/api/server/templates/base.html +181 -0
  117. oscura/api/server/templates/export.html +120 -0
  118. oscura/api/server/templates/home.html +284 -0
  119. oscura/api/server/templates/protocols.html +58 -0
  120. oscura/api/server/templates/reports.html +43 -0
  121. oscura/api/server/templates/session_detail.html +89 -0
  122. oscura/api/server/templates/sessions.html +83 -0
  123. oscura/api/server/templates/waveforms.html +73 -0
  124. oscura/automotive/__init__.py +8 -1
  125. oscura/automotive/can/__init__.py +10 -0
  126. oscura/automotive/can/checksum.py +3 -1
  127. oscura/automotive/can/dbc_generator.py +590 -0
  128. oscura/automotive/can/message_wrapper.py +121 -74
  129. oscura/automotive/can/patterns.py +98 -21
  130. oscura/automotive/can/session.py +292 -56
  131. oscura/automotive/can/state_machine.py +6 -3
  132. oscura/automotive/can/stimulus_response.py +97 -75
  133. oscura/automotive/dbc/__init__.py +10 -2
  134. oscura/automotive/dbc/generator.py +84 -56
  135. oscura/automotive/dbc/parser.py +6 -6
  136. oscura/automotive/dtc/data.json +2763 -0
  137. oscura/automotive/dtc/database.py +2 -2
  138. oscura/automotive/flexray/__init__.py +31 -0
  139. oscura/automotive/flexray/analyzer.py +504 -0
  140. oscura/automotive/flexray/crc.py +185 -0
  141. oscura/automotive/flexray/fibex.py +449 -0
  142. oscura/automotive/j1939/__init__.py +45 -8
  143. oscura/automotive/j1939/analyzer.py +605 -0
  144. oscura/automotive/j1939/spns.py +326 -0
  145. oscura/automotive/j1939/transport.py +306 -0
  146. oscura/automotive/lin/__init__.py +47 -0
  147. oscura/automotive/lin/analyzer.py +612 -0
  148. oscura/automotive/loaders/blf.py +13 -2
  149. oscura/automotive/loaders/csv_can.py +143 -72
  150. oscura/automotive/loaders/dispatcher.py +50 -2
  151. oscura/automotive/loaders/mdf.py +86 -45
  152. oscura/automotive/loaders/pcap.py +111 -61
  153. oscura/automotive/uds/__init__.py +4 -0
  154. oscura/automotive/uds/analyzer.py +725 -0
  155. oscura/automotive/uds/decoder.py +140 -58
  156. oscura/automotive/uds/models.py +7 -1
  157. oscura/automotive/visualization.py +1 -1
  158. oscura/cli/analyze.py +348 -0
  159. oscura/cli/batch.py +142 -122
  160. oscura/cli/benchmark.py +275 -0
  161. oscura/cli/characterize.py +137 -82
  162. oscura/cli/compare.py +224 -131
  163. oscura/cli/completion.py +250 -0
  164. oscura/cli/config_cmd.py +361 -0
  165. oscura/cli/decode.py +164 -87
  166. oscura/cli/export.py +286 -0
  167. oscura/cli/main.py +115 -31
  168. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  169. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  170. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  171. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  172. oscura/cli/progress.py +147 -0
  173. oscura/cli/shell.py +157 -135
  174. oscura/cli/validate_cmd.py +204 -0
  175. oscura/cli/visualize.py +158 -0
  176. oscura/convenience.py +125 -79
  177. oscura/core/__init__.py +4 -2
  178. oscura/core/backend_selector.py +3 -3
  179. oscura/core/cache.py +126 -15
  180. oscura/core/cancellation.py +1 -1
  181. oscura/{config → core/config}/__init__.py +20 -11
  182. oscura/{config → core/config}/defaults.py +1 -1
  183. oscura/{config → core/config}/loader.py +7 -5
  184. oscura/{config → core/config}/memory.py +5 -5
  185. oscura/{config → core/config}/migration.py +1 -1
  186. oscura/{config → core/config}/pipeline.py +99 -23
  187. oscura/{config → core/config}/preferences.py +1 -1
  188. oscura/{config → core/config}/protocol.py +3 -3
  189. oscura/{config → core/config}/schema.py +426 -272
  190. oscura/{config → core/config}/settings.py +1 -1
  191. oscura/{config → core/config}/thresholds.py +195 -153
  192. oscura/core/correlation.py +5 -6
  193. oscura/core/cross_domain.py +0 -2
  194. oscura/core/debug.py +9 -5
  195. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  196. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  197. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  198. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  199. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  200. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  201. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  202. oscura/core/gpu_backend.py +11 -7
  203. oscura/core/log_query.py +101 -11
  204. oscura/core/logging.py +126 -54
  205. oscura/core/logging_advanced.py +5 -5
  206. oscura/core/memory_limits.py +108 -70
  207. oscura/core/memory_monitor.py +2 -2
  208. oscura/core/memory_progress.py +7 -7
  209. oscura/core/memory_warnings.py +1 -1
  210. oscura/core/numba_backend.py +13 -13
  211. oscura/{plugins → core/plugins}/__init__.py +9 -9
  212. oscura/{plugins → core/plugins}/base.py +7 -7
  213. oscura/{plugins → core/plugins}/cli.py +3 -3
  214. oscura/{plugins → core/plugins}/discovery.py +186 -106
  215. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  216. oscura/{plugins → core/plugins}/manager.py +7 -7
  217. oscura/{plugins → core/plugins}/registry.py +3 -3
  218. oscura/{plugins → core/plugins}/versioning.py +1 -1
  219. oscura/core/progress.py +16 -1
  220. oscura/core/provenance.py +8 -2
  221. oscura/{schemas → core/schemas}/__init__.py +2 -2
  222. oscura/core/schemas/bus_configuration.json +322 -0
  223. oscura/core/schemas/device_mapping.json +182 -0
  224. oscura/core/schemas/packet_format.json +418 -0
  225. oscura/core/schemas/protocol_definition.json +363 -0
  226. oscura/core/types.py +4 -0
  227. oscura/core/uncertainty.py +3 -3
  228. oscura/correlation/__init__.py +52 -0
  229. oscura/correlation/multi_protocol.py +811 -0
  230. oscura/discovery/auto_decoder.py +117 -35
  231. oscura/discovery/comparison.py +191 -86
  232. oscura/discovery/quality_validator.py +155 -68
  233. oscura/discovery/signal_detector.py +196 -79
  234. oscura/export/__init__.py +18 -20
  235. oscura/export/kaitai_struct.py +513 -0
  236. oscura/export/scapy_layer.py +801 -0
  237. oscura/export/wireshark/README.md +15 -15
  238. oscura/export/wireshark/generator.py +1 -1
  239. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  240. oscura/export/wireshark_dissector.py +746 -0
  241. oscura/guidance/wizard.py +207 -111
  242. oscura/hardware/__init__.py +19 -0
  243. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  244. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  245. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  246. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  247. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  248. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  249. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  250. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  251. oscura/hardware/firmware/__init__.py +29 -0
  252. oscura/hardware/firmware/pattern_recognition.py +874 -0
  253. oscura/hardware/hal_detector.py +736 -0
  254. oscura/hardware/security/__init__.py +37 -0
  255. oscura/hardware/security/side_channel_detector.py +1126 -0
  256. oscura/inference/__init__.py +4 -0
  257. oscura/inference/active_learning/README.md +7 -7
  258. oscura/inference/active_learning/observation_table.py +4 -1
  259. oscura/inference/alignment.py +216 -123
  260. oscura/inference/bayesian.py +113 -33
  261. oscura/inference/crc_reverse.py +101 -55
  262. oscura/inference/logic.py +6 -2
  263. oscura/inference/message_format.py +342 -183
  264. oscura/inference/protocol.py +95 -44
  265. oscura/inference/protocol_dsl.py +180 -82
  266. oscura/inference/signal_intelligence.py +1439 -706
  267. oscura/inference/spectral.py +99 -57
  268. oscura/inference/state_machine.py +810 -158
  269. oscura/inference/stream.py +270 -110
  270. oscura/iot/__init__.py +34 -0
  271. oscura/iot/coap/__init__.py +32 -0
  272. oscura/iot/coap/analyzer.py +668 -0
  273. oscura/iot/coap/options.py +212 -0
  274. oscura/iot/lorawan/__init__.py +21 -0
  275. oscura/iot/lorawan/crypto.py +206 -0
  276. oscura/iot/lorawan/decoder.py +801 -0
  277. oscura/iot/lorawan/mac_commands.py +341 -0
  278. oscura/iot/mqtt/__init__.py +27 -0
  279. oscura/iot/mqtt/analyzer.py +999 -0
  280. oscura/iot/mqtt/properties.py +315 -0
  281. oscura/iot/zigbee/__init__.py +31 -0
  282. oscura/iot/zigbee/analyzer.py +615 -0
  283. oscura/iot/zigbee/security.py +153 -0
  284. oscura/iot/zigbee/zcl.py +349 -0
  285. oscura/jupyter/display.py +125 -45
  286. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  287. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  288. oscura/jupyter/exploratory/fuzzy.py +746 -0
  289. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  290. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  291. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  292. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  293. oscura/jupyter/exploratory/sync.py +612 -0
  294. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  295. oscura/jupyter/magic.py +4 -4
  296. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  297. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  298. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  299. oscura/loaders/__init__.py +171 -63
  300. oscura/loaders/binary.py +88 -1
  301. oscura/loaders/chipwhisperer.py +153 -137
  302. oscura/loaders/configurable.py +208 -86
  303. oscura/loaders/csv_loader.py +458 -215
  304. oscura/loaders/hdf5_loader.py +278 -119
  305. oscura/loaders/lazy.py +87 -54
  306. oscura/loaders/mmap_loader.py +1 -1
  307. oscura/loaders/numpy_loader.py +253 -116
  308. oscura/loaders/pcap.py +226 -151
  309. oscura/loaders/rigol.py +110 -49
  310. oscura/loaders/sigrok.py +201 -78
  311. oscura/loaders/tdms.py +81 -58
  312. oscura/loaders/tektronix.py +291 -174
  313. oscura/loaders/touchstone.py +182 -87
  314. oscura/loaders/vcd.py +215 -117
  315. oscura/loaders/wav.py +155 -68
  316. oscura/reporting/__init__.py +9 -7
  317. oscura/reporting/analyze.py +352 -146
  318. oscura/reporting/argument_preparer.py +69 -14
  319. oscura/reporting/auto_report.py +97 -61
  320. oscura/reporting/batch.py +131 -58
  321. oscura/reporting/chart_selection.py +57 -45
  322. oscura/reporting/comparison.py +63 -17
  323. oscura/reporting/content/executive.py +76 -24
  324. oscura/reporting/core_formats/multi_format.py +11 -8
  325. oscura/reporting/engine.py +312 -158
  326. oscura/reporting/enhanced_reports.py +949 -0
  327. oscura/reporting/export.py +86 -43
  328. oscura/reporting/formatting/numbers.py +69 -42
  329. oscura/reporting/html.py +139 -58
  330. oscura/reporting/index.py +137 -65
  331. oscura/reporting/output.py +158 -67
  332. oscura/reporting/pdf.py +67 -102
  333. oscura/reporting/plots.py +191 -112
  334. oscura/reporting/sections.py +88 -47
  335. oscura/reporting/standards.py +104 -61
  336. oscura/reporting/summary_generator.py +75 -55
  337. oscura/reporting/tables.py +138 -54
  338. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  339. oscura/reporting/templates/index.md +13 -13
  340. oscura/sessions/__init__.py +14 -23
  341. oscura/sessions/base.py +3 -3
  342. oscura/sessions/blackbox.py +106 -10
  343. oscura/sessions/generic.py +2 -2
  344. oscura/sessions/legacy.py +783 -0
  345. oscura/side_channel/__init__.py +63 -0
  346. oscura/side_channel/dpa.py +1025 -0
  347. oscura/utils/__init__.py +15 -1
  348. oscura/utils/autodetect.py +1 -5
  349. oscura/utils/bitwise.py +118 -0
  350. oscura/{builders → utils/builders}/__init__.py +1 -1
  351. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  352. oscura/{comparison → utils/comparison}/compare.py +202 -101
  353. oscura/{comparison → utils/comparison}/golden.py +83 -63
  354. oscura/{comparison → utils/comparison}/limits.py +313 -89
  355. oscura/{comparison → utils/comparison}/mask.py +151 -45
  356. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  357. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  358. oscura/{component → utils/component}/__init__.py +3 -3
  359. oscura/{component → utils/component}/impedance.py +122 -58
  360. oscura/{component → utils/component}/reactive.py +165 -168
  361. oscura/{component → utils/component}/transmission_line.py +3 -3
  362. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  363. oscura/{filtering → utils/filtering}/base.py +1 -1
  364. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  365. oscura/{filtering → utils/filtering}/design.py +169 -93
  366. oscura/{filtering → utils/filtering}/filters.py +2 -2
  367. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  368. oscura/utils/geometry.py +31 -0
  369. oscura/utils/imports.py +184 -0
  370. oscura/utils/lazy.py +1 -1
  371. oscura/{math → utils/math}/__init__.py +2 -2
  372. oscura/{math → utils/math}/arithmetic.py +114 -48
  373. oscura/{math → utils/math}/interpolation.py +139 -106
  374. oscura/utils/memory.py +129 -66
  375. oscura/utils/memory_advanced.py +92 -9
  376. oscura/utils/memory_extensions.py +10 -8
  377. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  378. oscura/{optimization → utils/optimization}/search.py +2 -2
  379. oscura/utils/performance/__init__.py +58 -0
  380. oscura/utils/performance/caching.py +889 -0
  381. oscura/utils/performance/lsh_clustering.py +333 -0
  382. oscura/utils/performance/memory_optimizer.py +699 -0
  383. oscura/utils/performance/optimizations.py +675 -0
  384. oscura/utils/performance/parallel.py +654 -0
  385. oscura/utils/performance/profiling.py +661 -0
  386. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  387. oscura/{pipeline → utils/pipeline}/composition.py +11 -3
  388. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  389. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  390. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  391. oscura/{search → utils/search}/__init__.py +3 -3
  392. oscura/{search → utils/search}/anomaly.py +188 -58
  393. oscura/utils/search/context.py +294 -0
  394. oscura/{search → utils/search}/pattern.py +138 -10
  395. oscura/utils/serial.py +51 -0
  396. oscura/utils/storage/__init__.py +61 -0
  397. oscura/utils/storage/database.py +1166 -0
  398. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  399. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  400. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  401. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  402. oscura/{triggering → utils/triggering}/base.py +6 -6
  403. oscura/{triggering → utils/triggering}/edge.py +2 -2
  404. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  405. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  406. oscura/{triggering → utils/triggering}/window.py +2 -2
  407. oscura/utils/validation.py +32 -0
  408. oscura/validation/__init__.py +121 -0
  409. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  410. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  411. oscura/{compliance → validation/compliance}/masks.py +1 -1
  412. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  413. oscura/{compliance → validation/compliance}/testing.py +114 -52
  414. oscura/validation/compliance_tests.py +915 -0
  415. oscura/validation/fuzzer.py +990 -0
  416. oscura/validation/grammar_tests.py +596 -0
  417. oscura/validation/grammar_validator.py +904 -0
  418. oscura/validation/hil_testing.py +977 -0
  419. oscura/{quality → validation/quality}/__init__.py +4 -4
  420. oscura/{quality → validation/quality}/ensemble.py +251 -171
  421. oscura/{quality → validation/quality}/explainer.py +3 -3
  422. oscura/{quality → validation/quality}/scoring.py +1 -1
  423. oscura/{quality → validation/quality}/warnings.py +4 -4
  424. oscura/validation/regression_suite.py +808 -0
  425. oscura/validation/replay.py +788 -0
  426. oscura/{testing → validation/testing}/__init__.py +2 -2
  427. oscura/{testing → validation/testing}/synthetic.py +5 -5
  428. oscura/visualization/__init__.py +9 -0
  429. oscura/visualization/accessibility.py +1 -1
  430. oscura/visualization/annotations.py +64 -67
  431. oscura/visualization/colors.py +7 -7
  432. oscura/visualization/digital.py +180 -81
  433. oscura/visualization/eye.py +236 -85
  434. oscura/visualization/interactive.py +320 -143
  435. oscura/visualization/jitter.py +587 -247
  436. oscura/visualization/layout.py +169 -134
  437. oscura/visualization/optimization.py +103 -52
  438. oscura/visualization/palettes.py +1 -1
  439. oscura/visualization/power.py +427 -211
  440. oscura/visualization/power_extended.py +626 -297
  441. oscura/visualization/presets.py +2 -0
  442. oscura/visualization/protocols.py +495 -181
  443. oscura/visualization/render.py +79 -63
  444. oscura/visualization/reverse_engineering.py +171 -124
  445. oscura/visualization/signal_integrity.py +460 -279
  446. oscura/visualization/specialized.py +190 -100
  447. oscura/visualization/spectral.py +670 -255
  448. oscura/visualization/thumbnails.py +166 -137
  449. oscura/visualization/waveform.py +150 -63
  450. oscura/workflows/__init__.py +3 -0
  451. oscura/{batch → workflows/batch}/__init__.py +5 -5
  452. oscura/{batch → workflows/batch}/advanced.py +150 -75
  453. oscura/workflows/batch/aggregate.py +531 -0
  454. oscura/workflows/batch/analyze.py +236 -0
  455. oscura/{batch → workflows/batch}/logging.py +2 -2
  456. oscura/{batch → workflows/batch}/metrics.py +1 -1
  457. oscura/workflows/complete_re.py +1144 -0
  458. oscura/workflows/compliance.py +44 -54
  459. oscura/workflows/digital.py +197 -51
  460. oscura/workflows/legacy/__init__.py +12 -0
  461. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  462. oscura/workflows/multi_trace.py +9 -9
  463. oscura/workflows/power.py +42 -62
  464. oscura/workflows/protocol.py +82 -49
  465. oscura/workflows/reverse_engineering.py +351 -150
  466. oscura/workflows/signal_integrity.py +157 -82
  467. oscura-0.6.0.dist-info/METADATA +643 -0
  468. oscura-0.6.0.dist-info/RECORD +590 -0
  469. oscura/analyzers/digital/ic_database.py +0 -498
  470. oscura/analyzers/digital/timing_paths.py +0 -339
  471. oscura/analyzers/digital/vintage.py +0 -377
  472. oscura/analyzers/digital/vintage_result.py +0 -148
  473. oscura/analyzers/protocols/parallel_bus.py +0 -449
  474. oscura/batch/aggregate.py +0 -300
  475. oscura/batch/analyze.py +0 -139
  476. oscura/dsl/__init__.py +0 -73
  477. oscura/exceptions.py +0 -59
  478. oscura/exploratory/fuzzy.py +0 -513
  479. oscura/exploratory/sync.py +0 -384
  480. oscura/export/wavedrom.py +0 -430
  481. oscura/exporters/__init__.py +0 -94
  482. oscura/exporters/csv.py +0 -303
  483. oscura/exporters/exporters.py +0 -44
  484. oscura/exporters/hdf5.py +0 -217
  485. oscura/exporters/html_export.py +0 -701
  486. oscura/exporters/json_export.py +0 -338
  487. oscura/exporters/markdown_export.py +0 -367
  488. oscura/exporters/matlab_export.py +0 -354
  489. oscura/exporters/npz_export.py +0 -219
  490. oscura/exporters/spice_export.py +0 -210
  491. oscura/exporters/vintage_logic_csv.py +0 -247
  492. oscura/reporting/vintage_logic_report.py +0 -523
  493. oscura/search/context.py +0 -149
  494. oscura/session/__init__.py +0 -34
  495. oscura/session/annotations.py +0 -289
  496. oscura/session/history.py +0 -313
  497. oscura/session/session.py +0 -520
  498. oscura/visualization/digital_advanced.py +0 -718
  499. oscura/visualization/figure_manager.py +0 -156
  500. oscura/workflow/__init__.py +0 -13
  501. oscura-0.5.0.dist-info/METADATA +0 -407
  502. oscura-0.5.0.dist-info/RECORD +0 -486
  503. /oscura/core/{config.py → config/legacy.py} +0 -0
  504. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  505. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  506. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  507. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  508. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  509. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  510. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  511. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
  512. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
  513. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,915 @@
1
+ """Compliance test generator for protocol standards validation.
2
+
3
+ This module provides comprehensive test suite generation for validating protocol
4
+ implementations against industry standards (IEEE, ISO, SAE, ANSI). Generates test
5
+ vectors for conformance, boundary conditions, and interoperability testing.
6
+
7
+ Example:
8
+ >>> from oscura.validation import ComplianceTestGenerator, ComplianceConfig
9
+ >>> from oscura.sessions import ProtocolSpec
10
+ >>>
11
+ >>> # Generate IEEE 802.3 Ethernet compliance tests
12
+ >>> config = ComplianceConfig(standard="IEEE_802_3", test_types=["conformance"])
13
+ >>> generator = ComplianceTestGenerator(config)
14
+ >>> suite = generator.generate_suite(protocol_spec)
15
+ >>>
16
+ >>> # Export to pytest
17
+ >>> generator.export_pytest(suite, Path("test_ethernet_compliance.py"))
18
+ >>>
19
+ >>> # Export to JSON test vectors
20
+ >>> generator.export_json(suite, Path("ethernet_test_vectors.json"))
21
+
22
+ References:
23
+ IEEE 802.3 Ethernet Standard
24
+ SAE J1939 CAN Bus Protocol
25
+ ISO 14229 Unified Diagnostic Services
26
+ ISO 15765 ISO-TP (CAN Transport Protocol)
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import json
32
+ import random
33
+ from dataclasses import dataclass, field
34
+ from enum import Enum
35
+ from pathlib import Path
36
+ from typing import TYPE_CHECKING, Any, Literal
37
+
38
+ if TYPE_CHECKING:
39
+ from oscura.sessions.blackbox import FieldHypothesis, ProtocolSpec
40
+
41
+
42
+ class StandardType(str, Enum):
43
+ """Supported industry standards for compliance testing.
44
+
45
+ Attributes:
46
+ IEEE_802_3: Ethernet (CSMA/CD) protocol
47
+ IEEE_1149_1: JTAG boundary scan
48
+ SAE_J1939: CAN-based vehicle network
49
+ ISO_14229: UDS diagnostic protocol
50
+ ISO_15765: ISO-TP CAN transport
51
+ MODBUS: Industrial Modbus RTU/TCP
52
+ PROFINET: Real-time industrial Ethernet
53
+ ETHERCAT: Industrial fieldbus
54
+ MQTT: IoT messaging protocol
55
+ COAP: Constrained application protocol
56
+ LORAWAN: Long-range wide-area network
57
+ """
58
+
59
+ IEEE_802_3 = "IEEE_802_3"
60
+ IEEE_1149_1 = "IEEE_1149_1"
61
+ SAE_J1939 = "SAE_J1939"
62
+ ISO_14229 = "ISO_14229"
63
+ ISO_15765 = "ISO_15765"
64
+ MODBUS = "MODBUS"
65
+ PROFINET = "PROFINET"
66
+ ETHERCAT = "ETHERCAT"
67
+ MQTT = "MQTT"
68
+ COAP = "COAP"
69
+ LORAWAN = "LORAWAN"
70
+
71
+
72
+ class TestType(str, Enum):
73
+ """Types of compliance tests to generate.
74
+
75
+ Attributes:
76
+ CONFORMANCE: Protocol conformance tests (message structure, timing, sequencing)
77
+ BOUNDARY: Boundary value tests (min/max values, overflow, underflow)
78
+ ERROR_HANDLING: Error handling tests (invalid checksums, malformed messages)
79
+ STATE_MACHINE: State machine coverage tests (all transitions exercised)
80
+ INTEROPERABILITY: Interoperability tests (multi-vendor compatibility)
81
+ """
82
+
83
+ CONFORMANCE = "conformance"
84
+ BOUNDARY = "boundary"
85
+ ERROR_HANDLING = "error_handling"
86
+ STATE_MACHINE = "state_machine"
87
+ INTEROPERABILITY = "interoperability"
88
+
89
+
90
+ @dataclass
91
+ class ComplianceConfig:
92
+ """Configuration for compliance test generation.
93
+
94
+ Attributes:
95
+ standard: Target industry standard for compliance.
96
+ test_types: Types of tests to generate.
97
+ num_tests_per_type: Number of tests per test type.
98
+ include_documentation: Include test documentation in output.
99
+ export_format: Export format ("pytest", "json", "pcap", "markdown").
100
+ strict_mode: Enforce strict compliance (no deviations allowed).
101
+
102
+ Example:
103
+ >>> config = ComplianceConfig(
104
+ ... standard=StandardType.SAE_J1939,
105
+ ... test_types=[TestType.CONFORMANCE, TestType.BOUNDARY],
106
+ ... num_tests_per_type=50
107
+ ... )
108
+ """
109
+
110
+ standard: StandardType | str = StandardType.SAE_J1939
111
+ test_types: list[TestType | str] = field(
112
+ default_factory=lambda: [TestType.CONFORMANCE, TestType.BOUNDARY]
113
+ )
114
+ num_tests_per_type: int = 20
115
+ include_documentation: bool = True
116
+ export_format: Literal["pytest", "json", "pcap", "markdown"] = "pytest"
117
+ strict_mode: bool = True
118
+
119
+ def __post_init__(self) -> None:
120
+ """Validate configuration after initialization."""
121
+ if self.num_tests_per_type <= 0:
122
+ raise ValueError(f"num_tests_per_type must be positive, got {self.num_tests_per_type}")
123
+
124
+ # Convert string to enum if needed
125
+ if isinstance(self.standard, str):
126
+ try:
127
+ self.standard = StandardType(self.standard)
128
+ except ValueError:
129
+ # Allow custom standards (not in enum)
130
+ pass
131
+
132
+ # Convert test type strings to enums
133
+ converted_types: list[TestType | str] = []
134
+ for test_type in self.test_types:
135
+ if isinstance(test_type, TestType):
136
+ converted_types.append(test_type)
137
+ elif isinstance(test_type, str):
138
+ try:
139
+ converted_types.append(TestType(test_type))
140
+ except ValueError:
141
+ converted_types.append(test_type) # Keep custom string
142
+ self.test_types = converted_types
143
+
144
+ if self.export_format not in {"pytest", "json", "pcap", "markdown"}:
145
+ raise ValueError(f"Invalid export_format: {self.export_format}")
146
+
147
+
148
+ @dataclass
149
+ class TestCase:
150
+ """Individual compliance test case.
151
+
152
+ Attributes:
153
+ name: Test case name.
154
+ description: Human-readable description.
155
+ test_type: Type of test (conformance, boundary, etc.).
156
+ input_data: Input data for test (bytes or dict).
157
+ expected_output: Expected output (bytes, dict, or validation result).
158
+ standard_reference: Reference to standard section (e.g., "IEEE 802.3 §4.2.1").
159
+ severity: Test severity ("critical", "high", "medium", "low").
160
+ metadata: Additional test metadata.
161
+
162
+ Example:
163
+ >>> test = TestCase(
164
+ ... name="ethernet_min_frame_size",
165
+ ... description="Verify minimum Ethernet frame size of 64 bytes",
166
+ ... test_type=TestType.BOUNDARY,
167
+ ... input_data=b"\\x00" * 64,
168
+ ... expected_output={"valid": True},
169
+ ... standard_reference="IEEE 802.3 §3.2.8",
170
+ ... severity="critical"
171
+ ... )
172
+ """
173
+
174
+ name: str
175
+ description: str
176
+ test_type: TestType | str
177
+ input_data: bytes | dict[str, Any]
178
+ expected_output: bytes | dict[str, Any] | bool | None
179
+ standard_reference: str
180
+ severity: Literal["critical", "high", "medium", "low"] = "medium"
181
+ metadata: dict[str, Any] = field(default_factory=dict)
182
+
183
+
184
+ @dataclass
185
+ class ComplianceTestSuite:
186
+ """Complete compliance test suite.
187
+
188
+ Attributes:
189
+ standard: Target standard.
190
+ test_cases: List of test cases.
191
+ metadata: Suite metadata (version, date, coverage stats).
192
+ documentation: Test suite documentation.
193
+
194
+ Example:
195
+ >>> suite = ComplianceTestSuite(
196
+ ... standard=StandardType.SAE_J1939,
197
+ ... test_cases=[test1, test2, test3],
198
+ ... metadata={"total_tests": 3, "coverage": 85.5}
199
+ ... )
200
+ >>> print(f"Tests: {len(suite.test_cases)}")
201
+ """
202
+
203
+ standard: StandardType | str
204
+ test_cases: list[TestCase] = field(default_factory=list)
205
+ metadata: dict[str, Any] = field(default_factory=dict)
206
+ documentation: str = ""
207
+
208
+ @property
209
+ def total_tests(self) -> int:
210
+ """Get total number of test cases."""
211
+ return len(self.test_cases)
212
+
213
+ def get_tests_by_type(self, test_type: TestType | str) -> list[TestCase]:
214
+ """Get test cases by type.
215
+
216
+ Args:
217
+ test_type: Type of tests to retrieve.
218
+
219
+ Returns:
220
+ List of matching test cases.
221
+ """
222
+ return [tc for tc in self.test_cases if tc.test_type == test_type]
223
+
224
+ def get_tests_by_severity(
225
+ self, severity: Literal["critical", "high", "medium", "low"]
226
+ ) -> list[TestCase]:
227
+ """Get test cases by severity.
228
+
229
+ Args:
230
+ severity: Severity level to filter.
231
+
232
+ Returns:
233
+ List of matching test cases.
234
+ """
235
+ return [tc for tc in self.test_cases if tc.severity == severity]
236
+
237
+
238
+ class ComplianceTestGenerator:
239
+ """Compliance test suite generator for protocol standards validation.
240
+
241
+ Generates comprehensive test suites from protocol specifications using
242
+ industry standard compliance requirements. Supports IEEE, SAE, ISO, ANSI standards.
243
+
244
+ Attributes:
245
+ config: Compliance test configuration.
246
+
247
+ Example:
248
+ >>> config = ComplianceConfig(standard=StandardType.IEEE_802_3)
249
+ >>> generator = ComplianceTestGenerator(config)
250
+ >>> suite = generator.generate_suite(protocol_spec)
251
+ >>> generator.export_pytest(suite, Path("test_compliance.py"))
252
+ """
253
+
254
+ def __init__(self, config: ComplianceConfig) -> None:
255
+ """Initialize compliance test generator.
256
+
257
+ Args:
258
+ config: Compliance test configuration.
259
+ """
260
+ self.config = config
261
+ self._rng = random.Random(42) # Deterministic for reproducibility
262
+
263
+ # Standard-specific constraints
264
+ self._standard_constraints = self._load_standard_constraints()
265
+
266
+ def generate_suite(self, spec: ProtocolSpec) -> ComplianceTestSuite:
267
+ """Generate complete compliance test suite from protocol specification.
268
+
269
+ Args:
270
+ spec: Protocol specification with field definitions.
271
+
272
+ Returns:
273
+ Complete compliance test suite with all test types.
274
+
275
+ Example:
276
+ >>> suite = generator.generate_suite(protocol_spec)
277
+ >>> print(f"Generated {suite.total_tests} tests")
278
+ >>> print(f"Conformance: {len(suite.get_tests_by_type('conformance'))}")
279
+ """
280
+ suite = ComplianceTestSuite(
281
+ standard=self.config.standard,
282
+ metadata={
283
+ "protocol_name": spec.name,
284
+ "standard": str(self.config.standard),
285
+ "strict_mode": self.config.strict_mode,
286
+ "test_types": [str(tt) for tt in self.config.test_types],
287
+ },
288
+ )
289
+
290
+ # Generate tests for each type
291
+ for test_type in self.config.test_types:
292
+ if test_type == TestType.CONFORMANCE:
293
+ suite.test_cases.extend(self._generate_conformance_tests(spec))
294
+ elif test_type == TestType.BOUNDARY:
295
+ suite.test_cases.extend(self._generate_boundary_tests(spec))
296
+ elif test_type == TestType.ERROR_HANDLING:
297
+ suite.test_cases.extend(self._generate_error_handling_tests(spec))
298
+ elif test_type == TestType.STATE_MACHINE:
299
+ suite.test_cases.extend(self._generate_state_machine_tests(spec))
300
+ elif test_type == TestType.INTEROPERABILITY:
301
+ suite.test_cases.extend(self._generate_interoperability_tests(spec))
302
+
303
+ # Generate documentation
304
+ if self.config.include_documentation:
305
+ suite.documentation = self._generate_documentation(suite, spec)
306
+
307
+ # Update metadata
308
+ suite.metadata.update(
309
+ {
310
+ "total_tests": suite.total_tests,
311
+ "conformance_tests": len(suite.get_tests_by_type(TestType.CONFORMANCE)),
312
+ "boundary_tests": len(suite.get_tests_by_type(TestType.BOUNDARY)),
313
+ "error_handling_tests": len(suite.get_tests_by_type(TestType.ERROR_HANDLING)),
314
+ "state_machine_tests": len(suite.get_tests_by_type(TestType.STATE_MACHINE)),
315
+ "critical_tests": len(suite.get_tests_by_severity("critical")),
316
+ "high_tests": len(suite.get_tests_by_severity("high")),
317
+ }
318
+ )
319
+
320
+ return suite
321
+
322
+ def _generate_conformance_tests(self, spec: ProtocolSpec) -> list[TestCase]:
323
+ """Generate protocol conformance tests.
324
+
325
+ Tests message structure, field ordering, data types, and protocol sequences
326
+ according to standard specifications.
327
+
328
+ Args:
329
+ spec: Protocol specification.
330
+
331
+ Returns:
332
+ List of conformance test cases.
333
+ """
334
+ tests: list[TestCase] = []
335
+ constraints = self._standard_constraints.get(str(self.config.standard), {})
336
+
337
+ # Test 1: Valid message structure
338
+ for i in range(min(self.config.num_tests_per_type, 10)):
339
+ msg = self._build_valid_message(spec, constraints)
340
+ tests.append(
341
+ TestCase(
342
+ name=f"{spec.name.lower()}_conformance_valid_{i}",
343
+ description=f"Valid {spec.name} message conforming to standard",
344
+ test_type=TestType.CONFORMANCE,
345
+ input_data=msg,
346
+ expected_output={"valid": True, "errors": []},
347
+ standard_reference=constraints.get(
348
+ "reference", f"{self.config.standard} General"
349
+ ),
350
+ severity="critical",
351
+ metadata={"test_id": f"CONF_{i:03d}"},
352
+ )
353
+ )
354
+
355
+ # Test 2: Field ordering compliance
356
+ if len(spec.fields) > 1:
357
+ tests.append(
358
+ TestCase(
359
+ name=f"{spec.name.lower()}_field_ordering",
360
+ description="Verify correct field ordering per standard",
361
+ test_type=TestType.CONFORMANCE,
362
+ input_data=self._build_valid_message(spec, constraints),
363
+ expected_output={"field_order_valid": True},
364
+ standard_reference=constraints.get("reference", "Field Order"),
365
+ severity="high",
366
+ metadata={"fields": [f.name for f in spec.fields]},
367
+ )
368
+ )
369
+
370
+ # Test 3: Minimum message length
371
+ min_length = sum(f.length for f in spec.fields)
372
+ tests.append(
373
+ TestCase(
374
+ name=f"{spec.name.lower()}_min_message_length",
375
+ description=f"Verify minimum message length of {min_length} bytes",
376
+ test_type=TestType.CONFORMANCE,
377
+ input_data=self._build_valid_message(spec, constraints),
378
+ expected_output={"length": min_length, "valid": True},
379
+ standard_reference=constraints.get("reference", "Message Length"),
380
+ severity="critical",
381
+ metadata={"min_length": min_length},
382
+ )
383
+ )
384
+
385
+ return tests
386
+
387
+ def _generate_boundary_tests(self, spec: ProtocolSpec) -> list[TestCase]:
388
+ """Generate boundary value tests.
389
+
390
+ Tests minimum/maximum values, overflow, underflow, and edge cases
391
+ for all protocol fields.
392
+
393
+ Args:
394
+ spec: Protocol specification.
395
+
396
+ Returns:
397
+ List of boundary test cases.
398
+ """
399
+ tests: list[TestCase] = []
400
+
401
+ # For each field, test min/max values
402
+ for field_idx, field_def in enumerate(spec.fields):
403
+ if field_def.field_type in {"data", "counter"}:
404
+ # Min value (0)
405
+ msg_min = self._build_message_with_field_value(spec, field_idx, 0)
406
+ tests.append(
407
+ TestCase(
408
+ name=f"{spec.name.lower()}_field_{field_def.name}_min",
409
+ description=f"Test minimum value (0) for {field_def.name}",
410
+ test_type=TestType.BOUNDARY,
411
+ input_data=msg_min,
412
+ expected_output={"valid": True, "field_value": 0},
413
+ standard_reference=f"{self.config.standard} Boundary Values",
414
+ severity="high",
415
+ metadata={"field": field_def.name, "boundary": "min"},
416
+ )
417
+ )
418
+
419
+ # Max value
420
+ max_val = (256**field_def.length) - 1
421
+ msg_max = self._build_message_with_field_value(spec, field_idx, max_val)
422
+ tests.append(
423
+ TestCase(
424
+ name=f"{spec.name.lower()}_field_{field_def.name}_max",
425
+ description=f"Test maximum value ({max_val}) for {field_def.name}",
426
+ test_type=TestType.BOUNDARY,
427
+ input_data=msg_max,
428
+ expected_output={"valid": True, "field_value": max_val},
429
+ standard_reference=f"{self.config.standard} Boundary Values",
430
+ severity="high",
431
+ metadata={"field": field_def.name, "boundary": "max"},
432
+ )
433
+ )
434
+
435
+ # All zeros message
436
+ msg_len = sum(f.length for f in spec.fields)
437
+ tests.append(
438
+ TestCase(
439
+ name=f"{spec.name.lower()}_all_zeros",
440
+ description="Test message with all zero bytes",
441
+ test_type=TestType.BOUNDARY,
442
+ input_data=b"\x00" * msg_len,
443
+ expected_output={"valid": self.config.strict_mode is False},
444
+ standard_reference=f"{self.config.standard} Edge Cases",
445
+ severity="medium",
446
+ metadata={"pattern": "all_zeros"},
447
+ )
448
+ )
449
+
450
+ # All ones message
451
+ tests.append(
452
+ TestCase(
453
+ name=f"{spec.name.lower()}_all_ones",
454
+ description="Test message with all 0xFF bytes",
455
+ test_type=TestType.BOUNDARY,
456
+ input_data=b"\xff" * msg_len,
457
+ expected_output={"valid": self.config.strict_mode is False},
458
+ standard_reference=f"{self.config.standard} Edge Cases",
459
+ severity="medium",
460
+ metadata={"pattern": "all_ones"},
461
+ )
462
+ )
463
+
464
+ return tests
465
+
466
+ def _generate_error_handling_tests(self, spec: ProtocolSpec) -> list[TestCase]:
467
+ """Generate error handling tests.
468
+
469
+ Tests invalid checksums, malformed messages, and protocol violations.
470
+
471
+ Args:
472
+ spec: Protocol specification.
473
+
474
+ Returns:
475
+ List of error handling test cases.
476
+ """
477
+ tests: list[TestCase] = []
478
+
479
+ # Find checksum field
480
+ checksum_field_idx = None
481
+ checksum_offset = 0
482
+ for idx, field_def in enumerate(spec.fields):
483
+ if field_def.field_type == "checksum":
484
+ checksum_field_idx = idx
485
+ break
486
+ checksum_offset += field_def.length
487
+
488
+ # Test 1: Invalid checksum
489
+ if checksum_field_idx is not None:
490
+ msg = self._build_valid_message(spec, {})
491
+ msg_arr = bytearray(msg)
492
+ # Corrupt checksum
493
+ checksum_len = spec.fields[checksum_field_idx].length
494
+ for i in range(checksum_len):
495
+ msg_arr[checksum_offset + i] ^= 0xFF
496
+
497
+ tests.append(
498
+ TestCase(
499
+ name=f"{spec.name.lower()}_invalid_checksum",
500
+ description="Test message with corrupted checksum",
501
+ test_type=TestType.ERROR_HANDLING,
502
+ input_data=bytes(msg_arr),
503
+ expected_output={"valid": False, "error": "checksum_mismatch"},
504
+ standard_reference=f"{self.config.standard} Error Detection",
505
+ severity="critical",
506
+ metadata={"error_type": "checksum"},
507
+ )
508
+ )
509
+
510
+ # Test 2: Truncated message
511
+ msg = self._build_valid_message(spec, {})
512
+ truncated = msg[: len(msg) // 2]
513
+ tests.append(
514
+ TestCase(
515
+ name=f"{spec.name.lower()}_truncated_message",
516
+ description="Test truncated message (incomplete data)",
517
+ test_type=TestType.ERROR_HANDLING,
518
+ input_data=truncated,
519
+ expected_output={"valid": False, "error": "incomplete_message"},
520
+ standard_reference=f"{self.config.standard} Message Format",
521
+ severity="high",
522
+ metadata={"error_type": "truncation"},
523
+ )
524
+ )
525
+
526
+ # Test 3: Oversized message
527
+ msg = self._build_valid_message(spec, {})
528
+ oversized = msg + b"\x00" * 10
529
+ tests.append(
530
+ TestCase(
531
+ name=f"{spec.name.lower()}_oversized_message",
532
+ description="Test oversized message (extra bytes)",
533
+ test_type=TestType.ERROR_HANDLING,
534
+ input_data=oversized,
535
+ expected_output={"valid": self.config.strict_mode is False},
536
+ standard_reference=f"{self.config.standard} Message Format",
537
+ severity="medium",
538
+ metadata={"error_type": "oversized"},
539
+ )
540
+ )
541
+
542
+ return tests
543
+
544
+ def _generate_state_machine_tests(self, spec: ProtocolSpec) -> list[TestCase]:
545
+ """Generate state machine coverage tests.
546
+
547
+ Tests all state transitions and verifies protocol state handling.
548
+
549
+ Args:
550
+ spec: Protocol specification.
551
+
552
+ Returns:
553
+ List of state machine test cases.
554
+ """
555
+ tests: list[TestCase] = []
556
+
557
+ # Generate basic state transition tests
558
+ transitions = [
559
+ ("IDLE", "ACTIVE", "initialize"),
560
+ ("ACTIVE", "ERROR", "fault"),
561
+ ("ERROR", "RECOVERY", "reset"),
562
+ ("RECOVERY", "IDLE", "complete"),
563
+ ]
564
+
565
+ for from_state, to_state, event in transitions:
566
+ tests.append(
567
+ TestCase(
568
+ name=f"{spec.name.lower()}_transition_{from_state}_to_{to_state}",
569
+ description=f"Test {from_state} -> {to_state} transition on {event}",
570
+ test_type=TestType.STATE_MACHINE,
571
+ input_data={"state": from_state, "event": event},
572
+ expected_output={"next_state": to_state, "valid": True},
573
+ standard_reference=f"{self.config.standard} State Machine",
574
+ severity="high",
575
+ metadata={"from": from_state, "to": to_state, "event": event},
576
+ )
577
+ )
578
+
579
+ return tests
580
+
581
+ def _generate_interoperability_tests(self, spec: ProtocolSpec) -> list[TestCase]:
582
+ """Generate interoperability tests.
583
+
584
+ Tests multi-vendor compatibility and protocol variant handling.
585
+
586
+ Args:
587
+ spec: Protocol specification.
588
+
589
+ Returns:
590
+ List of interoperability test cases.
591
+ """
592
+ tests: list[TestCase] = []
593
+
594
+ # Generate tests for different protocol variants/implementations
595
+ variants = ["vendor_a", "vendor_b", "reference_impl"]
596
+
597
+ for variant in variants:
598
+ msg = self._build_valid_message(spec, {})
599
+ tests.append(
600
+ TestCase(
601
+ name=f"{spec.name.lower()}_interop_{variant}",
602
+ description=f"Test interoperability with {variant} implementation",
603
+ test_type=TestType.INTEROPERABILITY,
604
+ input_data=msg,
605
+ expected_output={"compatible": True, "variant": variant},
606
+ standard_reference=f"{self.config.standard} Interoperability",
607
+ severity="medium",
608
+ metadata={"variant": variant},
609
+ )
610
+ )
611
+
612
+ return tests
613
+
614
+ def _build_valid_message(self, spec: ProtocolSpec, constraints: dict[str, Any]) -> bytes:
615
+ """Build valid protocol message conforming to standard.
616
+
617
+ Args:
618
+ spec: Protocol specification.
619
+ constraints: Standard-specific constraints.
620
+
621
+ Returns:
622
+ Valid message bytes.
623
+ """
624
+ msg = bytearray()
625
+
626
+ for field_def in spec.fields:
627
+ field_bytes = self._generate_field_value(field_def, constraints)
628
+ msg.extend(field_bytes)
629
+
630
+ return bytes(msg)
631
+
632
+ def _generate_field_value(
633
+ self, field_def: FieldHypothesis, constraints: dict[str, Any]
634
+ ) -> bytes:
635
+ """Generate value for a single field.
636
+
637
+ Args:
638
+ field_def: Field definition.
639
+ constraints: Standard-specific constraints.
640
+
641
+ Returns:
642
+ Field value as bytes.
643
+ """
644
+ if field_def.field_type == "constant":
645
+ const_val = field_def.evidence.get("value", 0)
646
+ return self._pack_value(const_val, field_def.length)
647
+
648
+ if field_def.field_type == "counter":
649
+ counter_val = self._rng.randint(0, (256**field_def.length) - 1)
650
+ return self._pack_value(counter_val, field_def.length)
651
+
652
+ if field_def.field_type == "checksum":
653
+ # Placeholder for checksum (computed later)
654
+ return b"\x00" * field_def.length
655
+
656
+ # Default: random data
657
+ return bytes(self._rng.randint(0, 255) for _ in range(field_def.length))
658
+
659
+ def _build_message_with_field_value(
660
+ self, spec: ProtocolSpec, field_idx: int, value: int
661
+ ) -> bytes:
662
+ """Build message with specific field set to value.
663
+
664
+ Args:
665
+ spec: Protocol specification.
666
+ field_idx: Index of field to set.
667
+ value: Value to assign to field.
668
+
669
+ Returns:
670
+ Complete message with field set.
671
+ """
672
+ msg = bytearray()
673
+
674
+ for idx, field_def in enumerate(spec.fields):
675
+ if idx == field_idx:
676
+ msg.extend(self._pack_value(value, field_def.length))
677
+ else:
678
+ msg.extend(self._generate_field_value(field_def, {}))
679
+
680
+ return bytes(msg)
681
+
682
+ def _pack_value(self, value: int, length: int) -> bytes:
683
+ """Pack integer value into bytes (little-endian).
684
+
685
+ Args:
686
+ value: Integer value.
687
+ length: Number of bytes.
688
+
689
+ Returns:
690
+ Packed bytes.
691
+ """
692
+ return value.to_bytes(length, byteorder="little")
693
+
694
+ def _load_standard_constraints(self) -> dict[str, Any]:
695
+ """Load standard-specific constraints and requirements.
696
+
697
+ Returns:
698
+ Dictionary of constraints by standard name.
699
+ """
700
+ return {
701
+ "StandardType.IEEE_802_3": {
702
+ "min_frame_size": 64,
703
+ "max_frame_size": 1518,
704
+ "reference": "IEEE 802.3 §3.2.8",
705
+ },
706
+ "StandardType.SAE_J1939": {
707
+ "max_pgn": 0x1FFFF,
708
+ "priority_range": (0, 7),
709
+ "reference": "SAE J1939/21",
710
+ },
711
+ "StandardType.ISO_14229": {
712
+ "service_id_range": (0x01, 0xFF),
713
+ "negative_response": 0x7F,
714
+ "reference": "ISO 14229-1:2020",
715
+ },
716
+ "StandardType.MODBUS": {
717
+ "max_address": 247,
718
+ "function_code_range": (1, 127),
719
+ "reference": "Modbus Application Protocol V1.1b3",
720
+ },
721
+ }
722
+
723
+ def _generate_documentation(self, suite: ComplianceTestSuite, spec: ProtocolSpec) -> str:
724
+ """Generate comprehensive test suite documentation.
725
+
726
+ Args:
727
+ suite: Test suite.
728
+ spec: Protocol specification.
729
+
730
+ Returns:
731
+ Markdown documentation.
732
+ """
733
+ doc = [
734
+ f"# Compliance Test Suite: {spec.name}",
735
+ f"\n## Standard: {suite.standard}",
736
+ f"\n**Total Tests:** {suite.total_tests}",
737
+ "\n### Test Coverage",
738
+ "",
739
+ ]
740
+
741
+ for test_type in self.config.test_types:
742
+ count = len(suite.get_tests_by_type(test_type))
743
+ doc.append(f"- **{test_type}**: {count} tests")
744
+
745
+ doc.extend(
746
+ [
747
+ "\n### Severity Distribution",
748
+ "",
749
+ f"- Critical: {len(suite.get_tests_by_severity('critical'))}",
750
+ f"- High: {len(suite.get_tests_by_severity('high'))}",
751
+ f"- Medium: {len(suite.get_tests_by_severity('medium'))}",
752
+ f"- Low: {len(suite.get_tests_by_severity('low'))}",
753
+ "\n## Test Cases",
754
+ "",
755
+ ]
756
+ )
757
+
758
+ for test_case in suite.test_cases[:10]: # First 10 for brevity
759
+ doc.append(f"### {test_case.name}")
760
+ doc.append(f"\n**Description:** {test_case.description}")
761
+ doc.append(f"**Type:** {test_case.test_type}")
762
+ doc.append(f"**Severity:** {test_case.severity}")
763
+ doc.append(f"**Standard Reference:** {test_case.standard_reference}")
764
+ doc.append("")
765
+
766
+ if suite.total_tests > 10:
767
+ doc.append(f"\n*... and {suite.total_tests - 10} more test cases*")
768
+
769
+ return "\n".join(doc)
770
+
771
+ def export_pytest(self, suite: ComplianceTestSuite, output: Path) -> None:
772
+ """Export compliance tests as pytest parametrized test cases.
773
+
774
+ Args:
775
+ suite: Compliance test suite.
776
+ output: Output Python test file path.
777
+
778
+ Example:
779
+ >>> generator.export_pytest(suite, Path("test_compliance.py"))
780
+ """
781
+ test_code = [
782
+ f'"""Generated compliance tests for {suite.standard}."""',
783
+ "",
784
+ "import pytest",
785
+ "",
786
+ "",
787
+ "@pytest.mark.parametrize(",
788
+ ' "test_case",',
789
+ " [",
790
+ ]
791
+
792
+ # Add test cases
793
+ for test_case in suite.test_cases:
794
+ # Serialize test case
795
+ test_dict = {
796
+ "name": test_case.name,
797
+ "description": test_case.description,
798
+ "test_type": str(test_case.test_type),
799
+ "input_data": (
800
+ test_case.input_data.hex()
801
+ if isinstance(test_case.input_data, bytes)
802
+ else test_case.input_data
803
+ ),
804
+ "expected_output": test_case.expected_output,
805
+ "standard_reference": test_case.standard_reference,
806
+ "severity": test_case.severity,
807
+ }
808
+ test_code.append(f" {test_dict!r},")
809
+
810
+ test_code.extend(
811
+ [
812
+ " ],",
813
+ ")",
814
+ "def test_compliance(test_case):",
815
+ f' """Test compliance with {suite.standard}."""',
816
+ " # TODO: Implement compliance validation (user should replace with actual validator)",
817
+ ' assert test_case["name"]',
818
+ ' assert test_case["standard_reference"]',
819
+ "",
820
+ "",
821
+ "def test_suite_coverage():",
822
+ f' """Verify test suite coverage for {suite.standard}."""',
823
+ f" assert {suite.total_tests} > 0 # Total tests",
824
+ ]
825
+ )
826
+
827
+ output.write_text("\n".join(test_code))
828
+
829
+ def export_json(self, suite: ComplianceTestSuite, output: Path) -> None:
830
+ """Export compliance tests as JSON test vectors.
831
+
832
+ Args:
833
+ suite: Compliance test suite.
834
+ output: Output JSON file path.
835
+
836
+ Example:
837
+ >>> generator.export_json(suite, Path("test_vectors.json"))
838
+ """
839
+ json_data = {
840
+ "standard": str(suite.standard),
841
+ "metadata": suite.metadata,
842
+ "documentation": suite.documentation,
843
+ "test_cases": [
844
+ {
845
+ "name": tc.name,
846
+ "description": tc.description,
847
+ "test_type": str(tc.test_type),
848
+ "input_data": (
849
+ tc.input_data.hex() if isinstance(tc.input_data, bytes) else tc.input_data
850
+ ),
851
+ "expected_output": tc.expected_output,
852
+ "standard_reference": tc.standard_reference,
853
+ "severity": tc.severity,
854
+ "metadata": tc.metadata,
855
+ }
856
+ for tc in suite.test_cases
857
+ ],
858
+ }
859
+
860
+ with output.open("w") as f:
861
+ json.dump(json_data, f, indent=2)
862
+
863
+ def export_pcap(self, suite: ComplianceTestSuite, output: Path) -> None:
864
+ """Export compliance tests as PCAP file (UDP packets).
865
+
866
+ Args:
867
+ suite: Compliance test suite.
868
+ output: Output PCAP file path.
869
+
870
+ Example:
871
+ >>> generator.export_pcap(suite, Path("compliance_tests.pcap"))
872
+ """
873
+ try:
874
+ from scapy.all import ( # type: ignore[attr-defined]
875
+ IP,
876
+ UDP,
877
+ Ether,
878
+ wrpcap,
879
+ )
880
+ except ImportError as e:
881
+ raise ImportError(
882
+ "scapy is required for PCAP export. Install with: uv pip install scapy"
883
+ ) from e
884
+
885
+ packets = []
886
+ for test_case in suite.test_cases:
887
+ if isinstance(test_case.input_data, bytes):
888
+ # Wrap in Ethernet/IP/UDP
889
+ pkt = Ether() / IP() / UDP(sport=12345, dport=54321) / test_case.input_data
890
+ packets.append(pkt)
891
+
892
+ if packets:
893
+ wrpcap(str(output), packets)
894
+
895
+ def export_markdown(self, suite: ComplianceTestSuite, output: Path) -> None:
896
+ """Export compliance tests as Markdown documentation.
897
+
898
+ Args:
899
+ suite: Compliance test suite.
900
+ output: Output Markdown file path.
901
+
902
+ Example:
903
+ >>> generator.export_markdown(suite, Path("compliance_tests.md"))
904
+ """
905
+ output.write_text(suite.documentation)
906
+
907
+
908
+ __all__ = [
909
+ "ComplianceConfig",
910
+ "ComplianceTestGenerator",
911
+ "ComplianceTestSuite",
912
+ "StandardType",
913
+ "TestCase",
914
+ "TestType",
915
+ ]