oscura 0.5.1__py3-none-any.whl → 0.7.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 (497) 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/edges.py +325 -65
  5. oscura/analyzers/digital/quality.py +293 -166
  6. oscura/analyzers/digital/timing.py +260 -115
  7. oscura/analyzers/digital/timing_numba.py +334 -0
  8. oscura/analyzers/entropy.py +605 -0
  9. oscura/analyzers/eye/diagram.py +176 -109
  10. oscura/analyzers/eye/metrics.py +5 -5
  11. oscura/analyzers/jitter/__init__.py +6 -4
  12. oscura/analyzers/jitter/ber.py +52 -52
  13. oscura/analyzers/jitter/classification.py +156 -0
  14. oscura/analyzers/jitter/decomposition.py +163 -113
  15. oscura/analyzers/jitter/spectrum.py +80 -64
  16. oscura/analyzers/ml/__init__.py +39 -0
  17. oscura/analyzers/ml/features.py +600 -0
  18. oscura/analyzers/ml/signal_classifier.py +604 -0
  19. oscura/analyzers/packet/daq.py +246 -158
  20. oscura/analyzers/packet/parser.py +12 -1
  21. oscura/analyzers/packet/payload.py +50 -2110
  22. oscura/analyzers/packet/payload_analysis.py +361 -181
  23. oscura/analyzers/packet/payload_patterns.py +133 -70
  24. oscura/analyzers/packet/stream.py +84 -23
  25. oscura/analyzers/patterns/__init__.py +26 -5
  26. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  27. oscura/analyzers/patterns/clustering.py +169 -108
  28. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  29. oscura/analyzers/patterns/discovery.py +1 -1
  30. oscura/analyzers/patterns/matching.py +581 -197
  31. oscura/analyzers/patterns/pattern_mining.py +778 -0
  32. oscura/analyzers/patterns/periodic.py +121 -38
  33. oscura/analyzers/patterns/sequences.py +175 -78
  34. oscura/analyzers/power/conduction.py +1 -1
  35. oscura/analyzers/power/soa.py +6 -6
  36. oscura/analyzers/power/switching.py +250 -110
  37. oscura/analyzers/protocol/__init__.py +17 -1
  38. oscura/analyzers/protocols/base.py +6 -6
  39. oscura/analyzers/protocols/ble/__init__.py +38 -0
  40. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  41. oscura/analyzers/protocols/ble/uuids.py +288 -0
  42. oscura/analyzers/protocols/can.py +257 -127
  43. oscura/analyzers/protocols/can_fd.py +107 -80
  44. oscura/analyzers/protocols/flexray.py +139 -80
  45. oscura/analyzers/protocols/hdlc.py +93 -58
  46. oscura/analyzers/protocols/i2c.py +247 -106
  47. oscura/analyzers/protocols/i2s.py +138 -86
  48. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  49. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  50. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  51. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  52. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  53. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  54. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  55. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  56. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  57. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  58. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  59. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  60. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  61. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  62. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  63. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  64. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  65. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  66. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  67. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  68. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  69. oscura/analyzers/protocols/jtag.py +180 -98
  70. oscura/analyzers/protocols/lin.py +219 -114
  71. oscura/analyzers/protocols/manchester.py +4 -4
  72. oscura/analyzers/protocols/onewire.py +253 -149
  73. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  74. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  75. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  76. oscura/analyzers/protocols/spi.py +192 -95
  77. oscura/analyzers/protocols/swd.py +321 -167
  78. oscura/analyzers/protocols/uart.py +267 -125
  79. oscura/analyzers/protocols/usb.py +235 -131
  80. oscura/analyzers/side_channel/power.py +17 -12
  81. oscura/analyzers/signal/__init__.py +15 -0
  82. oscura/analyzers/signal/timing_analysis.py +1086 -0
  83. oscura/analyzers/signal_integrity/__init__.py +4 -1
  84. oscura/analyzers/signal_integrity/sparams.py +2 -19
  85. oscura/analyzers/spectral/chunked.py +129 -60
  86. oscura/analyzers/spectral/chunked_fft.py +300 -94
  87. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  88. oscura/analyzers/statistical/checksum.py +376 -217
  89. oscura/analyzers/statistical/classification.py +229 -107
  90. oscura/analyzers/statistical/entropy.py +78 -53
  91. oscura/analyzers/statistics/correlation.py +407 -211
  92. oscura/analyzers/statistics/outliers.py +2 -2
  93. oscura/analyzers/statistics/streaming.py +30 -5
  94. oscura/analyzers/validation.py +216 -101
  95. oscura/analyzers/waveform/measurements.py +9 -0
  96. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  97. oscura/analyzers/waveform/spectral.py +500 -228
  98. oscura/api/__init__.py +31 -5
  99. oscura/api/dsl/__init__.py +582 -0
  100. oscura/{dsl → api/dsl}/commands.py +43 -76
  101. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  102. oscura/{dsl → api/dsl}/parser.py +107 -77
  103. oscura/{dsl → api/dsl}/repl.py +2 -2
  104. oscura/api/dsl.py +1 -1
  105. oscura/{integrations → api/integrations}/__init__.py +1 -1
  106. oscura/{integrations → api/integrations}/llm.py +201 -102
  107. oscura/api/operators.py +3 -3
  108. oscura/api/optimization.py +144 -30
  109. oscura/api/rest_server.py +921 -0
  110. oscura/api/server/__init__.py +17 -0
  111. oscura/api/server/dashboard.py +850 -0
  112. oscura/api/server/static/README.md +34 -0
  113. oscura/api/server/templates/base.html +181 -0
  114. oscura/api/server/templates/export.html +120 -0
  115. oscura/api/server/templates/home.html +284 -0
  116. oscura/api/server/templates/protocols.html +58 -0
  117. oscura/api/server/templates/reports.html +43 -0
  118. oscura/api/server/templates/session_detail.html +89 -0
  119. oscura/api/server/templates/sessions.html +83 -0
  120. oscura/api/server/templates/waveforms.html +73 -0
  121. oscura/automotive/__init__.py +8 -1
  122. oscura/automotive/can/__init__.py +10 -0
  123. oscura/automotive/can/checksum.py +3 -1
  124. oscura/automotive/can/dbc_generator.py +590 -0
  125. oscura/automotive/can/message_wrapper.py +121 -74
  126. oscura/automotive/can/patterns.py +98 -21
  127. oscura/automotive/can/session.py +292 -56
  128. oscura/automotive/can/state_machine.py +6 -3
  129. oscura/automotive/can/stimulus_response.py +97 -75
  130. oscura/automotive/dbc/__init__.py +10 -2
  131. oscura/automotive/dbc/generator.py +84 -56
  132. oscura/automotive/dbc/parser.py +6 -6
  133. oscura/automotive/dtc/data.json +17 -102
  134. oscura/automotive/dtc/database.py +2 -2
  135. oscura/automotive/flexray/__init__.py +31 -0
  136. oscura/automotive/flexray/analyzer.py +504 -0
  137. oscura/automotive/flexray/crc.py +185 -0
  138. oscura/automotive/flexray/fibex.py +449 -0
  139. oscura/automotive/j1939/__init__.py +45 -8
  140. oscura/automotive/j1939/analyzer.py +605 -0
  141. oscura/automotive/j1939/spns.py +326 -0
  142. oscura/automotive/j1939/transport.py +306 -0
  143. oscura/automotive/lin/__init__.py +47 -0
  144. oscura/automotive/lin/analyzer.py +612 -0
  145. oscura/automotive/loaders/blf.py +13 -2
  146. oscura/automotive/loaders/csv_can.py +143 -72
  147. oscura/automotive/loaders/dispatcher.py +50 -2
  148. oscura/automotive/loaders/mdf.py +86 -45
  149. oscura/automotive/loaders/pcap.py +111 -61
  150. oscura/automotive/uds/__init__.py +4 -0
  151. oscura/automotive/uds/analyzer.py +725 -0
  152. oscura/automotive/uds/decoder.py +140 -58
  153. oscura/automotive/uds/models.py +7 -1
  154. oscura/automotive/visualization.py +1 -1
  155. oscura/cli/analyze.py +348 -0
  156. oscura/cli/batch.py +142 -122
  157. oscura/cli/benchmark.py +275 -0
  158. oscura/cli/characterize.py +137 -82
  159. oscura/cli/compare.py +224 -131
  160. oscura/cli/completion.py +250 -0
  161. oscura/cli/config_cmd.py +361 -0
  162. oscura/cli/decode.py +164 -87
  163. oscura/cli/export.py +286 -0
  164. oscura/cli/main.py +115 -31
  165. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  166. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  167. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  168. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  169. oscura/cli/progress.py +147 -0
  170. oscura/cli/shell.py +157 -135
  171. oscura/cli/validate_cmd.py +204 -0
  172. oscura/cli/visualize.py +158 -0
  173. oscura/convenience.py +125 -79
  174. oscura/core/__init__.py +4 -2
  175. oscura/core/backend_selector.py +3 -3
  176. oscura/core/cache.py +126 -15
  177. oscura/core/cancellation.py +1 -1
  178. oscura/{config → core/config}/__init__.py +20 -11
  179. oscura/{config → core/config}/defaults.py +1 -1
  180. oscura/{config → core/config}/loader.py +7 -5
  181. oscura/{config → core/config}/memory.py +5 -5
  182. oscura/{config → core/config}/migration.py +1 -1
  183. oscura/{config → core/config}/pipeline.py +99 -23
  184. oscura/{config → core/config}/preferences.py +1 -1
  185. oscura/{config → core/config}/protocol.py +3 -3
  186. oscura/{config → core/config}/schema.py +426 -272
  187. oscura/{config → core/config}/settings.py +1 -1
  188. oscura/{config → core/config}/thresholds.py +195 -153
  189. oscura/core/correlation.py +5 -6
  190. oscura/core/cross_domain.py +0 -2
  191. oscura/core/debug.py +9 -5
  192. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  193. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  194. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  195. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  196. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  197. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  198. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  199. oscura/core/gpu_backend.py +11 -7
  200. oscura/core/log_query.py +101 -11
  201. oscura/core/logging.py +126 -54
  202. oscura/core/logging_advanced.py +5 -5
  203. oscura/core/memory_limits.py +108 -70
  204. oscura/core/memory_monitor.py +2 -2
  205. oscura/core/memory_progress.py +7 -7
  206. oscura/core/memory_warnings.py +1 -1
  207. oscura/core/numba_backend.py +13 -13
  208. oscura/{plugins → core/plugins}/__init__.py +9 -9
  209. oscura/{plugins → core/plugins}/base.py +7 -7
  210. oscura/{plugins → core/plugins}/cli.py +3 -3
  211. oscura/{plugins → core/plugins}/discovery.py +186 -106
  212. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  213. oscura/{plugins → core/plugins}/manager.py +7 -7
  214. oscura/{plugins → core/plugins}/registry.py +3 -3
  215. oscura/{plugins → core/plugins}/versioning.py +1 -1
  216. oscura/core/progress.py +16 -1
  217. oscura/core/provenance.py +8 -2
  218. oscura/{schemas → core/schemas}/__init__.py +2 -2
  219. oscura/{schemas → core/schemas}/device_mapping.json +2 -8
  220. oscura/{schemas → core/schemas}/packet_format.json +4 -24
  221. oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
  222. oscura/core/types.py +4 -0
  223. oscura/core/uncertainty.py +3 -3
  224. oscura/correlation/__init__.py +52 -0
  225. oscura/correlation/multi_protocol.py +811 -0
  226. oscura/discovery/auto_decoder.py +117 -35
  227. oscura/discovery/comparison.py +191 -86
  228. oscura/discovery/quality_validator.py +155 -68
  229. oscura/discovery/signal_detector.py +196 -79
  230. oscura/export/__init__.py +18 -8
  231. oscura/export/kaitai_struct.py +513 -0
  232. oscura/export/scapy_layer.py +801 -0
  233. oscura/export/wireshark/generator.py +1 -1
  234. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  235. oscura/export/wireshark_dissector.py +746 -0
  236. oscura/guidance/wizard.py +207 -111
  237. oscura/hardware/__init__.py +19 -0
  238. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  239. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  240. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  241. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  242. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  243. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  244. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  245. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  246. oscura/hardware/firmware/__init__.py +29 -0
  247. oscura/hardware/firmware/pattern_recognition.py +874 -0
  248. oscura/hardware/hal_detector.py +736 -0
  249. oscura/hardware/security/__init__.py +37 -0
  250. oscura/hardware/security/side_channel_detector.py +1126 -0
  251. oscura/inference/__init__.py +4 -0
  252. oscura/inference/active_learning/observation_table.py +4 -1
  253. oscura/inference/alignment.py +216 -123
  254. oscura/inference/bayesian.py +113 -33
  255. oscura/inference/crc_reverse.py +101 -55
  256. oscura/inference/logic.py +6 -2
  257. oscura/inference/message_format.py +342 -183
  258. oscura/inference/protocol.py +95 -44
  259. oscura/inference/protocol_dsl.py +180 -82
  260. oscura/inference/signal_intelligence.py +1439 -706
  261. oscura/inference/spectral.py +99 -57
  262. oscura/inference/state_machine.py +810 -158
  263. oscura/inference/stream.py +270 -110
  264. oscura/iot/__init__.py +34 -0
  265. oscura/iot/coap/__init__.py +32 -0
  266. oscura/iot/coap/analyzer.py +668 -0
  267. oscura/iot/coap/options.py +212 -0
  268. oscura/iot/lorawan/__init__.py +21 -0
  269. oscura/iot/lorawan/crypto.py +206 -0
  270. oscura/iot/lorawan/decoder.py +801 -0
  271. oscura/iot/lorawan/mac_commands.py +341 -0
  272. oscura/iot/mqtt/__init__.py +27 -0
  273. oscura/iot/mqtt/analyzer.py +999 -0
  274. oscura/iot/mqtt/properties.py +315 -0
  275. oscura/iot/zigbee/__init__.py +31 -0
  276. oscura/iot/zigbee/analyzer.py +615 -0
  277. oscura/iot/zigbee/security.py +153 -0
  278. oscura/iot/zigbee/zcl.py +349 -0
  279. oscura/jupyter/display.py +125 -45
  280. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  281. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  282. oscura/jupyter/exploratory/fuzzy.py +746 -0
  283. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  284. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  285. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  286. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  287. oscura/jupyter/exploratory/sync.py +612 -0
  288. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  289. oscura/jupyter/magic.py +4 -4
  290. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  291. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  292. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  293. oscura/loaders/__init__.py +183 -67
  294. oscura/loaders/binary.py +88 -1
  295. oscura/loaders/chipwhisperer.py +153 -137
  296. oscura/loaders/configurable.py +208 -86
  297. oscura/loaders/csv_loader.py +458 -215
  298. oscura/loaders/hdf5_loader.py +278 -119
  299. oscura/loaders/lazy.py +87 -54
  300. oscura/loaders/mmap_loader.py +1 -1
  301. oscura/loaders/numpy_loader.py +253 -116
  302. oscura/loaders/pcap.py +226 -151
  303. oscura/loaders/rigol.py +110 -49
  304. oscura/loaders/sigrok.py +201 -78
  305. oscura/loaders/tdms.py +81 -58
  306. oscura/loaders/tektronix.py +291 -174
  307. oscura/loaders/touchstone.py +182 -87
  308. oscura/loaders/tss.py +456 -0
  309. oscura/loaders/vcd.py +215 -117
  310. oscura/loaders/wav.py +155 -68
  311. oscura/reporting/__init__.py +9 -0
  312. oscura/reporting/analyze.py +352 -146
  313. oscura/reporting/argument_preparer.py +69 -14
  314. oscura/reporting/auto_report.py +97 -61
  315. oscura/reporting/batch.py +131 -58
  316. oscura/reporting/chart_selection.py +57 -45
  317. oscura/reporting/comparison.py +63 -17
  318. oscura/reporting/content/executive.py +76 -24
  319. oscura/reporting/core_formats/multi_format.py +11 -8
  320. oscura/reporting/engine.py +312 -158
  321. oscura/reporting/enhanced_reports.py +949 -0
  322. oscura/reporting/export.py +86 -43
  323. oscura/reporting/formatting/numbers.py +69 -42
  324. oscura/reporting/html.py +139 -58
  325. oscura/reporting/index.py +137 -65
  326. oscura/reporting/output.py +158 -67
  327. oscura/reporting/pdf.py +67 -102
  328. oscura/reporting/plots.py +191 -112
  329. oscura/reporting/sections.py +88 -47
  330. oscura/reporting/standards.py +104 -61
  331. oscura/reporting/summary_generator.py +75 -55
  332. oscura/reporting/tables.py +138 -54
  333. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  334. oscura/sessions/__init__.py +14 -23
  335. oscura/sessions/base.py +3 -3
  336. oscura/sessions/blackbox.py +106 -10
  337. oscura/sessions/generic.py +2 -2
  338. oscura/sessions/legacy.py +783 -0
  339. oscura/side_channel/__init__.py +63 -0
  340. oscura/side_channel/dpa.py +1025 -0
  341. oscura/utils/__init__.py +15 -1
  342. oscura/utils/bitwise.py +118 -0
  343. oscura/{builders → utils/builders}/__init__.py +1 -1
  344. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  345. oscura/{comparison → utils/comparison}/compare.py +202 -101
  346. oscura/{comparison → utils/comparison}/golden.py +83 -63
  347. oscura/{comparison → utils/comparison}/limits.py +313 -89
  348. oscura/{comparison → utils/comparison}/mask.py +151 -45
  349. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  350. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  351. oscura/{component → utils/component}/__init__.py +3 -3
  352. oscura/{component → utils/component}/impedance.py +122 -58
  353. oscura/{component → utils/component}/reactive.py +165 -168
  354. oscura/{component → utils/component}/transmission_line.py +3 -3
  355. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  356. oscura/{filtering → utils/filtering}/base.py +1 -1
  357. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  358. oscura/{filtering → utils/filtering}/design.py +169 -93
  359. oscura/{filtering → utils/filtering}/filters.py +2 -2
  360. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  361. oscura/utils/geometry.py +31 -0
  362. oscura/utils/imports.py +184 -0
  363. oscura/utils/lazy.py +1 -1
  364. oscura/{math → utils/math}/__init__.py +2 -2
  365. oscura/{math → utils/math}/arithmetic.py +114 -48
  366. oscura/{math → utils/math}/interpolation.py +139 -106
  367. oscura/utils/memory.py +129 -66
  368. oscura/utils/memory_advanced.py +92 -9
  369. oscura/utils/memory_extensions.py +10 -8
  370. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  371. oscura/{optimization → utils/optimization}/search.py +2 -2
  372. oscura/utils/performance/__init__.py +58 -0
  373. oscura/utils/performance/caching.py +889 -0
  374. oscura/utils/performance/lsh_clustering.py +333 -0
  375. oscura/utils/performance/memory_optimizer.py +699 -0
  376. oscura/utils/performance/optimizations.py +675 -0
  377. oscura/utils/performance/parallel.py +654 -0
  378. oscura/utils/performance/profiling.py +661 -0
  379. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  380. oscura/{pipeline → utils/pipeline}/composition.py +1 -1
  381. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  382. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  383. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  384. oscura/{search → utils/search}/__init__.py +3 -3
  385. oscura/{search → utils/search}/anomaly.py +188 -58
  386. oscura/utils/search/context.py +294 -0
  387. oscura/{search → utils/search}/pattern.py +138 -10
  388. oscura/utils/serial.py +51 -0
  389. oscura/utils/storage/__init__.py +61 -0
  390. oscura/utils/storage/database.py +1166 -0
  391. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  392. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  393. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  394. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  395. oscura/{triggering → utils/triggering}/base.py +6 -6
  396. oscura/{triggering → utils/triggering}/edge.py +2 -2
  397. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  398. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  399. oscura/{triggering → utils/triggering}/window.py +2 -2
  400. oscura/utils/validation.py +32 -0
  401. oscura/validation/__init__.py +121 -0
  402. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  403. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  404. oscura/{compliance → validation/compliance}/masks.py +1 -1
  405. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  406. oscura/{compliance → validation/compliance}/testing.py +114 -52
  407. oscura/validation/compliance_tests.py +915 -0
  408. oscura/validation/fuzzer.py +990 -0
  409. oscura/validation/grammar_tests.py +596 -0
  410. oscura/validation/grammar_validator.py +904 -0
  411. oscura/validation/hil_testing.py +977 -0
  412. oscura/{quality → validation/quality}/__init__.py +4 -4
  413. oscura/{quality → validation/quality}/ensemble.py +251 -171
  414. oscura/{quality → validation/quality}/explainer.py +3 -3
  415. oscura/{quality → validation/quality}/scoring.py +1 -1
  416. oscura/{quality → validation/quality}/warnings.py +4 -4
  417. oscura/validation/regression_suite.py +808 -0
  418. oscura/validation/replay.py +788 -0
  419. oscura/{testing → validation/testing}/__init__.py +2 -2
  420. oscura/{testing → validation/testing}/synthetic.py +5 -5
  421. oscura/visualization/__init__.py +9 -0
  422. oscura/visualization/accessibility.py +1 -1
  423. oscura/visualization/annotations.py +64 -67
  424. oscura/visualization/colors.py +7 -7
  425. oscura/visualization/digital.py +180 -81
  426. oscura/visualization/eye.py +236 -85
  427. oscura/visualization/interactive.py +320 -143
  428. oscura/visualization/jitter.py +587 -247
  429. oscura/visualization/layout.py +169 -134
  430. oscura/visualization/optimization.py +103 -52
  431. oscura/visualization/palettes.py +1 -1
  432. oscura/visualization/power.py +427 -211
  433. oscura/visualization/power_extended.py +626 -297
  434. oscura/visualization/presets.py +2 -0
  435. oscura/visualization/protocols.py +495 -181
  436. oscura/visualization/render.py +79 -63
  437. oscura/visualization/reverse_engineering.py +171 -124
  438. oscura/visualization/signal_integrity.py +460 -279
  439. oscura/visualization/specialized.py +190 -100
  440. oscura/visualization/spectral.py +670 -255
  441. oscura/visualization/thumbnails.py +166 -137
  442. oscura/visualization/waveform.py +150 -63
  443. oscura/workflows/__init__.py +3 -0
  444. oscura/{batch → workflows/batch}/__init__.py +5 -5
  445. oscura/{batch → workflows/batch}/advanced.py +150 -75
  446. oscura/workflows/batch/aggregate.py +531 -0
  447. oscura/workflows/batch/analyze.py +236 -0
  448. oscura/{batch → workflows/batch}/logging.py +2 -2
  449. oscura/{batch → workflows/batch}/metrics.py +1 -1
  450. oscura/workflows/complete_re.py +1144 -0
  451. oscura/workflows/compliance.py +44 -54
  452. oscura/workflows/digital.py +197 -51
  453. oscura/workflows/legacy/__init__.py +12 -0
  454. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  455. oscura/workflows/multi_trace.py +9 -9
  456. oscura/workflows/power.py +42 -62
  457. oscura/workflows/protocol.py +82 -49
  458. oscura/workflows/reverse_engineering.py +351 -150
  459. oscura/workflows/signal_integrity.py +157 -82
  460. oscura-0.7.0.dist-info/METADATA +661 -0
  461. oscura-0.7.0.dist-info/RECORD +591 -0
  462. oscura/batch/aggregate.py +0 -300
  463. oscura/batch/analyze.py +0 -139
  464. oscura/dsl/__init__.py +0 -73
  465. oscura/exceptions.py +0 -59
  466. oscura/exploratory/fuzzy.py +0 -513
  467. oscura/exploratory/sync.py +0 -384
  468. oscura/exporters/__init__.py +0 -94
  469. oscura/exporters/csv.py +0 -303
  470. oscura/exporters/exporters.py +0 -44
  471. oscura/exporters/hdf5.py +0 -217
  472. oscura/exporters/html_export.py +0 -701
  473. oscura/exporters/json_export.py +0 -291
  474. oscura/exporters/markdown_export.py +0 -367
  475. oscura/exporters/matlab_export.py +0 -354
  476. oscura/exporters/npz_export.py +0 -219
  477. oscura/exporters/spice_export.py +0 -210
  478. oscura/search/context.py +0 -149
  479. oscura/session/__init__.py +0 -34
  480. oscura/session/annotations.py +0 -289
  481. oscura/session/history.py +0 -313
  482. oscura/session/session.py +0 -520
  483. oscura/workflow/__init__.py +0 -13
  484. oscura-0.5.1.dist-info/METADATA +0 -583
  485. oscura-0.5.1.dist-info/RECORD +0 -481
  486. /oscura/core/{config.py → config/legacy.py} +0 -0
  487. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  488. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  489. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  490. /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
  491. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  492. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  493. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  494. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  495. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
  496. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
  497. {oscura-0.5.1.dist-info → oscura-0.7.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
+ ]