testerkit 0.4.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 (521) hide show
  1. testerkit/__init__.py +68 -0
  2. testerkit/_docs/_assets/operator-ui/channels/table.png +0 -0
  3. testerkit/_docs/_assets/operator-ui/dashboard/runs.png +0 -0
  4. testerkit/_docs/_assets/operator-ui/dashboard/stations.png +0 -0
  5. testerkit/_docs/_assets/operator-ui/events/filters.png +0 -0
  6. testerkit/_docs/_assets/operator-ui/events/table.png +0 -0
  7. testerkit/_docs/_assets/operator-ui/explore/chart.png +0 -0
  8. testerkit/_docs/_assets/operator-ui/explore/filters.png +0 -0
  9. testerkit/_docs/_assets/operator-ui/explore/plot-controls.png +0 -0
  10. testerkit/_docs/_assets/operator-ui/files/table.png +0 -0
  11. testerkit/_docs/_assets/operator-ui/launch/form.png +0 -0
  12. testerkit/_docs/_assets/operator-ui/metrics/filters.png +0 -0
  13. testerkit/_docs/_assets/operator-ui/metrics/pareto.png +0 -0
  14. testerkit/_docs/_assets/operator-ui/metrics/ppk.png +0 -0
  15. testerkit/_docs/_assets/operator-ui/metrics/yield.png +0 -0
  16. testerkit/_docs/_assets/operator-ui/results/detail-header.png +0 -0
  17. testerkit/_docs/_assets/operator-ui/results/detail-measurements.png +0 -0
  18. testerkit/_docs/_assets/operator-ui/results/detail-overview.png +0 -0
  19. testerkit/_docs/_assets/operator-ui/results/detail-steps.png +0 -0
  20. testerkit/_docs/_assets/operator-ui/results/stats.png +0 -0
  21. testerkit/_docs/_assets/operator-ui/results/table.png +0 -0
  22. testerkit/_docs/_assets/operator-ui/tour/designer.png +0 -0
  23. testerkit/_docs/_assets/operator-ui/tour/docs.png +0 -0
  24. testerkit/_docs/_assets/operator-ui/tour/fixtures.png +0 -0
  25. testerkit/_docs/_assets/operator-ui/tour/instruments.png +0 -0
  26. testerkit/_docs/_assets/operator-ui/tour/parts.png +0 -0
  27. testerkit/_docs/_assets/operator-ui/tour/profiles.png +0 -0
  28. testerkit/_docs/_assets/operator-ui/tour/stations.png +0 -0
  29. testerkit/_docs/_assets/operator-ui/tour/tests.png +0 -0
  30. testerkit/_docs/_assets/operator-ui/tour/uuts.png +0 -0
  31. testerkit/_docs/concepts/configuration/capabilities.md +447 -0
  32. testerkit/_docs/concepts/configuration/fixtures.md +335 -0
  33. testerkit/_docs/concepts/configuration/index.md +14 -0
  34. testerkit/_docs/concepts/configuration/parts.md +276 -0
  35. testerkit/_docs/concepts/configuration/stations.md +214 -0
  36. testerkit/_docs/concepts/data/data-stores.md +199 -0
  37. testerkit/_docs/concepts/data/event-log.md +185 -0
  38. testerkit/_docs/concepts/data/event-sourcing.md +76 -0
  39. testerkit/_docs/concepts/data/flight-streaming.md +58 -0
  40. testerkit/_docs/concepts/data/index.md +17 -0
  41. testerkit/_docs/concepts/data/sessions.md +78 -0
  42. testerkit/_docs/concepts/data/three-verbs.md +260 -0
  43. testerkit/_docs/concepts/execution/index.md +13 -0
  44. testerkit/_docs/concepts/execution/outcomes.md +199 -0
  45. testerkit/_docs/concepts/execution/step-hierarchy.md +170 -0
  46. testerkit/_docs/concepts/execution/step-manifest.md +115 -0
  47. testerkit/_docs/concepts/index.md +40 -0
  48. testerkit/_docs/concepts/overview/ai-integration.md +57 -0
  49. testerkit/_docs/concepts/overview/architecture.md +357 -0
  50. testerkit/_docs/concepts/overview/index.md +14 -0
  51. testerkit/_docs/concepts/overview/platform-vs-framework.md +147 -0
  52. testerkit/_docs/concepts/overview/pytest.md +53 -0
  53. testerkit/_docs/concepts/overview/tiers.md +46 -0
  54. testerkit/_docs/how-to/catalog/datasheet-to-test.md +169 -0
  55. testerkit/_docs/how-to/catalog/index.md +11 -0
  56. testerkit/_docs/how-to/configuration/configuring-stations.md +364 -0
  57. testerkit/_docs/how-to/configuration/custom-drivers.md +484 -0
  58. testerkit/_docs/how-to/configuration/index.md +13 -0
  59. testerkit/_docs/how-to/configuration/mock-mode.md +342 -0
  60. testerkit/_docs/how-to/data/benchmarking.md +120 -0
  61. testerkit/_docs/how-to/data/capture-an-artifact.md +84 -0
  62. testerkit/_docs/how-to/data/capture-waveform.md +110 -0
  63. testerkit/_docs/how-to/data/choosing-a-channel-verb.md +108 -0
  64. testerkit/_docs/how-to/data/compare-runs.md +120 -0
  65. testerkit/_docs/how-to/data/export-results.md +104 -0
  66. testerkit/_docs/how-to/data/find-flaky-tests.md +108 -0
  67. testerkit/_docs/how-to/data/grafana-dashboards.md +148 -0
  68. testerkit/_docs/how-to/data/index.md +23 -0
  69. testerkit/_docs/how-to/data/mcp-debug-failures.md +125 -0
  70. testerkit/_docs/how-to/data/mcp-query-runs.md +120 -0
  71. testerkit/_docs/how-to/data/querying-channels.md +117 -0
  72. testerkit/_docs/how-to/data/querying-events.md +85 -0
  73. testerkit/_docs/how-to/data/stream-live-channel.md +134 -0
  74. testerkit/_docs/how-to/execution/custom-operator-ui.md +136 -0
  75. testerkit/_docs/how-to/execution/index.md +21 -0
  76. testerkit/_docs/how-to/execution/limits.md +193 -0
  77. testerkit/_docs/how-to/execution/managing-sessions.md +100 -0
  78. testerkit/_docs/how-to/execution/multi-uut-testing.md +211 -0
  79. testerkit/_docs/how-to/execution/operator-prompts.md +151 -0
  80. testerkit/_docs/how-to/execution/profiles.md +304 -0
  81. testerkit/_docs/how-to/execution/spec-driven-testing.md +169 -0
  82. testerkit/_docs/how-to/execution/test-context.md +212 -0
  83. testerkit/_docs/how-to/execution/traceability.md +252 -0
  84. testerkit/_docs/how-to/execution/vector-expansion.md +394 -0
  85. testerkit/_docs/how-to/execution/writing-tests.md +363 -0
  86. testerkit/_docs/how-to/index.md +59 -0
  87. testerkit/_docs/how-to/overview/index.md +11 -0
  88. testerkit/_docs/how-to/overview/mcp-integration.md +168 -0
  89. testerkit/_docs/how-to/overview/operator-ui-tour.md +234 -0
  90. testerkit/_docs/integration/data/grafana.md +162 -0
  91. testerkit/_docs/integration/data/index.md +15 -0
  92. testerkit/_docs/integration/data/lakehouse-import.md +226 -0
  93. testerkit/_docs/integration/data/logging.md +131 -0
  94. testerkit/_docs/integration/data/results-api.md +51 -0
  95. testerkit/_docs/integration/index.md +20 -0
  96. testerkit/_docs/integration/runtime/harness.md +281 -0
  97. testerkit/_docs/integration/runtime/index.md +13 -0
  98. testerkit/_docs/integration/runtime/instruments.md +284 -0
  99. testerkit/_docs/integration/runtime/pytest-existing.md +315 -0
  100. testerkit/_docs/reference/catalog/cookbook.md +389 -0
  101. testerkit/_docs/reference/catalog/index.md +11 -0
  102. testerkit/_docs/reference/catalog/schema.md +375 -0
  103. testerkit/_docs/reference/cli.md +660 -0
  104. testerkit/_docs/reference/configuration.md +406 -0
  105. testerkit/_docs/reference/data/channels-schema.md +145 -0
  106. testerkit/_docs/reference/data/event-types.md +554 -0
  107. testerkit/_docs/reference/data/events-schema.md +120 -0
  108. testerkit/_docs/reference/data/files-schema.md +141 -0
  109. testerkit/_docs/reference/data/index.md +18 -0
  110. testerkit/_docs/reference/data/models.md +2229 -0
  111. testerkit/_docs/reference/data/outputs.md +82 -0
  112. testerkit/_docs/reference/data/parquet-schema.md +529 -0
  113. testerkit/_docs/reference/data/performance-limits.md +224 -0
  114. testerkit/_docs/reference/data/query-api.md +247 -0
  115. testerkit/_docs/reference/index.md +69 -0
  116. testerkit/_docs/reference/operator-ui/channels/detail.md +82 -0
  117. testerkit/_docs/reference/operator-ui/channels/list.md +70 -0
  118. testerkit/_docs/reference/operator-ui/dashboard.md +87 -0
  119. testerkit/_docs/reference/operator-ui/designer.md +167 -0
  120. testerkit/_docs/reference/operator-ui/events.md +89 -0
  121. testerkit/_docs/reference/operator-ui/files.md +74 -0
  122. testerkit/_docs/reference/operator-ui/fixtures.md +100 -0
  123. testerkit/_docs/reference/operator-ui/instruments.md +126 -0
  124. testerkit/_docs/reference/operator-ui/launch.md +79 -0
  125. testerkit/_docs/reference/operator-ui/live.md +102 -0
  126. testerkit/_docs/reference/operator-ui/measurements.md +159 -0
  127. testerkit/_docs/reference/operator-ui/metrics.md +217 -0
  128. testerkit/_docs/reference/operator-ui/parts.md +98 -0
  129. testerkit/_docs/reference/operator-ui/profiles.md +103 -0
  130. testerkit/_docs/reference/operator-ui/results/detail.md +214 -0
  131. testerkit/_docs/reference/operator-ui/results/list.md +106 -0
  132. testerkit/_docs/reference/operator-ui/stations.md +98 -0
  133. testerkit/_docs/reference/operator-ui/tests.md +173 -0
  134. testerkit/_docs/reference/operator-ui/uuts.md +67 -0
  135. testerkit/_docs/reference/overview/index.md +11 -0
  136. testerkit/_docs/reference/overview/pytest-native.md +142 -0
  137. testerkit/_docs/reference/overview/skills.md +177 -0
  138. testerkit/_docs/reference/pytest/fixtures.md +342 -0
  139. testerkit/_docs/reference/pytest/index.md +12 -0
  140. testerkit/_docs/reference/pytest/markers.md +257 -0
  141. testerkit/_docs/reference/runtime/api.md +246 -0
  142. testerkit/_docs/reference/runtime/client.md +280 -0
  143. testerkit/_docs/reference/runtime/connect.md +140 -0
  144. testerkit/_docs/reference/runtime/index.md +13 -0
  145. testerkit/_docs/tutorial/01-first-test.md +113 -0
  146. testerkit/_docs/tutorial/02-mock-instruments.md +88 -0
  147. testerkit/_docs/tutorial/03-fixtures.md +169 -0
  148. testerkit/_docs/tutorial/04-limits.md +139 -0
  149. testerkit/_docs/tutorial/05-configuration.md +194 -0
  150. testerkit/_docs/tutorial/06-specifications.md +273 -0
  151. testerkit/_docs/tutorial/07-real-instruments.md +245 -0
  152. testerkit/_docs/tutorial/08-capabilities.md +223 -0
  153. testerkit/_docs/tutorial/09-production.md +374 -0
  154. testerkit/_docs/tutorial/10-live-monitoring.md +156 -0
  155. testerkit/_docs/tutorial/11-waveforms-and-evidence.md +169 -0
  156. testerkit/_docs/tutorial/12-continuous-monitoring.md +145 -0
  157. testerkit/_docs/tutorial/13-parallel-testing.md +233 -0
  158. testerkit/_docs/tutorial/index.md +61 -0
  159. testerkit/_docs/tutorial/quickstart.md +282 -0
  160. testerkit/analysis/__init__.py +9 -0
  161. testerkit/analysis/_common.py +24 -0
  162. testerkit/analysis/measurement_facets.py +524 -0
  163. testerkit/analysis/measurements_query.py +1458 -0
  164. testerkit/analysis/metrics.py +389 -0
  165. testerkit/analysis/runs_query.py +434 -0
  166. testerkit/analysis/steps_query.py +413 -0
  167. testerkit/api/__init__.py +6 -0
  168. testerkit/api/_mime.py +71 -0
  169. testerkit/api/app.py +1171 -0
  170. testerkit/api/dialogs/__init__.py +36 -0
  171. testerkit/api/dialogs/manager.py +617 -0
  172. testerkit/api/dialogs/models.py +79 -0
  173. testerkit/api/models.py +80 -0
  174. testerkit/api/responses.py +205 -0
  175. testerkit/api/runner.py +222 -0
  176. testerkit/api/schemas.py +311 -0
  177. testerkit/benchmark/__init__.py +34 -0
  178. testerkit/benchmark/concurrency.py +248 -0
  179. testerkit/benchmark/core.py +232 -0
  180. testerkit/benchmark/runner.py +628 -0
  181. testerkit/benchmark/scenario.py +313 -0
  182. testerkit/benchmark/system.py +191 -0
  183. testerkit/benchmark/workloads.py +507 -0
  184. testerkit/catalog/__init__.py +6 -0
  185. testerkit/catalog/generic/generic_dmm.yaml +61 -0
  186. testerkit/catalog/generic/generic_eload.yaml +31 -0
  187. testerkit/catalog/generic/generic_fgen.yaml +56 -0
  188. testerkit/catalog/generic/generic_oscilloscope.yaml +45 -0
  189. testerkit/catalog/generic/generic_psu.yaml +77 -0
  190. testerkit/catalog/generic/generic_smu.yaml +73 -0
  191. testerkit/channels.py +538 -0
  192. testerkit/cli/__init__.py +32 -0
  193. testerkit/cli/_common.py +113 -0
  194. testerkit/cli/_time.py +179 -0
  195. testerkit/cli/benchmark_cmd.py +56 -0
  196. testerkit/cli/catalog_cmd.py +39 -0
  197. testerkit/cli/daemon.py +157 -0
  198. testerkit/cli/data_cmd.py +1040 -0
  199. testerkit/cli/discover_cmd.py +94 -0
  200. testerkit/cli/docs_cmd.py +100 -0
  201. testerkit/cli/instrument.py +172 -0
  202. testerkit/cli/mcp_cmd.py +37 -0
  203. testerkit/cli/metrics.py +366 -0
  204. testerkit/cli/project.py +312 -0
  205. testerkit/cli/root.py +14 -0
  206. testerkit/cli/runs.py +412 -0
  207. testerkit/cli/schema_cmd.py +87 -0
  208. testerkit/cli/serve_cmd.py +50 -0
  209. testerkit/cli/setup_cmd.py +604 -0
  210. testerkit/cli/station.py +294 -0
  211. testerkit/cli/validate.py +112 -0
  212. testerkit/client.py +463 -0
  213. testerkit/connect.py +635 -0
  214. testerkit/data/__init__.py +8 -0
  215. testerkit/data/_accumulator_pool.py +455 -0
  216. testerkit/data/_atomic.py +59 -0
  217. testerkit/data/_collection_indices.py +67 -0
  218. testerkit/data/_daemon_lifecycle.py +535 -0
  219. testerkit/data/_duckdb_daemon.py +695 -0
  220. testerkit/data/_duckdb_flight_server.py +813 -0
  221. testerkit/data/_event_filters.py +15 -0
  222. testerkit/data/_event_reader.py +101 -0
  223. testerkit/data/_flight_errors.py +199 -0
  224. testerkit/data/_flight_query.py +202 -0
  225. testerkit/data/_flight_retry.py +148 -0
  226. testerkit/data/_flight_subscribe.py +67 -0
  227. testerkit/data/_index_epoch.py +342 -0
  228. testerkit/data/_ipc_writer.py +185 -0
  229. testerkit/data/_json_safe.py +63 -0
  230. testerkit/data/_process.py +18 -0
  231. testerkit/data/_push_relay.py +124 -0
  232. testerkit/data/_runs_duckdb_daemon.py +2704 -0
  233. testerkit/data/_session_reaper.py +110 -0
  234. testerkit/data/_sql_helpers.py +30 -0
  235. testerkit/data/_store.py +24 -0
  236. testerkit/data/backends/__init__.py +7 -0
  237. testerkit/data/backends/_event_accumulator.py +957 -0
  238. testerkit/data/backends/_row_helpers.py +1172 -0
  239. testerkit/data/backends/parquet.py +1143 -0
  240. testerkit/data/backends/protocol.py +35 -0
  241. testerkit/data/channels/__init__.py +8 -0
  242. testerkit/data/channels/_flight_daemon.py +75 -0
  243. testerkit/data/channels/client.py +242 -0
  244. testerkit/data/channels/flight_manager.py +88 -0
  245. testerkit/data/channels/index.py +664 -0
  246. testerkit/data/channels/models.py +418 -0
  247. testerkit/data/channels/server.py +162 -0
  248. testerkit/data/channels/store.py +1481 -0
  249. testerkit/data/data_dir.py +61 -0
  250. testerkit/data/duckdb_manager.py +79 -0
  251. testerkit/data/event_log.py +384 -0
  252. testerkit/data/event_store.py +768 -0
  253. testerkit/data/events.py +1025 -0
  254. testerkit/data/exporters/__init__.py +6 -0
  255. testerkit/data/exporters/_helpers.py +40 -0
  256. testerkit/data/exporters/csv_exporter.py +168 -0
  257. testerkit/data/exporters/hdf5.py +234 -0
  258. testerkit/data/exporters/json_exporter.py +229 -0
  259. testerkit/data/exporters/mdf4.py +201 -0
  260. testerkit/data/exporters/stdf.py +255 -0
  261. testerkit/data/exporters/tdms.py +292 -0
  262. testerkit/data/files/__init__.py +71 -0
  263. testerkit/data/files/_backend.py +207 -0
  264. testerkit/data/files/_catalog_daemon.py +84 -0
  265. testerkit/data/files/catalog.py +218 -0
  266. testerkit/data/files/catalog_manager.py +325 -0
  267. testerkit/data/files/models.py +60 -0
  268. testerkit/data/files/serializers.py +414 -0
  269. testerkit/data/files/store.py +463 -0
  270. testerkit/data/files/streaming.py +783 -0
  271. testerkit/data/models.py +604 -0
  272. testerkit/data/ref.py +152 -0
  273. testerkit/data/retention.py +297 -0
  274. testerkit/data/run_store.py +367 -0
  275. testerkit/data/runs_duckdb_manager.py +103 -0
  276. testerkit/data/schema_dispatch.py +191 -0
  277. testerkit/data/schema_migrate.py +76 -0
  278. testerkit/data/schema_versions.py +85 -0
  279. testerkit/data/schemas.py +288 -0
  280. testerkit/data/subscribers/__init__.py +48 -0
  281. testerkit/data/subscribers/_base.py +20 -0
  282. testerkit/data/subscribers/_output_file.py +25 -0
  283. testerkit/data/subscribers/replay.py +55 -0
  284. testerkit/environment.py +84 -0
  285. testerkit/execution/__init__.py +11 -0
  286. testerkit/execution/_git.py +190 -0
  287. testerkit/execution/_state.py +740 -0
  288. testerkit/execution/accessors.py +54 -0
  289. testerkit/execution/audit.py +51 -0
  290. testerkit/execution/cascade.py +85 -0
  291. testerkit/execution/connections.py +491 -0
  292. testerkit/execution/harness.py +1931 -0
  293. testerkit/execution/instrument_events.py +42 -0
  294. testerkit/execution/limits.py +154 -0
  295. testerkit/execution/metadata.py +102 -0
  296. testerkit/execution/mocks.py +63 -0
  297. testerkit/execution/profiles.py +616 -0
  298. testerkit/execution/run_scope.py +1331 -0
  299. testerkit/execution/session_scope.py +196 -0
  300. testerkit/execution/sidecar.py +280 -0
  301. testerkit/execution/site_runner.py +705 -0
  302. testerkit/execution/sites.py +183 -0
  303. testerkit/execution/sync.py +294 -0
  304. testerkit/execution/uut_provider.py +231 -0
  305. testerkit/execution/vectors.py +205 -0
  306. testerkit/execution/verify.py +396 -0
  307. testerkit/expand.py +75 -0
  308. testerkit/files.py +235 -0
  309. testerkit/fixtures/__init__.py +6 -0
  310. testerkit/fixtures/manager.py +357 -0
  311. testerkit/grafana/__init__.py +1 -0
  312. testerkit/grafana/bootstrap.py +61 -0
  313. testerkit/grafana/cli.py +314 -0
  314. testerkit/grafana/dashboards/asset_utilization.json +92 -0
  315. testerkit/grafana/dashboards/channel_explorer.json +77 -0
  316. testerkit/grafana/dashboards/event_log.json +92 -0
  317. testerkit/grafana/dashboards/failure_pareto.json +104 -0
  318. testerkit/grafana/dashboards/measurement_distribution.json +97 -0
  319. testerkit/grafana/dashboards/measurement_trend.json +161 -0
  320. testerkit/grafana/dashboards/station_comparison.json +95 -0
  321. testerkit/grafana/dashboards/test_duration.json +104 -0
  322. testerkit/grafana/dashboards/unit_traceability.json +73 -0
  323. testerkit/grafana/dashboards/yield_overview.json +189 -0
  324. testerkit/grafana/provisioning/dashboards.yaml.j2 +12 -0
  325. testerkit/grafana/provisioning/datasources.yaml.j2 +17 -0
  326. testerkit/grafana/server.py +282 -0
  327. testerkit/init.py +724 -0
  328. testerkit/instruments/__init__.py +10 -0
  329. testerkit/instruments/base.py +82 -0
  330. testerkit/instruments/discovery/__init__.py +62 -0
  331. testerkit/instruments/discovery/_base.py +174 -0
  332. testerkit/instruments/discovery/lxi.py +152 -0
  333. testerkit/instruments/discovery/ni.py +95 -0
  334. testerkit/instruments/discovery/serial.py +73 -0
  335. testerkit/instruments/discovery/visa.py +98 -0
  336. testerkit/instruments/lifecycle.py +213 -0
  337. testerkit/instruments/loader.py +79 -0
  338. testerkit/instruments/locks.py +178 -0
  339. testerkit/instruments/mocks.py +230 -0
  340. testerkit/instruments/observer.py +270 -0
  341. testerkit/instruments/observers/__init__.py +36 -0
  342. testerkit/instruments/observers/_base.py +68 -0
  343. testerkit/instruments/observers/daqmx.py +70 -0
  344. testerkit/instruments/observers/descriptor.py +69 -0
  345. testerkit/instruments/observers/generic.py +92 -0
  346. testerkit/instruments/observers/lantz.py +31 -0
  347. testerkit/instruments/observers/modbus.py +96 -0
  348. testerkit/instruments/observers/motion.py +68 -0
  349. testerkit/instruments/observers/ni_modular.py +73 -0
  350. testerkit/instruments/observers/ophyd.py +80 -0
  351. testerkit/instruments/observers/pymeasure.py +120 -0
  352. testerkit/instruments/observers/qcodes.py +66 -0
  353. testerkit/instruments/observers/scpi.py +88 -0
  354. testerkit/instruments/observers/tektronix.py +57 -0
  355. testerkit/instruments/observers/visa.py +103 -0
  356. testerkit/instruments/pool.py +459 -0
  357. testerkit/instruments/proxy.py +59 -0
  358. testerkit/instruments/route_manager.py +329 -0
  359. testerkit/instruments/routed_proxy.py +88 -0
  360. testerkit/instruments/server.py +508 -0
  361. testerkit/instruments/switch.py +43 -0
  362. testerkit/instruments/visa.py +294 -0
  363. testerkit/matching/__init__.py +6 -0
  364. testerkit/matching/service.py +969 -0
  365. testerkit/mcp/__init__.py +6 -0
  366. testerkit/mcp/server.py +681 -0
  367. testerkit/mcp/tools.py +1868 -0
  368. testerkit/models/__init__.py +18 -0
  369. testerkit/models/capability.py +598 -0
  370. testerkit/models/catalog.py +76 -0
  371. testerkit/models/data_options.py +153 -0
  372. testerkit/models/enums.py +334 -0
  373. testerkit/models/instrument.py +152 -0
  374. testerkit/models/instrument_asset.py +25 -0
  375. testerkit/models/part.py +271 -0
  376. testerkit/models/part_manifest.py +96 -0
  377. testerkit/models/project.py +114 -0
  378. testerkit/models/station.py +140 -0
  379. testerkit/models/test_config.py +748 -0
  380. testerkit/ontology/__init__.py +39 -0
  381. testerkit/ontology/schema.py +188 -0
  382. testerkit/ontology/testerkit.yaml +1420 -0
  383. testerkit/parts/__init__.py +8 -0
  384. testerkit/parts/context.py +181 -0
  385. testerkit/parts/folder.py +251 -0
  386. testerkit/parts/loader.py +27 -0
  387. testerkit/prompts/__init__.py +17 -0
  388. testerkit/prompts/core.py +133 -0
  389. testerkit/pytest_plugin/__init__.py +1287 -0
  390. testerkit/pytest_plugin/autouse.py +369 -0
  391. testerkit/pytest_plugin/helpers.py +354 -0
  392. testerkit/pytest_plugin/hooks.py +1982 -0
  393. testerkit/pytest_plugin/markers.py +203 -0
  394. testerkit/pytest_plugin/retry.py +37 -0
  395. testerkit/pytest_plugin/sweeps.py +117 -0
  396. testerkit/queries.py +78 -0
  397. testerkit/reports/__init__.py +6 -0
  398. testerkit/reports/core.py +492 -0
  399. testerkit/reports/datasheet.py +831 -0
  400. testerkit/reports/templates/datasheet.html +383 -0
  401. testerkit/reports/templates/default.html +153 -0
  402. testerkit/sbom.py +142 -0
  403. testerkit/schema_export.py +101 -0
  404. testerkit/signals.py +63 -0
  405. testerkit/skills/README.md +58 -0
  406. testerkit/skills/templates/project-instructions.md +125 -0
  407. testerkit/skills/testerkit-analysis/SKILL.md +87 -0
  408. testerkit/skills/testerkit-capture/SKILL.md +114 -0
  409. testerkit/skills/testerkit-data/SKILL.md +138 -0
  410. testerkit/skills/testerkit-datasheets/SKILL.md +100 -0
  411. testerkit/skills/testerkit-datasheets/agents/scaffold-writer.md +115 -0
  412. testerkit/skills/testerkit-datasheets/agents/section-extractor.md +98 -0
  413. testerkit/skills/testerkit-datasheets/agents/section-reviewer.md +153 -0
  414. testerkit/skills/testerkit-datasheets/agents/section-splitter.md +89 -0
  415. testerkit/skills/testerkit-datasheets/agents/section-writer.md +226 -0
  416. testerkit/skills/testerkit-datasheets/references/catalog-pipeline.md +271 -0
  417. testerkit/skills/testerkit-datasheets/references/process-queue.md +42 -0
  418. testerkit/skills/testerkit-datasheets/references/scaffold.md +156 -0
  419. testerkit/skills/testerkit-datasheets/references/test-pipeline.md +312 -0
  420. testerkit/skills/testerkit-debug/SKILL.md +127 -0
  421. testerkit/skills/testerkit-interactive/SKILL.md +188 -0
  422. testerkit/skills/testerkit-interactive/references/live-ui-patterns.md +102 -0
  423. testerkit/skills/testerkit-mocks/SKILL.md +128 -0
  424. testerkit/skills/testerkit-parts/SKILL.md +126 -0
  425. testerkit/skills/testerkit-profiles/SKILL.md +139 -0
  426. testerkit/skills/testerkit-sites/SKILL.md +140 -0
  427. testerkit/skills/testerkit-stations/SKILL.md +146 -0
  428. testerkit/skills/testerkit-tests/SKILL.md +136 -0
  429. testerkit/store.py +1639 -0
  430. testerkit/ui/__init__.py +71 -0
  431. testerkit/ui/_asgi.py +162 -0
  432. testerkit/ui/app.py +32 -0
  433. testerkit/ui/components/__init__.py +13 -0
  434. testerkit/ui/components/artifact_viewer.py +250 -0
  435. testerkit/ui/components/channel_values.py +159 -0
  436. testerkit/ui/components/event_timeline.py +222 -0
  437. testerkit/ui/components/execution_gantt.py +210 -0
  438. testerkit/ui/components/file_streams.py +147 -0
  439. testerkit/ui/components/instrument_activity.py +146 -0
  440. testerkit/ui/components/session_table.py +118 -0
  441. testerkit/ui/pages/__init__.py +25 -0
  442. testerkit/ui/pages/channels/__init__.py +4 -0
  443. testerkit/ui/pages/channels/detail.py +932 -0
  444. testerkit/ui/pages/channels/list.py +515 -0
  445. testerkit/ui/pages/dashboard.py +236 -0
  446. testerkit/ui/pages/designer/__init__.py +5 -0
  447. testerkit/ui/pages/designer/graph.py +635 -0
  448. testerkit/ui/pages/designer/matching.py +663 -0
  449. testerkit/ui/pages/designer/page.py +593 -0
  450. testerkit/ui/pages/designer/properties.py +591 -0
  451. testerkit/ui/pages/designer/state.py +468 -0
  452. testerkit/ui/pages/docs/__init__.py +6 -0
  453. testerkit/ui/pages/docs/index.py +95 -0
  454. testerkit/ui/pages/docs/page.py +698 -0
  455. testerkit/ui/pages/events/__init__.py +3 -0
  456. testerkit/ui/pages/events/list.py +263 -0
  457. testerkit/ui/pages/explore.py +1108 -0
  458. testerkit/ui/pages/files/__init__.py +4 -0
  459. testerkit/ui/pages/files/detail.py +426 -0
  460. testerkit/ui/pages/files/list.py +447 -0
  461. testerkit/ui/pages/fixtures/__init__.py +8 -0
  462. testerkit/ui/pages/fixtures/detail.py +280 -0
  463. testerkit/ui/pages/fixtures/edit.py +370 -0
  464. testerkit/ui/pages/fixtures/list.py +180 -0
  465. testerkit/ui/pages/fixtures/new.py +195 -0
  466. testerkit/ui/pages/instruments/__init__.py +4 -0
  467. testerkit/ui/pages/instruments/detail.py +320 -0
  468. testerkit/ui/pages/instruments/edit.py +482 -0
  469. testerkit/ui/pages/instruments/list.py +249 -0
  470. testerkit/ui/pages/instruments/new.py +200 -0
  471. testerkit/ui/pages/launch.py +261 -0
  472. testerkit/ui/pages/live.py +124 -0
  473. testerkit/ui/pages/metrics_page.py +1229 -0
  474. testerkit/ui/pages/parts/__init__.py +11 -0
  475. testerkit/ui/pages/parts/detail.py +202 -0
  476. testerkit/ui/pages/parts/edit.py +310 -0
  477. testerkit/ui/pages/parts/list.py +160 -0
  478. testerkit/ui/pages/parts/new.py +170 -0
  479. testerkit/ui/pages/parts/requirements.py +153 -0
  480. testerkit/ui/pages/parts/stations.py +199 -0
  481. testerkit/ui/pages/profiles/__init__.py +4 -0
  482. testerkit/ui/pages/profiles/detail.py +113 -0
  483. testerkit/ui/pages/profiles/list.py +86 -0
  484. testerkit/ui/pages/results/__init__.py +4 -0
  485. testerkit/ui/pages/results/detail.py +631 -0
  486. testerkit/ui/pages/results/list.py +230 -0
  487. testerkit/ui/pages/stations/__init__.py +4 -0
  488. testerkit/ui/pages/stations/detail.py +256 -0
  489. testerkit/ui/pages/stations/edit.py +273 -0
  490. testerkit/ui/pages/stations/list.py +180 -0
  491. testerkit/ui/pages/stations/new.py +311 -0
  492. testerkit/ui/pages/tests/__init__.py +4 -0
  493. testerkit/ui/pages/tests/detail.py +152 -0
  494. testerkit/ui/pages/tests/list.py +194 -0
  495. testerkit/ui/pages/uuts/__init__.py +4 -0
  496. testerkit/ui/pages/uuts/list.py +97 -0
  497. testerkit/ui/shared/__init__.py +8 -0
  498. testerkit/ui/shared/components.py +1826 -0
  499. testerkit/ui/shared/dialogs.py +105 -0
  500. testerkit/ui/shared/event_binding.py +194 -0
  501. testerkit/ui/shared/layout.py +314 -0
  502. testerkit/ui/shared/services.py +1719 -0
  503. testerkit/ui/shared/timestamps.py +21 -0
  504. testerkit/ui/static/branding/favicon-16.png +0 -0
  505. testerkit/ui/static/branding/favicon-32.png +0 -0
  506. testerkit/ui/static/branding/favicon-48.png +0 -0
  507. testerkit/ui/static/branding/testerkit-icon-256.png +0 -0
  508. testerkit/ui/static/branding/testerkit-mark.svg +1 -0
  509. testerkit/ui/static/branding/testerkit-wordmark.svg +1 -0
  510. testerkit/ui/static/global.css +465 -0
  511. testerkit/utils/__init__.py +17 -0
  512. testerkit/utils/enum_meta.py +800 -0
  513. testerkit/utils/paths.py +75 -0
  514. testerkit/utils/ranges.py +257 -0
  515. testerkit/validation.py +82 -0
  516. testerkit/verbs.py +142 -0
  517. testerkit-0.4.0.dist-info/METADATA +250 -0
  518. testerkit-0.4.0.dist-info/RECORD +521 -0
  519. testerkit-0.4.0.dist-info/WHEEL +4 -0
  520. testerkit-0.4.0.dist-info/entry_points.txt +5 -0
  521. testerkit-0.4.0.dist-info/licenses/LICENSE +191 -0
@@ -0,0 +1,233 @@
1
+ # Step 13: Parallel Testing
2
+
3
+ **Goal:** Scale one bench from one UUT to two, tested at the same time, with zero changes to test code.
4
+
5
+ ## Prerequisites
6
+
7
+ - [Step 7: Real Instruments](07-real-instruments.md) — station YAML, instrument channels
8
+ - [Step 9: Production Ready](09-production.md) — fixture YAML, pin-to-instrument mapping, sidecar limits
9
+
10
+ ## The scenario
11
+
12
+ One bench, two UUT positions, tested in parallel. Each position is a **site** — the same parallel-position concept STDF calls `SITE_NUM` and NI TestStand calls a "test socket." A fixture that declares two or more sites turns a bare `pytest` invocation into a parallel run: no `--fixture` flag, no test rewrite, no per-UUT script.
13
+
14
+ A site's identity is its **0-based position in the fixture's `sites:` list** — `site_index`. It's never a field you set; it's just where the site sits in the list. `site_index 0` gets an optional `name:` label — a human-readable tag like `left` that has no bearing on execution order.
15
+
16
+ ## Growing a fixture to two sites
17
+
18
+ A single-UUT fixture wires `connections:` directly. A multi-UUT fixture replaces that with `sites:` — an ordered list, one entry per UUT position, each with its own `connections:`:
19
+
20
+ ```yaml
21
+ # fixtures/dual_site_bench.yaml
22
+ id: dual_site_bench
23
+ name: Dual-Site Bench Fixture
24
+ description: >-
25
+ Two-UUT parallel fixture — left/right sites share the bench's psu
26
+ and dmm but land on different instrument channels.
27
+ part_id: dual_rail_uut
28
+ station_types: [bench]
29
+ sites:
30
+ - name: left
31
+ connections:
32
+ vout:
33
+ name: vout
34
+ instrument: dmm
35
+ instrument_channel: "1"
36
+ instrument_terminal: hi
37
+ uut_pin: TP_VOUT
38
+ net: VOUT_3V3
39
+ vin:
40
+ name: vin
41
+ instrument: psu
42
+ instrument_channel: "1"
43
+ instrument_terminal: hi
44
+ uut_pin: TP_VIN
45
+ net: VIN_5V
46
+ - name: right
47
+ connections:
48
+ vout:
49
+ name: vout
50
+ instrument: dmm
51
+ instrument_channel: "2"
52
+ instrument_terminal: hi
53
+ uut_pin: TP_VOUT
54
+ net: VOUT_3V3
55
+ vin:
56
+ name: vin
57
+ instrument: psu
58
+ instrument_channel: "2"
59
+ instrument_terminal: hi
60
+ uut_pin: TP_VIN
61
+ net: VIN_5V
62
+ ```
63
+
64
+ `left` is `site_index 0`, `right` is `site_index 1` — list position, not an authored field. Both sites route to the same instrument roles (`dmm`, `psu`) on the station, but each lands on its own `instrument_channel` — `left` on channel `"1"`, `right` on channel `"2"`. That's the whole trick: one physical DMM, one physical PSU, two UUT positions, no collision.
65
+
66
+ Two or more sites makes this fixture **multi-site**, which is what fans a bare `pytest` invocation into parallel execution. One site is just the single-UUT case you already know — `connections:` directly on the fixture is shorthand for one unnamed site. For the full field-by-field schema, see [Fixture YAML reference](../reference/configuration.md#fixture-yaml); for the design rationale behind sites and shared instruments, see [Fixtures → Multi-UUT scaling](../concepts/configuration/fixtures.md#multi-uut-scaling-sites-shared-instruments-switching).
67
+
68
+ ## The station and the test don't change
69
+
70
+ The station YAML declares `psu` and `dmm` exactly as a single-UUT bench would — it has no idea there are two sites:
71
+
72
+ ```yaml
73
+ # stations/bench_dual.yaml
74
+ id: bench_dual
75
+ name: Dual-Site Bench
76
+ station_type: bench
77
+ instruments:
78
+ psu:
79
+ type: psu
80
+ driver: drivers.PSU
81
+ resource: TCPIP::192.168.1.101::INSTR
82
+ mock_config:
83
+ set_voltage: 5.0
84
+ set_current: 0.5
85
+ measure_voltage: 5.0
86
+ measure_current: 0.042
87
+ dmm:
88
+ type: dmm
89
+ driver: drivers.DMM
90
+ resource: TCPIP::192.168.1.102::INSTR
91
+ mock_config:
92
+ measure_dc_voltage: 3.3
93
+ measure_dc_current: 0.042
94
+ ```
95
+
96
+ Neither does the test:
97
+
98
+ ```python
99
+ # tests/test_dual_rail.py
100
+
101
+ def test_vout_within_spec(verify, psu, dmm, context) -> None:
102
+ """5 V PSU input -> ~3.3 V DMM readback, on this site's connections."""
103
+ psu.set_voltage(5.0)
104
+ psu.set_current(0.5)
105
+ for _ in context.connections:
106
+ verify("vout", dmm.measure_dc_voltage())
107
+ ```
108
+
109
+ ```yaml
110
+ # tests/test_dual_rail.yaml — sidecar
111
+ tests:
112
+ test_vout_within_spec:
113
+ connections: [vout]
114
+ limits:
115
+ vout: {low: 3.2, high: 3.4, unit: V}
116
+ ```
117
+
118
+ `context.connections` iterates the connections the sidecar's `connections: [vout]` list resolved for *this site* — see the [`connections` fixture reference](../reference/pytest/fixtures.md#connections-function). The test never names `left`, `right`, a channel, or an index; it just reads `dmm` and `psu` through its own site's wiring. The site-specific channel routing lives entirely in the fixture YAML from the previous section — that's what let the station and test files stay unmodified while the fixture grew from one site to two.
119
+
120
+ ## Launching a parallel run
121
+
122
+ `pytest.ini` assigns a serial to each named site by default, so a bare `pytest` is already a complete parallel run:
123
+
124
+ ```ini
125
+ # pytest.ini
126
+ [pytest]
127
+ addopts = --mock-instruments --uut-serials left=SN-A,right=SN-B
128
+ ```
129
+
130
+ ```cli
131
+ cd examples/12-parallel-sites
132
+ uv run pytest -q
133
+ ```
134
+
135
+ ```
136
+ [site:0] . [100%]
137
+ [site:0] 1 passed in 0.43s
138
+ [site:1] . [100%]
139
+ [site:1] 1 passed in 0.43s
140
+
141
+ ============================================================
142
+ Multi-UUT Results
143
+ ============================================================
144
+ site[0]: PASS 1 passed in 0.43s
145
+ site[1]: PASS 1 passed in 0.43s
146
+ ============================================================
147
+ ```
148
+
149
+ A fixture with 2+ sites is what triggers this: the parent process (the **orchestrator**) spawns one **worker** subprocess per site, each running the full test session against its own site's connections. `[site:N]` prefixes each worker's stdout as it streams back — the two lines interleave as the workers run concurrently, so `[site:1]` sometimes reports before `[site:0]`; the order isn't guaranteed. Below the per-site output, the `Multi-UUT Results` block is the orchestrator's own summary once both workers exit.
150
+
151
+ You'll also see pytest print `no tests ran` from the orchestrator process itself — it collected zero tests directly (both workers did the real collecting), so this line is harmless.
152
+
153
+ ## Reading the result
154
+
155
+ Two workers means two independent runs, each with its own serial:
156
+
157
+ ```cli
158
+ uv run testerkit runs --limit 5
159
+ ```
160
+
161
+ ```
162
+ Run ID Started UUT Serial Project Station Outcome
163
+ ----------------------------------------------------------------------------------------------------------
164
+ 437bdc0e 2026-07-01T18:00:00-0600 SN-B parallel-sites-example bench_dual passed
165
+ 7fa161df 2026-07-01T18:00:00-0600 SN-A parallel-sites-example bench_dual passed
166
+ ```
167
+
168
+ Site 0 (`left`) ran as `SN-A`, site 1 (`right`) ran as `SN-B`. Each run's parquet carries the `site_index` / `site_name` it was frozen with at start — `SN-A` recorded `site_index=0, site_name="left"`, `SN-B` recorded `site_index=1, site_name="right"`. That freeze point matters: rename a site in the fixture YAML next month and this run's rows still read the name that was active when it ran. See [Multi-UUT testing → Parquet Data](../how-to/execution/multi-uut-testing.md#parquet-data) for the DuckDB query shape over those columns.
169
+
170
+ Give it a few seconds before querying — ingest lands a beat after the summary prints. If `testerkit runs` comes back empty right after the run, re-run the command.
171
+
172
+ ## Experimenting with serial assignment
173
+
174
+ `pytest.ini`'s `addopts` is a default, not a lock — any `--uut-serials` / `--site` / `--uut-serial` on the command line overrides it for that invocation.
175
+
176
+ **Positional** — one serial per site, in fixture list order (`left` gets the first):
177
+
178
+ ```cli
179
+ uv run pytest -q --uut-serials SN1,SN2
180
+ ```
181
+
182
+ ```
183
+ [site:0] . [100%]
184
+ [site:0] 1 passed in 0.42s
185
+ [site:1] . [100%]
186
+ [site:1] 1 passed in 0.42s
187
+
188
+ ============================================================
189
+ Multi-UUT Results
190
+ ============================================================
191
+ site[0]: PASS 1 passed in 0.42s
192
+ site[1]: PASS 1 passed in 0.42s
193
+ ============================================================
194
+ ```
195
+
196
+ **Indexed** (`site_index=serial`, any order) and **named** (`site_name=serial`) produce the same 2-worker run — only how the serial maps to a site differs:
197
+
198
+ ```cli
199
+ uv run pytest -q --uut-serials 0=SN1,1=SN2
200
+ uv run pytest -q --uut-serials left=SN1,right=SN2
201
+ ```
202
+
203
+ **Single-lane** — target one site, single-process, no orchestrator:
204
+
205
+ ```cli
206
+ uv run pytest -q --site left --uut-serial SN1
207
+ ```
208
+
209
+ ```
210
+ . [100%]
211
+ 1 passed in 0.43s
212
+ ```
213
+
214
+ No `[site:N]` prefix, no `Multi-UUT Results` banner — this ran in the current process. It still records exactly as `left` would inside a parallel run (`site_index=0, site_name="left"`), which makes it the right tool for debugging one UUT position without waiting on its neighbor. `--site` always runs single-process, even against a multi-site fixture; pair it with `--uut-serial` (singular), not `--uut-serials`.
215
+
216
+ ## Why the shared instrument doesn't collide
217
+
218
+ `psu` and `dmm` are one physical box each in this example, shared by both sites. TesterKit serializes calls to a shared instrument so `left` and `right` never talk to it at the same instant — you didn't write a lock anywhere in `test_dual_rail.py` for that to be true. Mock instruments are the one exception: under `--mock-instruments` each site gets its own mock state, so a fault injected on one site's `dmm` never leaks into the other's.
219
+
220
+ For sync points, per-site environment variables, and debugging a hung or failing site, see [Multi-UUT testing](../how-to/execution/multi-uut-testing.md) — the full reference this chapter walked through.
221
+
222
+ ## You've completed the tutorial
223
+
224
+ You've taken a suite from a bare `conftest.py` with one mock fixture through production traceability, live monitoring, and now parallel execution across multiple UUT positions on one bench. The full worked example for this step lives in [`examples/12-parallel-sites`](https://github.com/pragmatest-dev/testerkit/tree/main/examples/12-parallel-sites) — clone it, wipe its `data/` directory, and re-run any of the commands above against real output of your own.
225
+
226
+ ## Next Steps
227
+
228
+ - [Multi-UUT testing](../how-to/execution/multi-uut-testing.md) — the full recipe: sync points, environment variables, debugging failures
229
+ - [Fixtures → Multi-UUT scaling](../concepts/configuration/fixtures.md#multi-uut-scaling-sites-shared-instruments-switching) — the design behind sites, shared instruments, and switched routing
230
+ - [Configuration reference](../reference/configuration.md#fixture-yaml) — fixture YAML field-by-field
231
+ - [CLI reference](../reference/cli.md) — every `testerkit` command
232
+
233
+ ← [Step 12: Continuous Monitoring](12-continuous-monitoring.md) | [Tutorial index](index.md)
@@ -0,0 +1,61 @@
1
+ # Tutorials
2
+
3
+ Two independent ways to learn TesterKit — pick the one that fits how you want to start. They are not a sequence: the Quick Start is **not** "step 0" of the step-by-step path.
4
+
5
+ ## Quick Start
6
+
7
+ `testerkit init quick_start --starter` scaffolds a complete project — part spec, station, fixture, a test, and config — that passes `pytest` immediately with mock instruments. Best when you want to see the whole thing working first, then read the walkthrough to understand each piece.
8
+
9
+ ```bash
10
+ pip install testerkit
11
+ testerkit init quick_start --starter && cd quick_start
12
+ pytest
13
+ ```
14
+
15
+ [Quick Start walkthrough →](quickstart.md)
16
+
17
+ ## Step-by-step tutorial
18
+
19
+ Start from nothing — a bare `conftest.py` with one mock fixture — and add a single concept per step, up to a production-ready, fully traceable suite with live monitoring. Best when you want to learn each piece as you introduce it; each step builds on the previous one.
20
+
21
+ | Step | Goal | What You'll Learn |
22
+ |------|------|-------------------|
23
+ | [1. First Test](01-first-test.md) | Run something | pytest, project structure |
24
+ | [2. Running Without Hardware](02-mock-instruments.md) | Use mock mode | `--mock-instruments`, `mock_config` (station-config block of canned return values used when `--mock-instruments` is on) |
25
+ | [3. pytest-native tests](03-fixtures.md) | Log measurements | the core per-test [TesterKit fixtures](../reference/overview/pytest-native.md) — `context` is the ambient run/UUT/station/vector state (always available; carries sweep params when present, observations always), `verify` records the measurement row AND raises on FAIL, `measure` records the row without raising (plus ~17 other public fixtures the plugin exposes — see [testerkit-fixtures](../reference/pytest/fixtures.md)) |
26
+ | [4. Add Limits](04-limits.md) | Pass/fail criteria | [Limit](../reference/data/models.md), Measurement, [Outcome](../reference/data/models.md#enum-outcome) |
27
+ | [5. Test Configuration](05-configuration.md) | Configure in YAML | [sidecar](05-configuration.md) (YAML next to a test file carrying vectors and limits) |
28
+ | [6. Part Specifications](06-specifications.md) | Define [parts](../concepts/configuration/parts.md) | parts/*.yaml, [characteristics](../concepts/configuration/capabilities.md) |
29
+ | [7. Real Instruments](07-real-instruments.md) | Talk to hardware | [stations/*.yaml](../concepts/configuration/stations.md), VISA, simulation |
30
+ | [8. Capability Matching](08-capabilities.md) | Find compatible [stations](../concepts/configuration/stations.md) | [direction flip, matching API](../concepts/configuration/capabilities.md) |
31
+ | [9. Production Ready](09-production.md) | Full [traceability](../how-to/execution/traceability.md) | [fixtures](../concepts/configuration/fixtures.md), test classes, sidecar |
32
+ | [10. Live Monitoring](10-live-monitoring.md) | Monitor in real time | [sessions](../concepts/data/sessions.md), [events](../concepts/data/event-log.md), [channels](../concepts/data/data-stores.md) |
33
+ | [11. Waveforms and Evidence](11-waveforms-and-evidence.md) | Capture a scope waveform and judge derived scalars | [three verbs](../concepts/data/three-verbs.md), `observe`, ChannelStore |
34
+ | [12. Continuous Monitoring](12-continuous-monitoring.md) | Stream live DMM data from an interactive session into the operator UI | [three verbs](../concepts/data/three-verbs.md), `channels.stream`, interactive `connect` |
35
+ | [13. Parallel Testing](13-parallel-testing.md) | Run one bench against two UUTs at once | multi-UUT [sites](../concepts/configuration/fixtures.md#multi-uut-scaling-sites-shared-instruments-switching), `site_index` / `site_name`, `--uut-serials`, the orchestrator/worker split |
36
+
37
+ ## Prerequisites
38
+
39
+ - Python 3.11 or later
40
+ - Basic familiarity with pytest
41
+ - TesterKit installed (`pip install testerkit`)
42
+
43
+ ## Time Commitment
44
+
45
+ Each step takes 10-15 minutes. You can complete the entire tutorial in about 2 hours, or work through it over several sessions.
46
+
47
+ ## What You'll Build
48
+
49
+ By the end of this tutorial, you'll have:
50
+
51
+ 1. A working test suite for a voltage converter
52
+ 2. YAML-based configuration for limits and test parameters
53
+ 3. Tests that run with real instruments OR in simulation mode
54
+ 4. Automatic station matching based on required capabilities
55
+ 5. Full traceability from requirements to results
56
+ 6. Live monitoring of test sessions with event queries
57
+
58
+ ## Ready?
59
+
60
+ - See it working now → [Quick Start](quickstart.md)
61
+ - Learn it from the ground up → [Step 1: First Test](01-first-test.md)
@@ -0,0 +1,282 @@
1
+ # Quick Start
2
+
3
+ Install TesterKit, scaffold a starter project, and run a passing test. Requires Python 3.11+.
4
+
5
+ ```bash
6
+ # 1. Install TesterKit
7
+ pip install testerkit
8
+
9
+ # 2. Create a starter project
10
+ testerkit init quick_start --starter
11
+ cd quick_start
12
+
13
+ # 3. Run the test
14
+ pytest
15
+ ```
16
+
17
+ The starter ships a single test (`test_output_voltage`). Run it and pytest reports `1 passed` — the measurement was taken against a mock instrument, checked against its limit, and recorded.
18
+
19
+ > **Explore without hardware.** [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/pragmatest-dev/testerkit-starter) opens a browser sandbox to try TesterKit with no install — mock-instrument tests, the operator UI, analytics, and AI integration. Real instrument control (PyVISA/serial to a bench) needs a local install, so the sandbox stops there.
20
+
21
+ > **Concepts cheat-sheet.** This quick start runs a complete TesterKit project, so it touches nearly every TesterKit concept at once — most for the first time. Each term below links forward to the tutorial step that introduces it properly:
22
+ >
23
+ > - **Part spec** — `parts/*.yaml`. Describes the device under test. → [Step 6](06-specifications.md), [concepts/parts](../concepts/configuration/parts.md)
24
+ > - **Station YAML** — `stations/*.yaml`. Declares the bench's instruments. → [Step 7](07-real-instruments.md), [concepts/stations](../concepts/configuration/stations.md)
25
+ > - **Sidecar YAML** — `tests/test_<module>.yaml`. Carries limits, sweeps, mocks for tests in that module. → [Step 5](05-configuration.md)
26
+ > - **`verify` / `measure` / `context` fixtures** — three of the fixtures TesterKit contributes. → [Step 3](03-fixtures.md), [reference/testerkit-fixtures](../reference/pytest/fixtures.md)
27
+ > - **`@pytest.mark.testerkit_limits`** — one of the seven TesterKit markers; pins a limit at the top of a test. → [Step 4](04-limits.md), [reference/testerkit-markers](../reference/pytest/markers.md)
28
+ > - **`mock_config`** — Per-instrument return values for mock mode. → [Step 2](02-mock-instruments.md), [how-to/mock-mode](../how-to/configuration/mock-mode.md)
29
+ > - **Characteristics, bands, accuracy, `when:`** — Part-spec vocabulary. → [Step 6](06-specifications.md), [reference/catalog-schema](../reference/catalog/schema.md)
30
+ > - **Capability matching** — How TesterKit pairs a part to a station. → [Step 8](08-capabilities.md), [concepts/capabilities](../concepts/configuration/capabilities.md)
31
+ > - **MCP** — Model Context Protocol; how AI agents drive TesterKit. → [how-to/mcp-integration](../how-to/overview/mcp-integration.md)
32
+
33
+ ## Project Structure
34
+
35
+ TesterKit projects follow a standard folder structure. The UI is driven by these folders.
36
+
37
+ ```
38
+ quick_start/
39
+ ├── testerkit.yaml # Project config (data_dir, default station, mock mode)
40
+ ├── parts/ # WHAT you're testing
41
+ │ └── example_part.yaml # Part specification
42
+ ├── stations/ # WHERE you test
43
+ │ └── starter_station.yaml # Instruments + addresses
44
+ ├── fixtures/ # HOW parts are connected to instruments
45
+ │ └── example_fixture.yaml # Pin-to-channel mappings
46
+ ├── instruments/ # Instrument asset records (identity, calibration)
47
+ │ ├── psu.yaml
48
+ │ └── dmm.yaml
49
+ ├── tests/ # Test code + sidecar config
50
+ │ ├── conftest.py # Custom fixtures (optional — roles auto-register)
51
+ │ ├── test_example.py # Test functions
52
+ │ └── test_example.yaml # Sidecar (limits, sweeps, mocks)
53
+ ├── data/ # Output (gitignored)
54
+ └── pyproject.toml
55
+ ```
56
+
57
+ ## Understanding the Starter Project
58
+
59
+ When you run `testerkit init quick_start --starter`, it generates all of these files. Here's what each one does:
60
+
61
+ ### Part Spec (`parts/example_part.yaml`)
62
+
63
+ ```yaml
64
+ # parts/example_part.yaml
65
+ id: "example_part"
66
+ name: "Example Part"
67
+ description: "Auto-generated example part specification"
68
+
69
+ pins:
70
+ TP_VOUT: {name: "TP1", net: "VOUT_3V3", description: "Output voltage test point"}
71
+
72
+ characteristics:
73
+ output_voltage:
74
+ function: "dc_voltage"
75
+ direction: "output"
76
+ unit: "V"
77
+ pin: "TP_VOUT"
78
+ bands:
79
+ - value: 3.3
80
+ accuracy: {pct_reading: 2.0}
81
+ ```
82
+
83
+ ### Station Config (`stations/starter_station.yaml`)
84
+
85
+ ```yaml
86
+ # stations/starter_station.yaml
87
+ id: "starter_station"
88
+ name: "Starter Station"
89
+ description: "Auto-generated starter station with mock instruments"
90
+
91
+ instruments:
92
+ psu:
93
+ type: "psu"
94
+ resource: "TCPIP::192.168.1.100::INSTR"
95
+ mock: true # Start with mocks, switch to real hardware later
96
+ mock_config: {set_voltage: null, enable_output: null, measure_voltage: 5.0, measure_current: 0.25}
97
+
98
+ dmm:
99
+ type: "dmm"
100
+ resource: "TCPIP::192.168.1.101::INSTR"
101
+ mock: true
102
+ mock_config: {measure_dc_voltage: 3.3}
103
+ ```
104
+
105
+ For real hardware, just remove `mock: true`. TesterKit uses [PyVISA](https://pyvisa.readthedocs.io/) directly:
106
+
107
+ ```yaml
108
+ # stations/bench_1.yaml - Real hardware
109
+ id: bench_1
110
+ name: "Test Bench 1"
111
+
112
+ instruments:
113
+ dmm:
114
+ type: dmm
115
+ resource: "TCPIP::192.168.1.100::INSTR"
116
+ # No mock: true → uses PyVISA, fixture has .query()/.write()
117
+ ```
118
+
119
+ Or use [PyMeasure](https://pymeasure.readthedocs.io/) for high-level drivers (100+ instruments):
120
+
121
+ ```yaml
122
+ instruments:
123
+ dmm:
124
+ type: dmm
125
+ driver: pymeasure.instruments.keysight.Keysight34461A
126
+ resource: "TCPIP::192.168.1.100::INSTR"
127
+ ```
128
+
129
+ ### Test Code (`tests/test_example.py`)
130
+
131
+ Tests are **plain pytest** — no decorator, no base class. TesterKit's pytest integration contributes [fixtures](../reference/pytest/fixtures.md) (the per-test `context` / `verify` / `measure`, plus `pins`, `instruments`, per-role auto-fixtures from the station YAML, etc.) and [seven markers](../reference/pytest/markers.md). For how TesterKit tests use pytest's own collection / fixture / marker mechanisms see [pytest-native reference](../reference/overview/pytest-native.md).
132
+
133
+ ```python
134
+ # tests/test_example.py
135
+ def test_output_voltage(context, psu, dmm, verify) -> None:
136
+ """Verify output voltage is within spec."""
137
+ vin = context.get_param("vin", 5.0)
138
+ psu.set_voltage(vin)
139
+ psu.enable_output()
140
+ # verify() resolves the limit from the sidecar / part YAML,
141
+ # records the measurement, and raises on fail.
142
+ verify("output_voltage", float(dmm.measure_dc_voltage()))
143
+ ```
144
+
145
+ For measurements that don't come from the part spec, use `measure(name, value, limit={"low": ..., "high": ..., "unit": "V"})` with inline limits or a sidecar `test_<module>.yaml`.
146
+
147
+ ### Sidecar (`tests/test_example.yaml`)
148
+
149
+ Sidecar YAML carries limits, sweeps, and mocks alongside the test file. A top-level key applies to every test in the module; per-test overrides go under `tests:`. The starter ships a per-test limit:
150
+
151
+ ```yaml
152
+ # tests/test_example.yaml
153
+ tests:
154
+ test_output_voltage:
155
+ limits:
156
+ output_voltage:
157
+ low: 3.234
158
+ high: 3.366
159
+ unit: V
160
+ ```
161
+
162
+ Sweeps and mocks live here too — e.g. a module-level `sweeps: [{vin: [5.0]}]` to parametrize, or `mocks: [{target: dmm.measure_dc_voltage, return_value: 3.31}]` (the starter instead sets mock returns in the station's `mock_config`).
163
+
164
+ ### Running Tests
165
+
166
+ The starter's `pyproject.toml` bakes the station, mock mode, and a UUT serial into `addopts` (and `testerkit.yaml` sets the same defaults), so the everyday command is just:
167
+
168
+ ```bash
169
+ pytest
170
+ ```
171
+
172
+ That expands to the explicit form below — useful when you want to override a default or run from outside the project:
173
+
174
+ ```bash
175
+ # Mock-instrument run (the starter's default)
176
+ pytest tests/ --station=starter_station --mock-instruments --uut-serial=STARTER001 -v
177
+
178
+ # With real hardware (drop --mock-instruments; use a real serial)
179
+ pytest tests/ --station=starter_station --uut-serial=SN001 -v
180
+ ```
181
+
182
+ > **On `--uut-serial` for early articles:** if your first UUT doesn't have
183
+ > a real serial yet (engineering build, breadboard, dev unit), call it
184
+ > whatever you like — `bob`, `proto-1`, `dev`. The serial is just the
185
+ > identifier the run record will be filed under. Best practice once you
186
+ > have real units is to use the value that uniquely identifies what is
187
+ > being tested and measured (printed serial, scanned barcode, lot+sequence).
188
+
189
+ ## The Pattern
190
+
191
+ Every TesterKit test follows this pattern:
192
+
193
+ 1. **GET CONDITIONS** from `context.get_param(...)` (not hardcoded)
194
+ 2. **SET UP** stimulus (PSU voltage, load current)
195
+ 3. **MEASURE** the result
196
+ 4. **CHECK** with `verify(name, value)` or `measure(name, value, ...)` — never `assert 3.0 <= v <= 3.6`
197
+
198
+ ```python
199
+ def test_something(context, psu, dmm, verify):
200
+ vin = context.get_param("vin", 5.0) # GET from context
201
+ psu.set_voltage(vin) # SET UP
202
+ psu.enable_output()
203
+ verify("output_voltage", # MEASURE + CHECK + RECORD
204
+ dmm.measure_dc_voltage())
205
+ ```
206
+
207
+ **No hardcoded values in code.** Conditions come from `context` (populated by native `@pytest.mark.parametrize` or sidecar YAML). Limits come from the part spec, an inline `@pytest.mark.testerkit_limits` decorator, or the sidecar's `limits:` field — never inline asserts.
208
+
209
+ For the full reference — markers, sidecar YAML, `context.changed()`, mocks, retries — see the [Writing Tests guide](../how-to/execution/writing-tests.md).
210
+
211
+ ## View Results
212
+
213
+ ### CLI
214
+
215
+ ```bash
216
+ testerkit runs # List recent runs
217
+ testerkit show <run_id> # Show run details
218
+ ```
219
+
220
+ ### Operator UI
221
+
222
+ ```bash
223
+ testerkit serve
224
+ # Open http://localhost:8000
225
+ ```
226
+
227
+ ### Programmatic
228
+
229
+ Each run writes one parquet at `data/runs/{date}/*.parquet`. Measurements are
230
+ nested under the vector rows (`record_type = 'vector'`), so read them with a
231
+ DuckDB `UNNEST`:
232
+
233
+ ```python
234
+ import duckdb
235
+
236
+ rows = duckdb.sql("""
237
+ SELECT run_id, m.name, m.value, m.unit, m.outcome
238
+ FROM read_parquet('data/runs/**/*.parquet', union_by_name=true),
239
+ UNNEST(measurements) AS t(m)
240
+ WHERE record_type = 'vector'
241
+ """).fetchall()
242
+ for row in rows:
243
+ print(row)
244
+ ```
245
+
246
+ For cross-run analytics (yield, Ppk, Pareto) use the higher-level
247
+ [`MeasurementsQuery`](../reference/data/query-api.md) API instead of reading
248
+ parquet directly.
249
+
250
+ ## Key Folders
251
+
252
+ | Folder | Purpose | UI Page |
253
+ |--------|---------|---------|
254
+ | `parts/` | Part specs (what you're testing) | /parts |
255
+ | `stations/` | Station configs (instruments + addresses) | /stations |
256
+ | `fixtures/` | Pin-to-instrument mappings | /fixtures |
257
+ | `instruments/` | Instrument asset records (identity, calibration) | /instruments |
258
+ | `tests/` | Test code + sidecar config | - |
259
+ | `data/` | Parquet + event log output (gitignored) | /runs |
260
+
261
+ ## Optional: Set Up AI Assistance
262
+
263
+ If you use an AI coding tool, TesterKit can register its [MCP (Model Context Protocol)](../how-to/overview/mcp-integration.md) server and generate project instructions so your AI understands TesterKit:
264
+
265
+ ```bash
266
+ testerkit setup claude-code # Claude Code
267
+ testerkit setup claude-desktop # Claude Desktop
268
+ testerkit setup copilot # GitHub Copilot (VS Code + CLI)
269
+ ```
270
+
271
+ ## Next: Connect Real Hardware
272
+
273
+ When you're ready to move from mocks to real instruments, see [Step 7: Real Instruments](07-real-instruments.md). It covers station configuration, `testerkit discover`, driver wiring, and common troubleshooting.
274
+
275
+ ## Next Steps
276
+
277
+ You've seen the whole loop. From here:
278
+
279
+ - **Learn it from the ground up** → the [step-by-step tutorial](index.md) builds a project from nothing, one concept at a time. It's a separate path from this complete-project quick start, not a continuation of it.
280
+ - [Core Concepts](../concepts/) — Understand parts, stations, and capabilities
281
+ - [Writing Tests](../how-to/execution/writing-tests.md) — Patterns and best practices
282
+ - [Configuration Reference](../reference/configuration.md) — YAML schema details
@@ -0,0 +1,9 @@
1
+ """Built-in analysis: yield, capability, Pareto, trend, and test time metrics.
2
+
3
+ Import directly from the submodule that owns the type:
4
+
5
+ from testerkit.analysis.measurements_query import MeasurementsQuery
6
+ from testerkit.analysis.runs_query import RunsQuery
7
+ from testerkit.analysis.steps_query import StepsQuery
8
+ from testerkit.analysis.metrics import calculate_ppk, pareto_analysis
9
+ """
@@ -0,0 +1,24 @@
1
+ """Shared utilities for the analysis module."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+
7
+ from testerkit.data.models import ensure_utc
8
+
9
+
10
+ def parse_datetime(value: str | datetime | None) -> datetime | None:
11
+ """Parse a datetime string or pass through a datetime object.
12
+
13
+ Handles ISO format strings and the common "Z" UTC suffix. Returns a
14
+ UTC-aware datetime (naive inputs are assumed UTC, the server's storage
15
+ convention), or None on failure (malformed strings, None input).
16
+ """
17
+ if value is None:
18
+ return None
19
+ if isinstance(value, datetime):
20
+ return ensure_utc(value)
21
+ try:
22
+ return ensure_utc(datetime.fromisoformat(value.replace("Z", "+00:00")))
23
+ except ValueError:
24
+ return None